From a31bd012c34dfd4aca7947638bac4237355fe6f6 Mon Sep 17 00:00:00 2001
From: Matt Provost
Date: Tue, 20 Jun 2023 14:25:27 -0700
Subject: [PATCH] Replace @elastic/datemath with @opensearch/datemath (#204)
* Replace @elastic/datemath with @opensearch/datemath
Signed-off-by: Matt Provost
* Clean up
Signed-off-by: Matt Provost
---------
Signed-off-by: Matt Provost
---
i18ntokens.json | 76 ++--
package.json | 3 +-
packages/opensearch-datemath/.npmignore | 2 +
packages/opensearch-datemath/index.d.ts | 62 +++
packages/opensearch-datemath/index.js | 171 ++++++++
packages/opensearch-datemath/index.test.ts | 393 ++++++++++++++++++
packages/opensearch-datemath/package.json | 11 +
packages/opensearch-datemath/readme.md | 3 +
packages/opensearch-datemath/tsconfig.json | 9 +
src-docs/src/components/codesandbox/link.js | 2 +-
.../src/views/guidelines/getting_started.md | 4 +-
.../super_date_picker_example.js | 2 +-
.../super_date_picker/date_modes.test.ts | 4 +-
.../super_date_picker/date_modes.ts | 2 +-
.../date_popover/absolute_tab.tsx | 2 +-
.../date_popover/relative_tab.tsx | 2 +-
.../super_date_picker/pretty_duration.ts | 2 +-
.../quick_select_popover/quick_select.tsx | 2 +-
.../quick_select_utils.ts | 2 +-
.../super_date_picker/relative_utils.ts | 2 +-
.../super_date_picker/super_date_picker.tsx | 2 +-
wiki/consuming.md | 2 +-
yarn.lock | 10 +-
23 files changed, 708 insertions(+), 62 deletions(-)
create mode 100644 packages/opensearch-datemath/.npmignore
create mode 100644 packages/opensearch-datemath/index.d.ts
create mode 100644 packages/opensearch-datemath/index.js
create mode 100644 packages/opensearch-datemath/index.test.ts
create mode 100644 packages/opensearch-datemath/package.json
create mode 100644 packages/opensearch-datemath/readme.md
create mode 100644 packages/opensearch-datemath/tsconfig.json
diff --git a/i18ntokens.json b/i18ntokens.json
index 867bed764e..96c4356734 100644
--- a/i18ntokens.json
+++ b/i18ntokens.json
@@ -187,12 +187,12 @@
"start": {
"line": 191,
"column": 8,
- "index": 5650
+ "index": 5638
},
"end": {
"line": 193,
"column": 40,
- "index": 5750
+ "index": 5738
}
},
"filepath": "src/components/bottom_bar/bottom_bar.tsx"
@@ -205,12 +205,12 @@
"start": {
"line": 217,
"column": 14,
- "index": 6647
+ "index": 6635
},
"end": {
"line": 221,
"column": 16,
- "index": 6920
+ "index": 6908
}
},
"filepath": "src/components/bottom_bar/bottom_bar.tsx"
@@ -223,12 +223,12 @@
"start": {
"line": 223,
"column": 14,
- "index": 6953
+ "index": 6941
},
"end": {
"line": 226,
"column": 16,
- "index": 7150
+ "index": 7138
}
},
"filepath": "src/components/bottom_bar/bottom_bar.tsx"
@@ -1861,12 +1861,12 @@
"start": {
"line": 146,
"column": 12,
- "index": 4299
+ "index": 4302
},
"end": {
"line": 151,
"column": 62,
- "index": 4513
+ "index": 4516
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1879,12 +1879,12 @@
"start": {
"line": 146,
"column": 12,
- "index": 4299
+ "index": 4302
},
"end": {
"line": 151,
"column": 62,
- "index": 4513
+ "index": 4516
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1897,12 +1897,12 @@
"start": {
"line": 170,
"column": 12,
- "index": 5261
+ "index": 5264
},
"end": {
"line": 172,
"column": 43,
- "index": 5365
+ "index": 5368
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1915,12 +1915,12 @@
"start": {
"line": 189,
"column": 8,
- "index": 5904
+ "index": 5907
},
"end": {
"line": 192,
"column": 75,
- "index": 6075
+ "index": 6078
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1933,12 +1933,12 @@
"start": {
"line": 210,
"column": 14,
- "index": 6566
+ "index": 6569
},
"end": {
"line": 214,
"column": 16,
- "index": 6757
+ "index": 6760
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1951,12 +1951,12 @@
"start": {
"line": 220,
"column": 12,
- "index": 6907
+ "index": 6910
},
"end": {
"line": 224,
"column": 14,
- "index": 7088
+ "index": 7091
}
},
"filepath": "src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx"
@@ -1987,12 +1987,12 @@
"start": {
"line": 194,
"column": 8,
- "index": 5693
+ "index": 5696
},
"end": {
"line": 196,
"column": 46,
- "index": 5792
+ "index": 5795
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2005,12 +2005,12 @@
"start": {
"line": 213,
"column": 12,
- "index": 6419
+ "index": 6422
},
"end": {
"line": 215,
"column": 37,
- "index": 6519
+ "index": 6522
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2023,12 +2023,12 @@
"start": {
"line": 226,
"column": 16,
- "index": 6926
+ "index": 6929
},
"end": {
"line": 228,
"column": 49,
- "index": 7039
+ "index": 7042
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2041,12 +2041,12 @@
"start": {
"line": 241,
"column": 16,
- "index": 7523
+ "index": 7526
},
"end": {
"line": 243,
"column": 45,
- "index": 7628
+ "index": 7631
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2059,12 +2059,12 @@
"start": {
"line": 261,
"column": 12,
- "index": 8244
+ "index": 8247
},
"end": {
"line": 261,
"column": 76,
- "index": 8308
+ "index": 8311
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2077,12 +2077,12 @@
"start": {
"line": 276,
"column": 12,
- "index": 8820
+ "index": 8823
},
"end": {
"line": 276,
"column": 76,
- "index": 8884
+ "index": 8887
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2095,12 +2095,12 @@
"start": {
"line": 290,
"column": 12,
- "index": 9356
+ "index": 9359
},
"end": {
"line": 290,
"column": 74,
- "index": 9418
+ "index": 9421
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2113,12 +2113,12 @@
"start": {
"line": 311,
"column": 14,
- "index": 10196
+ "index": 10199
},
"end": {
"line": 311,
"column": 76,
- "index": 10258
+ "index": 10261
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2131,12 +2131,12 @@
"start": {
"line": 318,
"column": 12,
- "index": 10451
+ "index": 10454
},
"end": {
"line": 326,
"column": 14,
- "index": 10725
+ "index": 10728
}
},
"filepath": "src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx"
@@ -2239,12 +2239,12 @@
"start": {
"line": 429,
"column": 14,
- "index": 11289
+ "index": 11292
},
"end": {
"line": 432,
"column": 16,
- "index": 11415
+ "index": 11418
}
},
"filepath": "src/components/date_picker/super_date_picker/super_date_picker.tsx"
diff --git a/package.json b/package.json
index 6f4d1f6f4c..0d634c2310 100644
--- a/package.json
+++ b/package.json
@@ -143,8 +143,8 @@
"@babel/preset-react": "^7.10.4",
"@babel/preset-typescript": "^7.12.1",
"@elastic/charts": "^30.2.0",
- "@elastic/datemath": "^5.0.3",
"@elastic/eslint-config-kibana": "^0.15.0",
+ "@opensearch/datemath": "file:./packages/opensearch-datemath",
"@svgr/core": "^8.0.0",
"@svgr/plugin-svgo": "^8.0.1",
"@svgr/plugin-jsx": "^8.0.1",
@@ -251,7 +251,6 @@
"yo": "^4.3.1"
},
"peerDependencies": {
- "@elastic/datemath": "^5.0.2",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"moment": "^2.13.0",
diff --git a/packages/opensearch-datemath/.npmignore b/packages/opensearch-datemath/.npmignore
new file mode 100644
index 0000000000..591be7afd1
--- /dev/null
+++ b/packages/opensearch-datemath/.npmignore
@@ -0,0 +1,2 @@
+/tsconfig.json
+/__tests__
diff --git a/packages/opensearch-datemath/index.d.ts b/packages/opensearch-datemath/index.d.ts
new file mode 100644
index 0000000000..d5a38f0176
--- /dev/null
+++ b/packages/opensearch-datemath/index.d.ts
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Any modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import moment from 'moment';
+export type Unit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y';
+
+declare const datemath: {
+ unitsMap: {
+ [k in Unit]: {
+ weight: number;
+ type: 'calendar' | 'fixed' | 'mixed';
+ base: number;
+ };
+ };
+ units: Unit[];
+ unitsAsc: Unit[];
+ unitsDesc: Unit[];
+
+ /**
+ * Parses a string into a moment object. The string can be something like "now - 15m".
+ * @param options.forceNow If this optional parameter is supplied, "now" will be treated as this
+ * date, rather than the real "now".
+ */
+ parse(
+ input: string,
+ options?: {
+ roundUp?: boolean;
+ forceNow?: Date;
+ momentInstance?: typeof moment;
+ }
+ ): moment.Moment | undefined;
+};
+
+// eslint-disable-next-line import/no-default-export
+export default datemath;
diff --git a/packages/opensearch-datemath/index.js b/packages/opensearch-datemath/index.js
new file mode 100644
index 0000000000..4367949d7c
--- /dev/null
+++ b/packages/opensearch-datemath/index.js
@@ -0,0 +1,171 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Any modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const moment = require('moment');
+
+const unitsMap = {
+ ms: { weight: 1, type: 'fixed', base: 1 },
+ s: { weight: 2, type: 'fixed', base: 1000 },
+ m: { weight: 3, type: 'mixed', base: 1000 * 60 },
+ h: { weight: 4, type: 'mixed', base: 1000 * 60 * 60 },
+ d: { weight: 5, type: 'mixed', base: 1000 * 60 * 60 * 24 },
+ w: { weight: 6, type: 'calendar', base: NaN },
+ M: { weight: 7, type: 'calendar', base: NaN },
+ // q: { weight: 8, type: 'calendar' }, // TODO: moment duration does not support quarter
+ y: { weight: 9, type: 'calendar', base: NaN },
+};
+const units = Object.keys(unitsMap).sort((a, b) => unitsMap[b].weight - unitsMap[a].weight);
+const unitsDesc = [...units];
+const unitsAsc = [...units].reverse();
+
+const isDate = (d) => Object.prototype.toString.call(d) === '[object Date]';
+
+const isValidDate = (d) => isDate(d) && !isNaN(d.valueOf());
+
+/*
+ * This is a simplified version of opensearch's date parser.
+ * If you pass in a momentjs instance as the third parameter the calculation
+ * will be done using this (and its locale settings) instead of the one bundled
+ * with this library.
+ */
+function parse(text, { roundUp = false, momentInstance = moment, forceNow } = {}) {
+ if (!text) return undefined;
+ if (momentInstance.isMoment(text)) return text;
+ if (isDate(text)) return momentInstance(text);
+ if (forceNow !== undefined && !isValidDate(forceNow)) {
+ throw new Error('forceNow must be a valid Date');
+ }
+
+ let time;
+ let mathString = '';
+ let index;
+ let parseString;
+
+ if (text.substring(0, 3) === 'now') {
+ time = momentInstance(forceNow);
+ mathString = text.substring('now'.length);
+ } else {
+ index = text.indexOf('||');
+ if (index === -1) {
+ parseString = text;
+ mathString = ''; // nothing else
+ } else {
+ parseString = text.substring(0, index);
+ mathString = text.substring(index + 2);
+ }
+ // We're going to just require ISO8601 timestamps, k?
+ time = momentInstance(parseString);
+ }
+
+ if (!mathString.length) {
+ return time;
+ }
+
+ return parseDateMath(mathString, time, roundUp);
+}
+
+function parseDateMath(mathString, time, roundUp) {
+ const dateTime = time;
+ const len = mathString.length;
+ let i = 0;
+
+ while (i < len) {
+ const c = mathString.charAt(i++);
+ let type;
+ let num;
+ let unit;
+
+ if (c === '/') {
+ type = 0;
+ } else if (c === '+') {
+ type = 1;
+ } else if (c === '-') {
+ type = 2;
+ } else {
+ return;
+ }
+
+ if (isNaN(mathString.charAt(i))) {
+ num = 1;
+ } else if (mathString.length === 2) {
+ num = mathString.charAt(i);
+ } else {
+ const numFrom = i;
+ while (!isNaN(mathString.charAt(i))) {
+ i++;
+ if (i >= len) return;
+ }
+ num = parseInt(mathString.substring(numFrom, i), 10);
+ }
+
+ if (type === 0) {
+ // rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M)
+ if (num !== 1) {
+ return;
+ }
+ }
+
+ unit = mathString.charAt(i++);
+
+ // append additional characters in the unit
+ for (let j = i; j < len; j++) {
+ const unitChar = mathString.charAt(i);
+ if (/[a-z]/i.test(unitChar)) {
+ unit += unitChar;
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ if (units.indexOf(unit) === -1) {
+ return;
+ } else {
+ if (type === 0) {
+ if (roundUp) dateTime.endOf(unit);
+ else dateTime.startOf(unit);
+ } else if (type === 1) {
+ dateTime.add(num, unit);
+ } else if (type === 2) {
+ dateTime.subtract(num, unit);
+ }
+ }
+ }
+
+ return dateTime;
+}
+
+module.exports = {
+ parse: parse,
+ unitsMap: Object.freeze(unitsMap),
+ units: Object.freeze(units),
+ unitsAsc: Object.freeze(unitsAsc),
+ unitsDesc: Object.freeze(unitsDesc),
+};
diff --git a/packages/opensearch-datemath/index.test.ts b/packages/opensearch-datemath/index.test.ts
new file mode 100644
index 0000000000..e293da72ac
--- /dev/null
+++ b/packages/opensearch-datemath/index.test.ts
@@ -0,0 +1,393 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Any modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import dateMath from './index';
+import moment from 'moment';
+import sinon from 'sinon';
+import expect from '@osd/expect';
+
+/**
+ * Require a new instance of the moment library, bypassing the require cache.
+ * This is needed, since we are trying to test whether or not this library works
+ * when passing in a different configured moment instance. If we would change
+ * the locales on the imported moment, it would automatically apply
+ * to the source code, even without passing it in to the method, since they share
+ * the same global state. This method avoids this, by loading a separate instance
+ * of moment, by deleting the require cache and require the library again.
+ */
+function momentClone() {
+ jest.resetModules();
+ delete require.cache[require.resolve('moment')];
+ return require('moment');
+}
+
+describe('dateMath', function () {
+ // Test each of these intervals when testing relative time
+ const spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y', 'ms'];
+ const anchor = '2014-01-01T06:06:06.666Z';
+ const anchoredDate = new Date(Date.parse(anchor));
+ const unix = moment(anchor).valueOf();
+ const format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
+ let clock;
+
+ describe('errors', function () {
+ it('should return undefined if passed something falsy', function () {
+ expect(dateMath.parse('')).to.be(undefined);
+ });
+
+ it('should return undefined if I pass an operator besides [+-/]', function () {
+ expect(dateMath.parse('now&1d')).to.be(undefined);
+ });
+
+ it('should return undefined if I pass a unit besides' + spans.toString(), function () {
+ expect(dateMath.parse('now+5f')).to.be(undefined);
+ });
+
+ it('should return undefined if rounding unit is not 1', function () {
+ expect(dateMath.parse('now/2y')).to.be(undefined);
+ expect(dateMath.parse('now/0.5y')).to.be(undefined);
+ });
+
+ it('should not go into an infinite loop when missing a unit', function () {
+ expect(dateMath.parse('now-0')).to.be(undefined);
+ expect(dateMath.parse('now-00')).to.be(undefined);
+ expect(dateMath.parse('now-000')).to.be(undefined);
+ });
+
+ describe('forceNow', function () {
+ it('should throw an Error if passed a string', function () {
+ // @ts-ignore bad arg
+ const fn = () => dateMath.parse('now', { forceNow: '2000-01-01T00:00:00.000Z' });
+ expect(fn).to.throwError();
+ });
+
+ it('should throw an Error if passed a moment', function () {
+ // @ts-ignore bad arg
+ expect(() => dateMath.parse('now', { forceNow: moment() })).to.throwError();
+ });
+
+ it('should throw an Error if passed an invalid date', function () {
+ expect(() => dateMath.parse('now', { forceNow: new Date('foobar') })).to.throwError();
+ });
+ });
+ });
+
+ describe('objects and strings', function () {
+ let mmnt;
+ let date;
+ let string;
+ let now;
+
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(unix);
+ now = moment();
+ mmnt = moment(anchor);
+ date = mmnt.toDate();
+ string = mmnt.format(format);
+ });
+
+ afterEach(function () {
+ clock.restore();
+ });
+
+ it('should return the same moment if passed a moment', function () {
+ expect(dateMath.parse(mmnt)).to.eql(mmnt);
+ });
+
+ it('should return a moment if passed a date', function () {
+ expect(dateMath.parse(date).format(format)).to.eql(mmnt.format(format));
+ });
+
+ it('should return a moment if passed an ISO8601 string', function () {
+ expect(dateMath.parse(string).format(format)).to.eql(mmnt.format(format));
+ });
+
+ it('should return the current time when parsing now', function () {
+ expect(dateMath.parse('now').format(format)).to.eql(now.format(format));
+ });
+
+ it('should use the forceNow parameter when parsing now', function () {
+ expect(dateMath.parse('now', { forceNow: anchoredDate }).valueOf()).to.eql(unix);
+ });
+ });
+
+ describe('subtraction', function () {
+ let now;
+ let anchored;
+
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(unix);
+ now = moment();
+ anchored = moment(anchor);
+ });
+
+ afterEach(function () {
+ clock.restore();
+ });
+
+ [5, 12, 247].forEach((len) => {
+ spans.forEach((span) => {
+ const nowEx = `now-${len}${span}`;
+ const thenEx = `${anchor}||-${len}${span}`;
+
+ it('should return ' + len + span + ' ago', function () {
+ const parsed = dateMath.parse(nowEx).format(format);
+ expect(parsed).to.eql(now.subtract(len, span).format(format));
+ });
+
+ it('should return ' + len + span + ' before ' + anchor, function () {
+ const parsed = dateMath.parse(thenEx).format(format);
+ expect(parsed).to.eql(anchored.subtract(len, span).format(format));
+ });
+
+ it('should return ' + len + span + ' before forceNow', function () {
+ const parsed = dateMath.parse(nowEx, { forceNow: anchoredDate }).valueOf();
+ expect(parsed).to.eql(anchored.subtract(len, span).valueOf());
+ });
+ });
+ });
+ });
+
+ describe('addition', function () {
+ let now;
+ let anchored;
+
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(unix);
+ now = moment();
+ anchored = moment(anchor);
+ });
+
+ afterEach(function () {
+ clock.restore();
+ });
+
+ [5, 12, 247].forEach((len) => {
+ spans.forEach((span) => {
+ const nowEx = `now+${len}${span}`;
+ const thenEx = `${anchor}||+${len}${span}`;
+
+ it('should return ' + len + span + ' from now', function () {
+ expect(dateMath.parse(nowEx).format(format)).to.eql(now.add(len, span).format(format));
+ });
+
+ it('should return ' + len + span + ' after ' + anchor, function () {
+ expect(dateMath.parse(thenEx).format(format)).to.eql(
+ anchored.add(len, span).format(format)
+ );
+ });
+
+ it('should return ' + len + span + ' after forceNow', function () {
+ expect(dateMath.parse(nowEx, { forceNow: anchoredDate }).valueOf()).to.eql(
+ anchored.add(len, span).valueOf()
+ );
+ });
+ });
+ });
+ });
+
+ describe('rounding', function () {
+ let now;
+ let anchored;
+
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(unix);
+ now = moment();
+ anchored = moment(anchor);
+ });
+
+ afterEach(function () {
+ clock.restore();
+ });
+
+ spans.forEach((span) => {
+ it(`should round now to the beginning of the ${span}`, function () {
+ expect(dateMath.parse('now/' + span).format(format)).to.eql(
+ now.startOf(span).format(format)
+ );
+ });
+
+ it(`should round now to the beginning of forceNow's ${span}`, function () {
+ expect(dateMath.parse('now/' + span, { forceNow: anchoredDate }).valueOf()).to.eql(
+ anchored.startOf(span).valueOf()
+ );
+ });
+
+ it(`should round now to the end of the ${span}`, function () {
+ expect(dateMath.parse('now/' + span, { roundUp: true }).format(format)).to.eql(
+ now.endOf(span).format(format)
+ );
+ });
+
+ it(`should round now to the end of forceNow's ${span}`, function () {
+ expect(
+ dateMath.parse('now/' + span, { roundUp: true, forceNow: anchoredDate }).valueOf()
+ ).to.eql(anchored.endOf(span).valueOf());
+ });
+ });
+ });
+
+ describe('math and rounding', function () {
+ let now;
+ let anchored;
+
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(unix);
+ now = moment();
+ anchored = moment(anchor);
+ });
+
+ afterEach(function () {
+ clock.restore();
+ });
+
+ it('should round to the nearest second with 0 value', function () {
+ const val = dateMath.parse('now-0s/s').format(format);
+ expect(val).to.eql(now.startOf('s').format(format));
+ });
+
+ it('should subtract 17s, rounded to the nearest second', function () {
+ const val = dateMath.parse('now-17s/s').format(format);
+ expect(val).to.eql(now.startOf('s').subtract(17, 's').format(format));
+ });
+
+ it('should add 555ms, rounded to the nearest millisecond', function () {
+ const val = dateMath.parse('now+555ms/ms').format(format);
+ expect(val).to.eql(now.add(555, 'ms').startOf('ms').format(format));
+ });
+
+ it('should subtract 555ms, rounded to the nearest second', function () {
+ const val = dateMath.parse('now-555ms/s').format(format);
+ expect(val).to.eql(now.subtract(555, 'ms').startOf('s').format(format));
+ });
+
+ it('should round weeks to Sunday by default', function () {
+ const val = dateMath.parse('now-1w/w');
+ expect(val.isoWeekday()).to.eql(7);
+ });
+
+ it('should round weeks based on the passed moment locale start of week setting', function () {
+ const m = momentClone();
+ // Define a locale, that has Tuesday as beginning of the week
+ m.defineLocale('x-test', {
+ week: { dow: 2 },
+ });
+ const val = dateMath.parse('now-1w/w', { momentInstance: m });
+ expect(val.isoWeekday()).to.eql(2);
+ });
+
+ it('should round up weeks based on the passed moment locale start of week setting', function () {
+ const m = momentClone();
+ // Define a locale, that has Tuesday as beginning of the week
+ m.defineLocale('x-test', {
+ week: { dow: 3 },
+ });
+ const val = dateMath.parse('now-1w/w', {
+ roundUp: true,
+ momentInstance: m,
+ });
+ // The end of the range (rounding up) should be the last day of the week (so one day before)
+ // our start of the week, that's why 3 - 1
+ expect(val.isoWeekday()).to.eql(3 - 1);
+ });
+
+ it('should round relative to forceNow', function () {
+ const val = dateMath.parse('now-0s/s', { forceNow: anchoredDate }).valueOf();
+ expect(val).to.eql(anchored.startOf('s').valueOf());
+ });
+
+ it('should parse long expressions', () => {
+ expect(dateMath.parse('now-1d/d+8h+50m')).to.be.ok();
+ });
+ });
+
+ describe('used momentjs instance', function () {
+ it('should use the default moment instance if parameter not specified', function () {
+ const momentSpy = sinon.spy(moment, 'isMoment');
+ dateMath.parse('now');
+ expect(momentSpy.called).to.be(true);
+ momentSpy.restore();
+ });
+
+ it('should not use default moment instance if parameter is specified', function () {
+ const m = momentClone();
+ const momentSpy = sinon.spy(moment, 'isMoment');
+ const cloneSpy = sinon.spy(m, 'isMoment');
+ dateMath.parse('now', { momentInstance: m });
+ expect(momentSpy.called).to.be(false);
+ expect(cloneSpy.called).to.be(true);
+ momentSpy.restore();
+ cloneSpy.restore();
+ });
+
+ it('should work with multiple different instances', function () {
+ const m1 = momentClone();
+ const m2 = momentClone();
+ const m1Spy = sinon.spy(m1, 'isMoment');
+ const m2Spy = sinon.spy(m2, 'isMoment');
+ dateMath.parse('now', { momentInstance: m1 });
+ expect(m1Spy.called).to.be(true);
+ expect(m2Spy.called).to.be(false);
+ m1Spy.resetHistory();
+ m2Spy.resetHistory();
+ dateMath.parse('now', { momentInstance: m2 });
+ expect(m1Spy.called).to.be(false);
+ expect(m2Spy.called).to.be(true);
+ m1Spy.restore();
+ m2Spy.restore();
+ });
+
+ it('should use global instance after passing an instance', function () {
+ const m = momentClone();
+ const momentSpy = sinon.spy(moment, 'isMoment');
+ const cloneSpy = sinon.spy(m, 'isMoment');
+ dateMath.parse('now', { momentInstance: m });
+ expect(momentSpy.called).to.be(false);
+ expect(cloneSpy.called).to.be(true);
+ momentSpy.resetHistory();
+ cloneSpy.resetHistory();
+ dateMath.parse('now');
+ expect(momentSpy.called).to.be(true);
+ expect(cloneSpy.called).to.be(false);
+ momentSpy.restore();
+ cloneSpy.restore();
+ });
+ });
+
+ describe('units', function () {
+ it('should have units descending for unitsDesc', function () {
+ expect(dateMath.unitsDesc).to.eql(['y', 'M', 'w', 'd', 'h', 'm', 's', 'ms']);
+ });
+
+ it('should have units ascending for unitsAsc', function () {
+ expect(dateMath.unitsAsc).to.eql(['ms', 's', 'm', 'h', 'd', 'w', 'M', 'y']);
+ });
+ });
+});
diff --git a/packages/opensearch-datemath/package.json b/packages/opensearch-datemath/package.json
new file mode 100644
index 0000000000..39dfcec403
--- /dev/null
+++ b/packages/opensearch-datemath/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@opensearch/datemath",
+ "version": "5.0.3",
+ "description": "opensearch datemath parser, used in OpenSearch Dashboards",
+ "license": "Apache-2.0",
+ "main": "index.js",
+ "typings": "index.d.ts",
+ "peerDependencies": {
+ "moment": "^2.24.0"
+ }
+}
diff --git a/packages/opensearch-datemath/readme.md b/packages/opensearch-datemath/readme.md
new file mode 100644
index 0000000000..c5d739d13e
--- /dev/null
+++ b/packages/opensearch-datemath/readme.md
@@ -0,0 +1,3 @@
+# datemath
+
+Datemath string parser used in OpenSearch Dashboards
diff --git a/packages/opensearch-datemath/tsconfig.json b/packages/opensearch-datemath/tsconfig.json
new file mode 100644
index 0000000000..7a65f45500
--- /dev/null
+++ b/packages/opensearch-datemath/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "tsBuildInfoFile": "../../build/tsbuildinfo/packages/opensearch-datemath"
+ },
+ "include": [
+ "index.d.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src-docs/src/components/codesandbox/link.js b/src-docs/src/components/codesandbox/link.js
index 6c2ef46f21..addecf041b 100644
--- a/src-docs/src/components/codesandbox/link.js
+++ b/src-docs/src/components/codesandbox/link.js
@@ -152,7 +152,7 @@ ${exampleClose}
dependencies: {
'@opensearch-project/oui': pkg.version,
...[
- '@elastic/datemath',
+ '@opensearch/datemath',
'moment',
'react',
'react-dom',
diff --git a/src-docs/src/views/guidelines/getting_started.md b/src-docs/src/views/guidelines/getting_started.md
index cb14d3838d..c7fe7e388c 100644
--- a/src-docs/src/views/guidelines/getting_started.md
+++ b/src-docs/src/views/guidelines/getting_started.md
@@ -9,7 +9,7 @@ yarn add @opensearch-project/oui
Note that OUI has [several `peerDependencies` requirements](package.json) that will also need to be installed if starting with a blank project. You can read more about other ways to [consume OUI][consuming].
```js
-yarn add @opensearch-project/oui @elastic/datemath moment prop-types
+yarn add @opensearch-project/oui moment prop-types
```
@@ -45,7 +45,7 @@ yarn start --port 9000
OUI expects that you polyfill ES2015 features, e.g. [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/). Without an ES2015 polyfill your app might throw errors on certain browsers.
-OUI also has `moment` and `@elastic/datemath` as dependencies itself. These are already loaded in most OpenSearch repos, but make sure to install them if you are starting from scratch.
+OUI also has `moment` as a dependency itself. This is already loaded in most OpenSearch repos, but make sure to install it if you are starting from scratch.
## What's available
diff --git a/src-docs/src/views/super_date_picker/super_date_picker_example.js b/src-docs/src/views/super_date_picker/super_date_picker_example.js
index 8a5c5c4711..4f13e9b9f0 100644
--- a/src-docs/src/views/super_date_picker/super_date_picker_example.js
+++ b/src-docs/src/views/super_date_picker/super_date_picker_example.js
@@ -90,7 +90,7 @@ export const SuperDatePickerExample = {
to convert start and end strings into moment objects.
- {`import dateMath from '@elastic/datemath';
+ {`import dateMath from '@opensearch/datemath';
const startMoment = dateMath.parse(start);
// dateMath.parse is inconsistent with unparsable strings.
diff --git a/src/components/date_picker/super_date_picker/date_modes.test.ts b/src/components/date_picker/super_date_picker/date_modes.test.ts
index d3a2073925..343b4f79a3 100644
--- a/src/components/date_picker/super_date_picker/date_modes.test.ts
+++ b/src/components/date_picker/super_date_picker/date_modes.test.ts
@@ -30,9 +30,9 @@
import { getDateMode, toAbsoluteString, toRelativeString } from './date_modes';
-jest.mock('@elastic/datemath', () => {
+jest.mock('@opensearch/datemath', () => {
const moment = jest.requireActual('moment');
- const datemath = jest.requireActual('@elastic/datemath');
+ const datemath = jest.requireActual('@opensearch/datemath');
const anchor = '2019-03-19T00:00:00.000Z';
const anchoredDate = new Date(Date.parse(anchor));
// https://momentjs.com/docs/#/customization/now/
diff --git a/src/components/date_picker/super_date_picker/date_modes.ts b/src/components/date_picker/super_date_picker/date_modes.ts
index 5906b1fbb5..3e03c6b3c4 100644
--- a/src/components/date_picker/super_date_picker/date_modes.ts
+++ b/src/components/date_picker/super_date_picker/date_modes.ts
@@ -28,7 +28,7 @@
* under the License.
*/
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import {
parseRelativeParts,
toRelativeStringFromParts,
diff --git a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
index e7c428a408..345f2c8b44 100644
--- a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
+++ b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
@@ -32,7 +32,7 @@ import React, { Component, ChangeEventHandler } from 'react';
import moment, { Moment, LocaleSpecifier } from 'moment'; // eslint-disable-line import/named
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import { OuiDatePicker, OuiDatePickerProps } from '../../date_picker';
import { OuiFormRow, OuiFieldText, OuiFormLabel } from '../../../form';
diff --git a/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx b/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx
index 5f242030e2..80e3de1c2c 100644
--- a/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx
+++ b/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx
@@ -29,7 +29,7 @@
*/
import React, { Component, ChangeEventHandler } from 'react';
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import { toSentenceCase } from '../../../../services/string/to_case';
import { htmlIdGenerator } from '../../../../services';
import { OuiFlexGroup, OuiFlexItem } from '../../../flex';
diff --git a/src/components/date_picker/super_date_picker/pretty_duration.ts b/src/components/date_picker/super_date_picker/pretty_duration.ts
index eb5f8c07a7..bf56bc6d55 100644
--- a/src/components/date_picker/super_date_picker/pretty_duration.ts
+++ b/src/components/date_picker/super_date_picker/pretty_duration.ts
@@ -28,7 +28,7 @@
* under the License.
*/
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import moment, { LocaleSpecifier } from 'moment'; // eslint-disable-line import/named
import { timeUnits, timeUnitsPlural } from './time_units';
import { getDateMode, DATE_MODES } from './date_modes';
diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx b/src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx
index 9d7b83d8e2..17e7b13d1b 100644
--- a/src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx
+++ b/src/components/date_picker/super_date_picker/quick_select_popover/quick_select.tsx
@@ -34,7 +34,7 @@ import React, {
KeyboardEventHandler,
} from 'react';
import moment from 'moment';
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import { htmlIdGenerator } from '../../../../services';
import { OuiButton, OuiButtonIcon } from '../../../button';
import { OuiFlexGroup, OuiFlexItem } from '../../../flex';
diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/quick_select_utils.ts b/src/components/date_picker/super_date_picker/quick_select_popover/quick_select_utils.ts
index c69124da14..9f5f35fc89 100644
--- a/src/components/date_picker/super_date_picker/quick_select_popover/quick_select_utils.ts
+++ b/src/components/date_picker/super_date_picker/quick_select_popover/quick_select_utils.ts
@@ -29,7 +29,7 @@
*/
import moment from 'moment';
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import { isString } from '../../../../services/predicate';
import { relativeUnitsFromLargestToSmallest } from '../relative_options';
import { DATE_MODES } from '../date_modes';
diff --git a/src/components/date_picker/super_date_picker/relative_utils.ts b/src/components/date_picker/super_date_picker/relative_utils.ts
index 59b4c807e7..df471c376c 100644
--- a/src/components/date_picker/super_date_picker/relative_utils.ts
+++ b/src/components/date_picker/super_date_picker/relative_utils.ts
@@ -28,7 +28,7 @@
* under the License.
*/
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import moment from 'moment';
import { get } from '../../../services/objects';
diff --git a/src/components/date_picker/super_date_picker/super_date_picker.tsx b/src/components/date_picker/super_date_picker/super_date_picker.tsx
index 1d301d1487..2e475e905a 100644
--- a/src/components/date_picker/super_date_picker/super_date_picker.tsx
+++ b/src/components/date_picker/super_date_picker/super_date_picker.tsx
@@ -37,7 +37,7 @@ import {
} from './pretty_duration';
import { prettyInterval } from './pretty_interval';
-import dateMath from '@elastic/datemath';
+import dateMath from '@opensearch/datemath';
import {
OuiSuperUpdateButton,
diff --git a/wiki/consuming.md b/wiki/consuming.md
index 94b3138f80..c683a4d68b 100644
--- a/wiki/consuming.md
+++ b/wiki/consuming.md
@@ -4,7 +4,7 @@
OUI expects that you polyfill ES2015 features, e.g. [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/). Without an ES2015 polyfill your app might throw errors on certain browsers.
-OUI also has `moment` and `@elastic/datemath` as dependencies itself. These are already loaded in most OpenSearch repos, but make sure to install them if you are starting from scratch.
+OUI also has `moment` as a dependency itself. This is already loaded in most OpenSearch repos, but make sure to install it if you are starting from scratch.
## What's available
diff --git a/yarn.lock b/yarn.lock
index d647fad2bd..d297124a59 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1304,13 +1304,6 @@
utility-types "^3.10.0"
uuid "^3.3.2"
-"@elastic/datemath@^5.0.3":
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/@elastic/datemath/-/datemath-5.0.3.tgz#7baccdab672b9a3ecb7fe8387580670936b58573"
- integrity sha512-8Hbr1Uyjm5OcYBfEB60K7sCP6U3IXuWDaLaQmYv3UxgI4jqBWbakoemwWvsqPVUvnwEjuX6z7ghPZbefs8xiaA==
- dependencies:
- tslib "^1.9.3"
-
"@elastic/eslint-config-kibana@^0.15.0":
version "0.15.0"
resolved "https://registry.yarnpkg.com/@elastic/eslint-config-kibana/-/eslint-config-kibana-0.15.0.tgz#a552793497cdfc1829c2f9b7cd7018eb008f1606"
@@ -1929,6 +1922,9 @@
dependencies:
"@octokit/openapi-types" "^12.11.0"
+"@opensearch/datemath@file:./packages/opensearch-datemath":
+ version "5.0.3"
+
"@pkgjs/parseargs@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"