uplot-webgpu 0.1.0
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/CANVAS_PROXY.md +602 -0
- package/README.md +854 -0
- package/favicon.ico +0 -0
- package/index.html +14 -0
- package/index.js +21 -0
- package/original/paths.canvas2d/bars.js +252 -0
- package/original/paths.canvas2d/catmullRomCentrip.js +125 -0
- package/original/paths.canvas2d/linear.js +170 -0
- package/original/paths.canvas2d/monotoneCubic.js +68 -0
- package/original/paths.canvas2d/points.js +66 -0
- package/original/paths.canvas2d/spline.js +103 -0
- package/original/paths.canvas2d/stepped.js +124 -0
- package/original/paths.canvas2d/utils.js +301 -0
- package/original/uPlot.canvas2d.js +3548 -0
- package/package.json +110 -0
- package/paths/bars.js +253 -0
- package/paths/catmullRomCentrip.js +126 -0
- package/paths/linear.js +171 -0
- package/paths/monotoneCubic.js +69 -0
- package/paths/points.js +67 -0
- package/paths/spline.js +104 -0
- package/paths/stepped.js +125 -0
- package/paths/utils.js +301 -0
- package/scripts/uPlot.css +168 -0
- package/scripts/uPlot.d.ts +26 -0
- package/scripts/uPlot.js +3687 -0
- package/scripts/utils/dom.js +124 -0
- package/scripts/utils/domClasses.js +22 -0
- package/scripts/utils/feats.js +13 -0
- package/scripts/utils/fmtDate.js +398 -0
- package/scripts/utils/opts.js +844 -0
- package/scripts/utils/strings.js +22 -0
- package/scripts/utils/sync.js +27 -0
- package/scripts/utils/utils.js +692 -0
- package/scripts/webgpu/GPUPath.d.ts +46 -0
- package/scripts/webgpu/GPUPath.js +633 -0
- package/scripts/webgpu/GPUPath.ts +634 -0
- package/scripts/webgpu/WebGPURenderer.d.ts +176 -0
- package/scripts/webgpu/WebGPURenderer.js +4256 -0
- package/scripts/webgpu/WebGPURenderer.ts +4257 -0
- package/scripts/webgpu/browserSmokeHarness.js +105 -0
- package/scripts/webgpu/exporters.d.ts +8 -0
- package/scripts/webgpu/exporters.js +212 -0
- package/scripts/webgpu/shaders.d.ts +2 -0
- package/scripts/webgpu/shaders.js +76 -0
- package/scripts/webgpu/shaders.ts +77 -0
- package/scripts/webgpu/smokeTest.d.ts +2 -0
- package/scripts/webgpu/smokeTest.js +144 -0
- package/scripts/webgpu/webgpu-ambient.d.ts +41 -0
- package/tinybuild.config.js +109 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OFF,
|
|
3
|
+
} from './domClasses.js';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
change,
|
|
7
|
+
dppxchange,
|
|
8
|
+
} from './strings.js';
|
|
9
|
+
|
|
10
|
+
export const domEnv = typeof window != 'undefined';
|
|
11
|
+
|
|
12
|
+
export const doc = domEnv ? document : null;
|
|
13
|
+
export const win = domEnv ? window : null;
|
|
14
|
+
|
|
15
|
+
export let pxRatio;
|
|
16
|
+
|
|
17
|
+
//export const canHover = domEnv && !win.matchMedia('(hover: none)').matches;
|
|
18
|
+
|
|
19
|
+
let query;
|
|
20
|
+
|
|
21
|
+
function setPxRatio() {
|
|
22
|
+
let _pxRatio = devicePixelRatio;
|
|
23
|
+
|
|
24
|
+
// during print preview, Chrome fires off these dppx queries even without changes
|
|
25
|
+
if (pxRatio != _pxRatio) {
|
|
26
|
+
pxRatio = _pxRatio;
|
|
27
|
+
|
|
28
|
+
query && off(change, query, setPxRatio);
|
|
29
|
+
query = matchMedia(`(min-resolution: ${pxRatio - 0.001}dppx) and (max-resolution: ${pxRatio + 0.001}dppx)`);
|
|
30
|
+
on(change, query, setPxRatio);
|
|
31
|
+
|
|
32
|
+
win.dispatchEvent(new CustomEvent(dppxchange));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function addClass(el, c) {
|
|
37
|
+
if (c != null) {
|
|
38
|
+
let cl = el.classList;
|
|
39
|
+
!cl.contains(c) && cl.add(c);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function remClass(el, c) {
|
|
44
|
+
let cl = el.classList;
|
|
45
|
+
cl.contains(c) && cl.remove(c);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function setStylePx(el, name, value) {
|
|
49
|
+
el.style[name] = value + "px";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function placeTag(tag, cls, targ, refEl) {
|
|
53
|
+
let el = doc.createElement(tag);
|
|
54
|
+
|
|
55
|
+
if (cls != null)
|
|
56
|
+
addClass(el, cls);
|
|
57
|
+
|
|
58
|
+
if (targ != null)
|
|
59
|
+
targ.insertBefore(el, refEl);
|
|
60
|
+
|
|
61
|
+
return el;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function placeDiv(cls, targ) {
|
|
65
|
+
return placeTag("div", cls, targ);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const xformCache = new WeakMap();
|
|
69
|
+
|
|
70
|
+
export function elTrans(el, xPos, yPos, xMax, yMax) {
|
|
71
|
+
let xform = "translate(" + xPos + "px," + yPos + "px)";
|
|
72
|
+
let xformOld = xformCache.get(el);
|
|
73
|
+
|
|
74
|
+
if (xform != xformOld) {
|
|
75
|
+
el.style.transform = xform;
|
|
76
|
+
xformCache.set(el, xform);
|
|
77
|
+
|
|
78
|
+
if (xPos < 0 || yPos < 0 || xPos > xMax || yPos > yMax)
|
|
79
|
+
addClass(el, OFF);
|
|
80
|
+
else
|
|
81
|
+
remClass(el, OFF);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const colorCache = new WeakMap();
|
|
86
|
+
|
|
87
|
+
export function elColor(el, background, borderColor) {
|
|
88
|
+
let newColor = background + borderColor;
|
|
89
|
+
let oldColor = colorCache.get(el);
|
|
90
|
+
|
|
91
|
+
if (newColor != oldColor) {
|
|
92
|
+
colorCache.set(el, newColor);
|
|
93
|
+
el.style.background = background;
|
|
94
|
+
el.style.borderColor = borderColor;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const sizeCache = new WeakMap();
|
|
99
|
+
|
|
100
|
+
export function elSize(el, newWid, newHgt, centered) {
|
|
101
|
+
let newSize = newWid + "" + newHgt;
|
|
102
|
+
let oldSize = sizeCache.get(el);
|
|
103
|
+
|
|
104
|
+
if (newSize != oldSize) {
|
|
105
|
+
sizeCache.set(el, newSize);
|
|
106
|
+
el.style.height = newHgt + "px";
|
|
107
|
+
el.style.width = newWid + "px";
|
|
108
|
+
el.style.marginLeft = centered ? -newWid/2 + "px" : 0;
|
|
109
|
+
el.style.marginTop = centered ? -newHgt/2 + "px" : 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const evOpts = {passive: true};
|
|
114
|
+
const evOpts2 = {...evOpts, capture: true};
|
|
115
|
+
|
|
116
|
+
export function on(ev, el, cb, capt) {
|
|
117
|
+
el.addEventListener(ev, cb, capt ? evOpts2 : evOpts);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function off(ev, el, cb, capt) {
|
|
121
|
+
el.removeEventListener(ev, cb, capt ? evOpts2 : evOpts);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
domEnv && setPxRatio();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const pre = "u-";
|
|
2
|
+
|
|
3
|
+
export const UPLOT = "uplot";
|
|
4
|
+
export const ORI_HZ = pre + "hz";
|
|
5
|
+
export const ORI_VT = pre + "vt";
|
|
6
|
+
export const TITLE = pre + "title";
|
|
7
|
+
export const WRAP = pre + "wrap";
|
|
8
|
+
export const UNDER = pre + "under";
|
|
9
|
+
export const OVER = pre + "over";
|
|
10
|
+
export const AXIS = pre + "axis";
|
|
11
|
+
export const OFF = pre + "off";
|
|
12
|
+
export const SELECT = pre + "select";
|
|
13
|
+
export const CURSOR_X = pre + "cursor-x";
|
|
14
|
+
export const CURSOR_Y = pre + "cursor-y";
|
|
15
|
+
export const CURSOR_PT = pre + "cursor-pt";
|
|
16
|
+
export const LEGEND = pre + "legend"
|
|
17
|
+
export const LEGEND_LIVE = pre + "live";
|
|
18
|
+
export const LEGEND_INLINE = pre + "inline";
|
|
19
|
+
export const LEGEND_SERIES = pre + "series";
|
|
20
|
+
export const LEGEND_MARKER = pre + "marker";
|
|
21
|
+
export const LEGEND_LABEL = pre + "label";
|
|
22
|
+
export const LEGEND_VALUE = pre + "value";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const FEAT_TIME = true;
|
|
2
|
+
export const FEAT_LEGEND = true;
|
|
3
|
+
|
|
4
|
+
export const FEAT_POINTS = true;
|
|
5
|
+
|
|
6
|
+
export const FEAT_PATHS = true;
|
|
7
|
+
export const FEAT_PATHS_LINEAR = true;
|
|
8
|
+
export const FEAT_PATHS_STEPPED = true;
|
|
9
|
+
export const FEAT_PATHS_BARS = true;
|
|
10
|
+
export const FEAT_PATHS_SPLINE = true;
|
|
11
|
+
export const FEAT_PATHS_SPLINE2 = true;
|
|
12
|
+
|
|
13
|
+
export const FEAT_JOIN = true;
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FEAT_TIME,
|
|
3
|
+
} from './feats.js';
|
|
4
|
+
import { abs, floor } from './utils.js';
|
|
5
|
+
|
|
6
|
+
const months = [
|
|
7
|
+
"January",
|
|
8
|
+
"February",
|
|
9
|
+
"March",
|
|
10
|
+
"April",
|
|
11
|
+
"May",
|
|
12
|
+
"June",
|
|
13
|
+
"July",
|
|
14
|
+
"August",
|
|
15
|
+
"September",
|
|
16
|
+
"October",
|
|
17
|
+
"November",
|
|
18
|
+
"December",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const days = [
|
|
22
|
+
"Sunday",
|
|
23
|
+
"Monday",
|
|
24
|
+
"Tuesday",
|
|
25
|
+
"Wednesday",
|
|
26
|
+
"Thursday",
|
|
27
|
+
"Friday",
|
|
28
|
+
"Saturday",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function slice3(str) {
|
|
32
|
+
return str.slice(0, 3);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const days3 = FEAT_TIME && days.map(slice3);
|
|
36
|
+
|
|
37
|
+
const months3 = FEAT_TIME && months.map(slice3);
|
|
38
|
+
|
|
39
|
+
const engNames = {
|
|
40
|
+
MMMM: months,
|
|
41
|
+
MMM: months3,
|
|
42
|
+
WWWW: days,
|
|
43
|
+
WWW: days3,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function zeroPad2(int) {
|
|
47
|
+
return (int < 10 ? '0' : '') + int;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function zeroPad3(int) {
|
|
51
|
+
return (int < 10 ? '00' : int < 100 ? '0' : '') + int;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
function suffix(int) {
|
|
56
|
+
let mod10 = int % 10;
|
|
57
|
+
|
|
58
|
+
return int + (
|
|
59
|
+
mod10 == 1 && int != 11 ? "st" :
|
|
60
|
+
mod10 == 2 && int != 12 ? "nd" :
|
|
61
|
+
mod10 == 3 && int != 13 ? "rd" : "th"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
const subs = {
|
|
67
|
+
// 2019
|
|
68
|
+
YYYY: d => d.getFullYear(),
|
|
69
|
+
// 19
|
|
70
|
+
YY: d => (d.getFullYear()+'').slice(2),
|
|
71
|
+
// July
|
|
72
|
+
MMMM: (d, names) => names.MMMM[d.getMonth()],
|
|
73
|
+
// Jul
|
|
74
|
+
MMM: (d, names) => names.MMM[d.getMonth()],
|
|
75
|
+
// 07
|
|
76
|
+
MM: d => zeroPad2(d.getMonth()+1),
|
|
77
|
+
// 7
|
|
78
|
+
M: d => d.getMonth()+1,
|
|
79
|
+
// 09
|
|
80
|
+
DD: d => zeroPad2(d.getDate()),
|
|
81
|
+
// 9
|
|
82
|
+
D: d => d.getDate(),
|
|
83
|
+
// Monday
|
|
84
|
+
WWWW: (d, names) => names.WWWW[d.getDay()],
|
|
85
|
+
// Mon
|
|
86
|
+
WWW: (d, names) => names.WWW[d.getDay()],
|
|
87
|
+
// 03
|
|
88
|
+
HH: d => zeroPad2(d.getHours()),
|
|
89
|
+
// 3
|
|
90
|
+
H: d => d.getHours(),
|
|
91
|
+
// 9 (12hr, unpadded)
|
|
92
|
+
h: d => {let h = d.getHours(); return h == 0 ? 12 : h > 12 ? h - 12 : h;},
|
|
93
|
+
// AM
|
|
94
|
+
AA: d => d.getHours() >= 12 ? 'PM' : 'AM',
|
|
95
|
+
// am
|
|
96
|
+
aa: d => d.getHours() >= 12 ? 'pm' : 'am',
|
|
97
|
+
// a
|
|
98
|
+
a: d => d.getHours() >= 12 ? 'p' : 'a',
|
|
99
|
+
// 09
|
|
100
|
+
mm: d => zeroPad2(d.getMinutes()),
|
|
101
|
+
// 9
|
|
102
|
+
m: d => d.getMinutes(),
|
|
103
|
+
// 09
|
|
104
|
+
ss: d => zeroPad2(d.getSeconds()),
|
|
105
|
+
// 9
|
|
106
|
+
s: d => d.getSeconds(),
|
|
107
|
+
// 374
|
|
108
|
+
fff: d => zeroPad3(d.getMilliseconds()),
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
// this really only makes sense for DateZoned
|
|
112
|
+
// -05:00
|
|
113
|
+
tzo: d => {
|
|
114
|
+
let o = d.getTimezoneOffset();
|
|
115
|
+
let s = o > 0 ? '-' : '+';
|
|
116
|
+
o = abs(o);
|
|
117
|
+
let hh = zeroPad2(floor(o / 60));
|
|
118
|
+
let mm = zeroPad2(o % 60);
|
|
119
|
+
return `${s}${hh}:${mm}`;
|
|
120
|
+
}
|
|
121
|
+
*/
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// export const iso8601 = fmtDate('{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}.{fff}{tzo}');
|
|
125
|
+
|
|
126
|
+
export function fmtDate(tpl, names) {
|
|
127
|
+
names = names || engNames;
|
|
128
|
+
let parts = [];
|
|
129
|
+
|
|
130
|
+
let R = /\{([a-z]+)\}|[^{]+/gi, m;
|
|
131
|
+
|
|
132
|
+
while (m = R.exec(tpl))
|
|
133
|
+
parts.push(m[0][0] == '{' ? subs[m[1]] : m[0]);
|
|
134
|
+
|
|
135
|
+
return d => {
|
|
136
|
+
let out = '';
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < parts.length; i++)
|
|
139
|
+
out += typeof parts[i] == "string" ? parts[i] : parts[i](d, names);
|
|
140
|
+
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const localTz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
146
|
+
|
|
147
|
+
export function tzDate(dateOrTs, tz) {
|
|
148
|
+
if (tz == null || tz == localTz)
|
|
149
|
+
return typeof dateOrTs == 'number' ? new Date(dateOrTs) : dateOrTs;
|
|
150
|
+
|
|
151
|
+
let d = new DateZoned(dateOrTs);
|
|
152
|
+
d.setTimeZone(tz);
|
|
153
|
+
return d;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const twoDigit = '2-digit';
|
|
157
|
+
|
|
158
|
+
const fmtrOpts = {
|
|
159
|
+
weekday: "short",
|
|
160
|
+
year: 'numeric',
|
|
161
|
+
month: twoDigit,
|
|
162
|
+
day: twoDigit,
|
|
163
|
+
hour: twoDigit,
|
|
164
|
+
minute: twoDigit,
|
|
165
|
+
second: twoDigit,
|
|
166
|
+
fractionalSecondDigits: 3,
|
|
167
|
+
timeZoneName: 'longOffset',
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/*
|
|
171
|
+
// this might be a bit easier to parse to avoid negative .slice() offsets
|
|
172
|
+
new Intl.DateTimeFormat('en-US', {
|
|
173
|
+
hour12: false,
|
|
174
|
+
timeZone: 'Europe/London',
|
|
175
|
+
year: 'numeric',
|
|
176
|
+
month: '2-digit',
|
|
177
|
+
day: '2-digit',
|
|
178
|
+
hour: '2-digit',
|
|
179
|
+
minute: '2-digit',
|
|
180
|
+
second: '2-digit',
|
|
181
|
+
timeZoneName: 'longOffset',
|
|
182
|
+
weekday: 'short',
|
|
183
|
+
fractionalSecondDigits: 3,
|
|
184
|
+
}).format(new Date());
|
|
185
|
+
|
|
186
|
+
// Tue, 07/22/2025, 07:02:37.043 GMT+01:00
|
|
187
|
+
*/
|
|
188
|
+
|
|
189
|
+
const tzFmt = {};
|
|
190
|
+
|
|
191
|
+
function getFormatter(tz) {
|
|
192
|
+
if (tzFmt[tz] == null)
|
|
193
|
+
tzFmt[tz] = new Intl.DateTimeFormat("sv", {...fmtrOpts, timeZone: tz}).format;
|
|
194
|
+
|
|
195
|
+
return tzFmt[tz];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export class DateZoned extends Date {
|
|
199
|
+
tz = null;
|
|
200
|
+
#utc = false;
|
|
201
|
+
// sön, 1972-10-15 17:25:23,434 GMT+01:00
|
|
202
|
+
#str = null;
|
|
203
|
+
|
|
204
|
+
constructor(...args) {
|
|
205
|
+
super(...args);
|
|
206
|
+
|
|
207
|
+
if (args[0] instanceof DateZoned) {
|
|
208
|
+
this.tz = args[0].tz;
|
|
209
|
+
this.#str = args[0].#str;
|
|
210
|
+
this.#utc = args[0].#utc;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#get(utcMeth, locMeth, fr, to, add = 0) {
|
|
215
|
+
let s = this.#str;
|
|
216
|
+
return this.#utc ? utcMeth.call(this) : s == null ? locMeth.call(this) : Number(s.slice(fr,to)) + add;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setTimeZone(tz) {
|
|
220
|
+
this.tz = tz;
|
|
221
|
+
|
|
222
|
+
if (tz == 'UTC' || tz == 'Etc/UTC')
|
|
223
|
+
this.#utc = true;
|
|
224
|
+
else {
|
|
225
|
+
let fmt = getFormatter(tz);
|
|
226
|
+
let f = fmt(this);
|
|
227
|
+
|
|
228
|
+
if (f.endsWith('GMT'))
|
|
229
|
+
f += '+00:00';
|
|
230
|
+
|
|
231
|
+
this.#str = f;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
getFullYear() {
|
|
236
|
+
return this.#get(this.getUTCFullYear, super.getFullYear, -33, -29);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
getMonth() {
|
|
240
|
+
return this.#get(this.getUTCMonth, super.getMonth, -28, -26, -1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getDate() {
|
|
244
|
+
return this.#get(this.getUTCDate, super.getDate, -25, -23);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getHours() {
|
|
248
|
+
return this.#get(this.getUTCHours, super.getHours, -22, -20);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getMinutes() {
|
|
252
|
+
return this.#get(this.getUTCMinutes, super.getMinutes, -19, -17);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getSeconds() {
|
|
256
|
+
return this.#get(this.getUTCSeconds, super.getSeconds, -16, -14);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
getMilliseconds() {
|
|
260
|
+
return this.#get(this.getUTCMilliseconds, super.getMilliseconds, -13, -10);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
getDay() {
|
|
264
|
+
let s = this.#str;
|
|
265
|
+
return this.#utc ? this.getUTCDay() : s == null ? super.getDay() : (
|
|
266
|
+
s[0] == 's' ? 0 : // sön
|
|
267
|
+
s[0] == 'm' ? 1 : // mån
|
|
268
|
+
s[1] == 'i' ? 2 : // tis
|
|
269
|
+
s[0] == 'o' ? 3 : // ons
|
|
270
|
+
s[1] == 'o' ? 4 : // tors
|
|
271
|
+
s[0] == 'f' ? 5 : // fre
|
|
272
|
+
s[0] == 'l' ? 6 : // lör
|
|
273
|
+
-1
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getTimezoneOffset() {
|
|
278
|
+
let s = this.#str;
|
|
279
|
+
return this.#utc ? 0 : s == null ? super.getTimezoneOffset() : (60 * Number(s.slice(-5,-3)) + Number(s.slice(-2))) * (s.at(-6) == '-' ? -1 : 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getDayOfYear(date) {
|
|
284
|
+
let y = date.getFullYear();
|
|
285
|
+
let m = date.getMonth() + 1;
|
|
286
|
+
let d = date.getDate();
|
|
287
|
+
|
|
288
|
+
// https://stackoverflow.com/a/27790471
|
|
289
|
+
return --m*31-(m>1?(1054267675>>m*3-6&7)-(y&3||!(y%25)&&y&15?0:1):0)+d;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function leapYear(year) {
|
|
293
|
+
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// these can be done through just incrRoundDn of 1e3 or 60 * 1e3
|
|
297
|
+
// export const PERIOD_SECOND = 0;
|
|
298
|
+
// export const PERIOD_MINUTE = 1;
|
|
299
|
+
|
|
300
|
+
// this might be needed for tzs where DST is not whole hours?
|
|
301
|
+
// otherwise incrRoundDn of 3600 * 1e3
|
|
302
|
+
// export const PERIOD_HOUR = 2;
|
|
303
|
+
|
|
304
|
+
// thse need special handling due to day length changing due to DST
|
|
305
|
+
export const PERIOD_DAY = 3;
|
|
306
|
+
export const PERIOD_MONTH = 4;
|
|
307
|
+
export const PERIOD_YEAR = 5;
|
|
308
|
+
// export const PERIOD_WEEK;
|
|
309
|
+
|
|
310
|
+
// get start of period, requires DateZoned and period const
|
|
311
|
+
export function floorSOP(dz, per) {
|
|
312
|
+
let ts = dz.getTime();
|
|
313
|
+
|
|
314
|
+
// initial guess (assumes no DST)
|
|
315
|
+
let ts2 = ts - (
|
|
316
|
+
dz.getMilliseconds() +
|
|
317
|
+
dz.getSeconds() * 1e3 +
|
|
318
|
+
dz.getMinutes() * 60 * 1e3 +
|
|
319
|
+
dz.getHours() * 3600 * 1e3 +
|
|
320
|
+
(
|
|
321
|
+
(
|
|
322
|
+
per == PERIOD_MONTH ? dz.getDate() - 1:
|
|
323
|
+
per == PERIOD_YEAR ? getDayOfYear(dz) - 1:
|
|
324
|
+
0
|
|
325
|
+
)
|
|
326
|
+
* 24 * 3600 * 1e3
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// if (ts2 == ts)
|
|
331
|
+
// return dz;
|
|
332
|
+
|
|
333
|
+
let dz2 = new DateZoned(ts2);
|
|
334
|
+
dz2.setTimeZone(dz.tz);
|
|
335
|
+
|
|
336
|
+
let h2 = dz2.getHours();
|
|
337
|
+
|
|
338
|
+
// we want hours to be 0
|
|
339
|
+
if (h2 > 0) {
|
|
340
|
+
let dstAdj = h2 > 12 ? 24 - h2 : -h2;
|
|
341
|
+
dz2 = new DateZoned(ts2 + dstAdj * 3600 * 1e3);
|
|
342
|
+
dz2.setTimeZone(dz.tz);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return dz2;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// tweaks the time by +/- 1hr to make sure it lands on 12am
|
|
349
|
+
// used for correcting optimistically-computed ticks from adding fixed increments
|
|
350
|
+
// export function sopNear(dz, per) {}
|
|
351
|
+
|
|
352
|
+
/*
|
|
353
|
+
let fmt = fmtDate('{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}.{fff}{tzo}');
|
|
354
|
+
|
|
355
|
+
{
|
|
356
|
+
let d = new DateZoned(1554274800000); // post-roll date
|
|
357
|
+
d.setTimeZone('Europe/London');
|
|
358
|
+
let sod = getSOP(d, PERIOD_DAY);
|
|
359
|
+
console.log(sod.getTime() / 1e3);
|
|
360
|
+
console.log(fmt(sod));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
{
|
|
364
|
+
let d = new DateZoned(1554274800000); // post-roll date
|
|
365
|
+
d.setTimeZone('America/Chicago');
|
|
366
|
+
let sod = getSOP(d, PERIOD_DAY);
|
|
367
|
+
console.log(sod.getTime() / 1e3);
|
|
368
|
+
console.log(fmt(sod));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
{
|
|
372
|
+
let d = new DateZoned(1554004800000); // few hours after london spring forward
|
|
373
|
+
d.setTimeZone('Europe/London');
|
|
374
|
+
let sod = getSOP(d, PERIOD_DAY);
|
|
375
|
+
console.log(sod.getTime() / 1e3);
|
|
376
|
+
console.log(fmt(sod));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
{
|
|
380
|
+
let d = new DateZoned(1572156000000); // few hours after london fall back
|
|
381
|
+
d.setTimeZone('Europe/London');
|
|
382
|
+
let sod = getSOP(d, PERIOD_DAY);
|
|
383
|
+
console.log(sod.getTime() / 1e3);
|
|
384
|
+
console.log(fmt(sod));
|
|
385
|
+
}
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
/*
|
|
390
|
+
TODO:
|
|
391
|
+
|
|
392
|
+
2024 - leap year
|
|
393
|
+
start of year before feb vs after
|
|
394
|
+
start of month in dst fwd month / bwd month
|
|
395
|
+
start of day in dst fwd day / bwd day
|
|
396
|
+
|
|
397
|
+
Australia/Darwin
|
|
398
|
+
*/
|