@miso.ai/server-commons 0.6.5-beta.13 → 0.6.5-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -6,7 +6,9 @@
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
- "scripts": {},
9
+ "scripts": {
10
+ "test": "npx uvu test -i 'ignored'"
11
+ },
10
12
  "repository": "MisoAI/miso-server-js-sdk",
11
13
  "license": "MIT",
12
14
  "contributors": [
@@ -21,5 +23,5 @@
21
23
  "uuid": "^9.0.0",
22
24
  "yargs": "^17.5.1"
23
25
  },
24
- "version": "0.6.5-beta.13"
26
+ "version": "0.6.5-beta.15"
25
27
  }
package/src/date.js CHANGED
@@ -7,8 +7,6 @@ TS_PER_UNIT.m = TS_PER_UNIT.s * 60;
7
7
  TS_PER_UNIT.h = TS_PER_UNIT.m * 60;
8
8
  TS_PER_UNIT.d = TS_PER_UNIT.h * 24;
9
9
  TS_PER_UNIT.w = TS_PER_UNIT.d * 7;
10
- //TS_PER_UNIT.M = TS_PER_UNIT.d * 31;
11
- //TS_PER_UNIT.y = TS_PER_UNIT.d * 366;
12
10
 
13
11
  export function parseDuration(expr, unit) {
14
12
  if (expr === undefined || typeof expr === 'number') {
@@ -33,69 +31,114 @@ export function getYear(dateStr) {
33
31
  return new Date(dateStr).getFullYear();
34
32
  }
35
33
 
34
+ const RE_DATE_EXPR = /^(?:\d{4})|(?:\d{4}-[Qq\d]\d)|(?:\d{4}-\d{2}-[Ww\d]\d)$/;
35
+
36
+ // TODO: support hour, minute
37
+
36
38
  export function startOfDate(expr) {
37
39
  if (expr === undefined) {
38
40
  return undefined;
39
41
  }
40
- if (typeof expr === 'number') {
41
- if (expr < 10000) {
42
- expr = startOfYearByYearNum(expr);
43
- }
44
- return expr;
45
- }
46
- const ts = Date.parse(expr);
47
- if (isNaN(ts)) {
48
- throw new Error(`Unrecognized date: ${expr}`);
42
+ const parsed = parseDateExpr(expr);
43
+ switch (parsed.type) {
44
+ case 'ts':
45
+ return parsed.ts;
46
+ case 'year':
47
+ return Date.UTC(parsed.year);
48
+ case 'quarter':
49
+ return Date.UTC(parsed.year, (parsed.quarter - 1) * 3);
50
+ case 'month':
51
+ return Date.UTC(parsed.year, parsed.month - 1);
52
+ case 'week':
53
+ return Date.UTC(parsed.year, parsed.month - 1, (parsed.week - 1) * 7 + 1);
54
+ case 'day':
55
+ return Date.UTC(parsed.year, parsed.month - 1, parsed.day);
56
+ default:
57
+ throw new Error(`Unrecognized date: ${expr}`);
49
58
  }
50
- return ts;
51
59
  }
52
60
 
53
- const RE_YEAR = /^\d{4}$/g;
54
- const RE_MONTH = /^\d{4}-\d{2}$/g;
55
- const RE_DATE = /^\d{4}-\d{2}-\d{2}$/g;
56
-
57
- // TODO: support end of hour, minute
58
-
59
- export function endOfDate(expr) {
61
+ export function nextOfDate(expr) {
60
62
  if (expr === undefined) {
61
63
  return undefined;
62
64
  }
63
- if (typeof expr === 'number') {
64
- if (expr < 10000) {
65
- expr = endOfYearByYearNum(expr);
66
- }
67
- return expr;
68
- }
69
- let ts = Date.parse(expr);
70
- if (isNaN(ts)) {
71
- throw new Error(`Unrecognized date: ${expr}`);
65
+ const parsed = parseDateExpr(expr);
66
+ switch (parsed.type) {
67
+ case 'ts':
68
+ return parsed.ts + 1;
69
+ case 'year':
70
+ return Date.UTC(parsed.year + 1);
71
+ case 'quarter':
72
+ return Date.UTC(parsed.year, parsed.quarter * 3);
73
+ case 'month':
74
+ return Date.UTC(parsed.year, parsed.month);
75
+ case 'week':
76
+ // roll over to 1st of next month if week is 4
77
+ return Date.UTC(parsed.year, parsed.week < 4 ? parsed.month - 1 : parsed.month, (parsed.week % 4) * 7 + 1);
78
+ case 'day':
79
+ return Date.UTC(parsed.year, parsed.month - 1, parsed.day + 1);
80
+ default:
81
+ throw new Error(`Unrecognized date: ${expr}`);
72
82
  }
73
- return expr.match(RE_YEAR) ? endOfYear(ts) :
74
- expr.match(RE_MONTH) ? endOfMonth(ts) :
75
- expr.match(RE_DATE) ? endOfDay(ts) : ts;
76
83
  }
77
84
 
78
- function endOfYear(ts) {
79
- return endOfYearByYearNum(new Date(ts).getFullYear());
80
- }
81
-
82
- function startOfYearByYearNum(fullYear) {
83
- return new Date(fullYear, 0, 1).getTime();
84
- }
85
-
86
- function endOfYearByYearNum(fullYear) {
87
- return new Date(fullYear + 1, 0, 1).getTime() - 1000;
88
- }
89
-
90
- function endOfMonth(ts) {
91
- const date = new Date(ts);
92
- const year = date.getFullYear();
93
- const month = date.getMonth();
94
- return (month == 11 ? new Date(year + 1, 0, 1) : new Date(year, month + 1, 1)).getTime() - 1000;
85
+ export function endOfDate(expr) {
86
+ if (expr === undefined) {
87
+ return undefined;
88
+ }
89
+ return nextOfDate(expr) - 1000; // 1 sec
95
90
  }
96
91
 
97
- const MS_PER_DAY = 1000 * 60 * 60 * 24;
98
-
99
- function endOfDay(ts) {
100
- return (Math.floor(ts / MS_PER_DAY) + 1) * MS_PER_DAY - 1000;
92
+ function parseDateExpr(expr) {
93
+ switch (typeof expr) {
94
+ case 'number':
95
+ if (expr < 100) {
96
+ throw new Error(`Unrecognized date: ${expr}`);
97
+ }
98
+ return expr < 10000 ? { type: 'year', year: expr } : { type: 'ts', ts: expr };
99
+ case 'string':
100
+ expr = expr.trim();
101
+ const len = expr.length;
102
+ if (!RE_DATE_EXPR.test(expr)) {
103
+ throw new Error(`Unrecognized date: ${expr}`);
104
+ }
105
+ const year = parseInt(expr.slice(0, 4), 10);
106
+ if (isNaN(year)) {
107
+ throw new Error(`Unrecognized date: ${expr}`);
108
+ }
109
+ if (len === 4) {
110
+ return { type: 'year', year };
111
+ }
112
+ if (len === 7 && (expr.charAt(5) === 'Q' || expr.charAt(5) === 'q')) {
113
+ const quarter = parseInt(expr.charAt(6), 10);
114
+ if (isNaN(quarter) || quarter < 1 || quarter > 4) {
115
+ throw new Error(`Unrecognized date: ${expr}`);
116
+ }
117
+ return { type: 'quarter', year, quarter };
118
+ }
119
+ const month = parseInt(expr.slice(5, 7), 10);
120
+ if (isNaN(month) || month < 1 || month > 12) {
121
+ throw new Error(`Unrecognized date: ${expr}`);
122
+ }
123
+ if (len === 7) {
124
+ return { type: 'month', year, month };
125
+ }
126
+ if (expr.charAt(8) === 'W' || expr.charAt(8) === 'w') {
127
+ const week = parseInt(expr.charAt(9), 10);
128
+ if (isNaN(week) || week < 1 || week > 4) {
129
+ throw new Error(`Unrecognized date: ${expr}`);
130
+ }
131
+ return { type: 'week', year, month, week };
132
+ }
133
+ let date;
134
+ try {
135
+ // parse so we can validate the day part
136
+ date = new Date(Date.parse(expr));
137
+ } catch (_) {
138
+ throw new Error(`Unrecognized date: ${expr}`);
139
+ }
140
+ return { type: 'day', year, month, day: date.getUTCDate() };
141
+ default:
142
+ throw new Error(`Unrecognized date: ${expr}`);
143
+ }
101
144
  }
@@ -0,0 +1,31 @@
1
+ import { test } from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+ import { startOfDate, endOfDate } from '../src/index.js';
4
+
5
+ test('startOfDate', () => {
6
+ assert.equal(startOfDate('2025'), Date.parse('2025-01-01T00:00:00Z'));
7
+ assert.equal(startOfDate('2025-Q1'), Date.parse('2025-01-01T00:00:00Z'));
8
+ assert.equal(startOfDate('2025-q2'), Date.parse('2025-04-01T00:00:00Z'));
9
+ assert.equal(startOfDate('2025-Q3'), Date.parse('2025-07-01T00:00:00Z'));
10
+ assert.equal(startOfDate('2025-q4'), Date.parse('2025-10-01T00:00:00Z'));
11
+ assert.equal(startOfDate('2025-02'), Date.parse('2025-02-01T00:00:00Z'));
12
+ assert.equal(startOfDate('2025-02-W1'), Date.parse('2025-02-01T00:00:00Z'));
13
+ assert.equal(startOfDate('2025-02-w2'), Date.parse('2025-02-08T00:00:00Z'));
14
+ assert.equal(startOfDate('2025-02-03'), Date.parse('2025-02-03T00:00:00Z'));
15
+ });
16
+
17
+ test('endOfDate', () => {
18
+ assert.equal(endOfDate('2025'), Date.parse('2025-12-31T23:59:59Z'));
19
+ assert.equal(endOfDate('2025-Q1'), Date.parse('2025-03-31T23:59:59Z'));
20
+ assert.equal(endOfDate('2025-q2'), Date.parse('2025-06-30T23:59:59Z'));
21
+ assert.equal(endOfDate('2025-Q3'), Date.parse('2025-09-30T23:59:59Z'));
22
+ assert.equal(endOfDate('2025-q4'), Date.parse('2025-12-31T23:59:59Z'));
23
+ assert.equal(endOfDate('2025-02'), Date.parse('2025-02-28T23:59:59Z'));
24
+ assert.equal(endOfDate('2025-03-W1'), Date.parse('2025-03-07T23:59:59Z'));
25
+ assert.equal(endOfDate('2025-03-w2'), Date.parse('2025-03-14T23:59:59Z'));
26
+ assert.equal(endOfDate('2025-03-W3'), Date.parse('2025-03-21T23:59:59Z'));
27
+ assert.equal(endOfDate('2025-03-w4'), Date.parse('2025-03-31T23:59:59Z'));
28
+ assert.equal(endOfDate('2025-03-03'), Date.parse('2025-03-03T23:59:59Z'));
29
+ });
30
+
31
+ test.run();