@synnaxlabs/x 0.45.1 → 0.46.1
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/.turbo/turbo-build.log +33 -31
- package/dist/array.cjs +1 -1
- package/dist/array.js +2 -5
- package/dist/binary.cjs +1 -1
- package/dist/binary.js +1 -1
- package/dist/box-BXWXSkKu.js +203 -0
- package/dist/box-rH3ggwXk.cjs +1 -0
- package/dist/box.cjs +1 -1
- package/dist/box.js +1 -1
- package/dist/caseconv.cjs +1 -1
- package/dist/caseconv.js +1 -1
- package/dist/deep.cjs +1 -1
- package/dist/deep.js +1 -1
- package/dist/{external-tyaEMW4S.js → external-2YWy569j.js} +3 -3
- package/dist/{external-uXk0Avrg.cjs → external-B6edOwoQ.cjs} +1 -1
- package/dist/{external-BPgtxa8d.js → external-B80i4ymZ.js} +2 -2
- package/dist/external-BxmTQZ6m.cjs +1 -0
- package/dist/external-DLiGrXn7.cjs +1 -0
- package/dist/external-Du5qzfYv.js +35 -0
- package/dist/get-CtJEJIC_.js +82 -0
- package/dist/get-D2VRwUw4.cjs +1 -0
- package/dist/{index-CwGPVvbl.cjs → index-Bfvg0v-N.cjs} +1 -1
- package/dist/{index-uDxeM-cl.cjs → index-CyNZHQFw.cjs} +1 -1
- package/dist/{index-Bxlv0uf_.js → index-qmkoZBNO.js} +1 -1
- package/dist/{index-BHXRDFYj.js → index-yz34Wc2p.js} +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +498 -439
- package/dist/{location-BPoXwOni.cjs → location-0qDBiCqP.cjs} +1 -1
- package/dist/{location-CVxysrHI.js → location-BIzpxczO.js} +1 -1
- package/dist/location.cjs +1 -1
- package/dist/location.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/{scale-DjxC6ep2.js → scale-BBWhTUqJ.js} +3 -3
- package/dist/{scale-BHs716im.cjs → scale-BXy1w8R_.cjs} +1 -1
- package/dist/scale.cjs +1 -1
- package/dist/scale.js +1 -1
- package/dist/series-B2zqvP8A.cjs +6 -0
- package/dist/{series-W5Aafjeu.js → series-Clbw-fZI.js} +543 -451
- package/dist/spatial.cjs +1 -1
- package/dist/spatial.js +4 -4
- package/dist/src/array/external.d.ts +4 -0
- package/dist/src/array/external.d.ts.map +1 -0
- package/dist/src/array/index.d.ts +1 -1
- package/dist/src/array/index.d.ts.map +1 -1
- package/dist/src/array/nullable.d.ts +3 -0
- package/dist/src/array/nullable.d.ts.map +1 -0
- package/dist/src/array/update.d.ts +14 -0
- package/dist/src/array/update.d.ts.map +1 -0
- package/dist/src/array/update.spec.d.ts +2 -0
- package/dist/src/array/update.spec.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/label/index.d.ts +2 -0
- package/dist/src/label/index.d.ts.map +1 -0
- package/dist/src/label/label.d.ts +11 -0
- package/dist/src/label/label.d.ts.map +1 -0
- package/dist/src/math/constants.d.ts +16 -0
- package/dist/src/math/constants.d.ts.map +1 -0
- package/dist/src/math/external.d.ts +1 -0
- package/dist/src/math/external.d.ts.map +1 -1
- package/dist/src/math/round.d.ts +10 -0
- package/dist/src/math/round.d.ts.map +1 -1
- package/dist/src/migrate/migrate.d.ts +1 -1
- package/dist/src/spatial/box/box.d.ts.map +1 -1
- package/dist/src/spatial/xy/xy.d.ts +10 -0
- package/dist/src/spatial/xy/xy.d.ts.map +1 -1
- package/dist/src/status/status.d.ts +26 -15
- package/dist/src/status/status.d.ts.map +1 -1
- package/dist/src/strings/strings.d.ts +1 -0
- package/dist/src/strings/strings.d.ts.map +1 -1
- package/dist/src/telem/series.d.ts +4 -4
- package/dist/src/telem/series.d.ts.map +1 -1
- package/dist/src/telem/telem.d.ts +35 -29
- package/dist/src/telem/telem.d.ts.map +1 -1
- package/dist/telem.cjs +1 -1
- package/dist/telem.js +14 -13
- package/dist/{xy-DWwtHmgn.cjs → xy-C-MUIjVs.cjs} +1 -1
- package/dist/{xy-DYPw8-8C.js → xy-DnrCAZaw.js} +16 -9
- package/dist/xy.cjs +1 -1
- package/dist/xy.js +1 -1
- package/dist/zod.cjs +1 -1
- package/dist/zod.js +1 -1
- package/package.json +9 -9
- package/src/array/external.ts +12 -0
- package/src/array/index.ts +1 -1
- package/src/array/nullable.ts +7 -0
- package/src/array/update.spec.ts +133 -0
- package/src/array/update.ts +66 -0
- package/src/caseconv/caseconv.spec.ts +2 -0
- package/src/caseconv/caseconv.ts +9 -9
- package/src/deep/partial.spec.ts +9 -9
- package/src/deep/path.spec.ts +5 -2
- package/src/index.ts +1 -0
- package/src/label/index.ts +10 -0
- package/src/label/label.ts +20 -0
- package/src/math/constants.ts +29 -0
- package/src/math/external.ts +1 -0
- package/src/math/round.spec.ts +168 -0
- package/src/math/round.ts +48 -0
- package/src/spatial/box/box.ts +1 -2
- package/src/spatial/xy/xy.ts +18 -1
- package/src/status/status.spec.ts +55 -0
- package/src/status/status.ts +49 -30
- package/src/strings/strings.spec.ts +42 -0
- package/src/strings/strings.ts +14 -0
- package/src/telem/series.ts +2 -3
- package/src/telem/telem.spec.ts +155 -18
- package/src/telem/telem.ts +185 -56
- package/src/testutil/testutil.spec.ts +1 -1
- package/src/zod/nullToUndefined.spec.ts +2 -2
- package/src/zod/toArray.spec.ts +9 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/.vscode/settings.json +0 -5
- package/README.md +0 -38
- package/dist/box-BYuq-Gjx.js +0 -203
- package/dist/box-Blu-4d1n.cjs +0 -1
- package/dist/external-CtHGFcox.cjs +0 -1
- package/dist/get-CXkBfLu1.js +0 -82
- package/dist/get-OP63N4c3.cjs +0 -1
- package/dist/series-C6ZwNf8i.cjs +0 -6
package/src/telem/telem.spec.ts
CHANGED
|
@@ -73,6 +73,11 @@ describe("TimeStamp", () => {
|
|
|
73
73
|
expect(ts.equals(TimeSpan.microseconds(10).add(TimeStamp.utcOffset))).toBe(true);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
test("constructing from MIN and MAX as numbers", () => {
|
|
77
|
+
expect(new TimeStamp(TimeStamp.MIN.nanoseconds).equals(TimeStamp.MIN)).toBe(true);
|
|
78
|
+
expect(new TimeStamp(TimeStamp.MAX.nanoseconds).equals(TimeStamp.MAX)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
76
81
|
test("construct from time string", () => {
|
|
77
82
|
const ts = new TimeStamp("12:30", "UTC");
|
|
78
83
|
expect(ts.date().getUTCHours()).toEqual(12);
|
|
@@ -460,7 +465,7 @@ describe("TimeStamp", () => {
|
|
|
460
465
|
});
|
|
461
466
|
});
|
|
462
467
|
|
|
463
|
-
describe("
|
|
468
|
+
describe("toString with formats", () => {
|
|
464
469
|
const ts = new TimeStamp([2022, 12, 15], "UTC")
|
|
465
470
|
.add(TimeSpan.hours(12))
|
|
466
471
|
.add(TimeSpan.minutes(20))
|
|
@@ -479,7 +484,7 @@ describe("TimeStamp", () => {
|
|
|
479
484
|
|
|
480
485
|
FORMAT_TESTS.forEach(([format, expected]) => {
|
|
481
486
|
test(`should format timestamp as ${format}`, () => {
|
|
482
|
-
expect(ts.
|
|
487
|
+
expect(ts.toString(format, "UTC")).toEqual(expected);
|
|
483
488
|
});
|
|
484
489
|
});
|
|
485
490
|
});
|
|
@@ -925,6 +930,137 @@ describe("TimeSpan", () => {
|
|
|
925
930
|
});
|
|
926
931
|
});
|
|
927
932
|
|
|
933
|
+
describe("toString with semantic format", () => {
|
|
934
|
+
const TESTS: [TimeSpan, string][] = [
|
|
935
|
+
// Sub-second durations
|
|
936
|
+
[TimeSpan.ZERO, "0s"],
|
|
937
|
+
[TimeSpan.nanoseconds(50), "< 1s"],
|
|
938
|
+
[TimeSpan.microseconds(50), "< 1s"],
|
|
939
|
+
[TimeSpan.milliseconds(50), "< 1s"],
|
|
940
|
+
[TimeSpan.milliseconds(999), "< 1s"],
|
|
941
|
+
|
|
942
|
+
// Seconds
|
|
943
|
+
[TimeSpan.seconds(1), "1s"],
|
|
944
|
+
[TimeSpan.seconds(30), "30s"],
|
|
945
|
+
[TimeSpan.seconds(59), "59s"],
|
|
946
|
+
|
|
947
|
+
// Minutes with seconds (< 5 minutes)
|
|
948
|
+
[TimeSpan.seconds(60), "1m"],
|
|
949
|
+
[TimeSpan.seconds(90), "1m 30s"],
|
|
950
|
+
[TimeSpan.seconds(119), "1m 59s"],
|
|
951
|
+
[TimeSpan.seconds(120), "2m"],
|
|
952
|
+
[TimeSpan.seconds(150), "2m 30s"],
|
|
953
|
+
[TimeSpan.seconds(240), "4m"],
|
|
954
|
+
[TimeSpan.seconds(270), "4m 30s"],
|
|
955
|
+
|
|
956
|
+
// Minutes without seconds (>= 5 minutes)
|
|
957
|
+
[TimeSpan.seconds(300), "5m"],
|
|
958
|
+
[TimeSpan.seconds(330), "5m"], // seconds dropped
|
|
959
|
+
[TimeSpan.minutes(30), "30m"],
|
|
960
|
+
[TimeSpan.minutes(59), "59m"],
|
|
961
|
+
|
|
962
|
+
// Hours with minutes (< 3 hours)
|
|
963
|
+
[TimeSpan.minutes(60), "1h"],
|
|
964
|
+
[TimeSpan.minutes(90), "1h 30m"],
|
|
965
|
+
[TimeSpan.minutes(119), "1h 59m"],
|
|
966
|
+
[TimeSpan.minutes(120), "2h"],
|
|
967
|
+
[TimeSpan.minutes(150), "2h 30m"],
|
|
968
|
+
[TimeSpan.minutes(179), "2h 59m"],
|
|
969
|
+
|
|
970
|
+
// Hours without minutes (>= 3 hours)
|
|
971
|
+
[TimeSpan.minutes(180), "3h"],
|
|
972
|
+
[TimeSpan.minutes(195), "3h"], // minutes dropped
|
|
973
|
+
[TimeSpan.hours(12), "12h"],
|
|
974
|
+
[TimeSpan.hours(23), "23h"],
|
|
975
|
+
|
|
976
|
+
// Days with hours (< 2 days)
|
|
977
|
+
[TimeSpan.hours(24), "1d"],
|
|
978
|
+
[TimeSpan.hours(25), "1d 1h"],
|
|
979
|
+
[TimeSpan.hours(36), "1d 12h"],
|
|
980
|
+
[TimeSpan.hours(47), "1d 23h"],
|
|
981
|
+
|
|
982
|
+
// Days without hours (>= 2 days)
|
|
983
|
+
[TimeSpan.hours(48), "2d"],
|
|
984
|
+
[TimeSpan.hours(50), "2d"], // hours dropped
|
|
985
|
+
[TimeSpan.days(3), "3d"],
|
|
986
|
+
[TimeSpan.days(6), "6d"],
|
|
987
|
+
|
|
988
|
+
// Weeks with days (< 2 weeks)
|
|
989
|
+
[TimeSpan.days(7), "1w"],
|
|
990
|
+
[TimeSpan.days(8), "1w 1d"],
|
|
991
|
+
[TimeSpan.days(10), "1w 3d"],
|
|
992
|
+
[TimeSpan.days(13), "1w 6d"],
|
|
993
|
+
|
|
994
|
+
// Weeks without days (>= 2 weeks)
|
|
995
|
+
[TimeSpan.days(14), "2w"],
|
|
996
|
+
[TimeSpan.days(15), "2w"], // days dropped
|
|
997
|
+
[TimeSpan.days(21), "3w"],
|
|
998
|
+
[TimeSpan.days(28), "4w"],
|
|
999
|
+
|
|
1000
|
+
// Months with days (< 3 months)
|
|
1001
|
+
[TimeSpan.days(30), "1mo"],
|
|
1002
|
+
[TimeSpan.days(35), "1mo 5d"],
|
|
1003
|
+
[TimeSpan.days(45), "1mo 15d"],
|
|
1004
|
+
[TimeSpan.days(60), "2mo"],
|
|
1005
|
+
[TimeSpan.days(75), "2mo 15d"],
|
|
1006
|
+
|
|
1007
|
+
// Months without days (>= 3 months)
|
|
1008
|
+
[TimeSpan.days(90), "3mo"],
|
|
1009
|
+
[TimeSpan.days(95), "3mo"], // days dropped
|
|
1010
|
+
[TimeSpan.days(180), "6mo"],
|
|
1011
|
+
[TimeSpan.days(330), "11mo"],
|
|
1012
|
+
|
|
1013
|
+
// Years with months (< 2 years)
|
|
1014
|
+
[TimeSpan.days(364), "12mo"],
|
|
1015
|
+
[TimeSpan.days(365), "1y"],
|
|
1016
|
+
[TimeSpan.days(395), "1y 1mo"],
|
|
1017
|
+
[TimeSpan.days(500), "1y 4mo"],
|
|
1018
|
+
[TimeSpan.days(700), "1y 11mo"],
|
|
1019
|
+
|
|
1020
|
+
// Years without months (>= 2 years)
|
|
1021
|
+
[TimeSpan.days(730), "2y"],
|
|
1022
|
+
[TimeSpan.days(750), "2y"], // months dropped
|
|
1023
|
+
[TimeSpan.days(1095), "3y"],
|
|
1024
|
+
[TimeSpan.days(3650), "10y"],
|
|
1025
|
+
|
|
1026
|
+
// Complex durations
|
|
1027
|
+
[TimeSpan.seconds(3661), "1h 1m"],
|
|
1028
|
+
[TimeSpan.minutes(1441), "1d"], // 24h 1m, but minutes are dropped at day level
|
|
1029
|
+
[TimeSpan.minutes(1500), "1d 1h"], // 25h exactly
|
|
1030
|
+
[TimeSpan.hours(169), "1w 1h"],
|
|
1031
|
+
|
|
1032
|
+
// Negative durations
|
|
1033
|
+
[TimeSpan.seconds(-30), "-30s"],
|
|
1034
|
+
[TimeSpan.minutes(-90), "-1h 30m"],
|
|
1035
|
+
[TimeSpan.hours(-25), "-1d 1h"],
|
|
1036
|
+
[TimeSpan.days(-8), "-1w 1d"],
|
|
1037
|
+
[TimeSpan.days(-400), "-1y 1mo"],
|
|
1038
|
+
];
|
|
1039
|
+
|
|
1040
|
+
TESTS.forEach(([ts, expected]) => {
|
|
1041
|
+
test(`${ts.valueOf()} => ${expected}`, () => {
|
|
1042
|
+
expect(ts.toString("semantic")).toEqual(expected);
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
describe("toString with format", () => {
|
|
1048
|
+
test("toString with semantic format", () => {
|
|
1049
|
+
const ts = TimeSpan.hours(25);
|
|
1050
|
+
expect(ts.toString("semantic")).toEqual("1d 1h");
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
test("toString with default format", () => {
|
|
1054
|
+
const ts = TimeSpan.hours(25);
|
|
1055
|
+
expect(ts.toString()).toEqual("1d 1h");
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
test("toString with full format", () => {
|
|
1059
|
+
const ts = TimeSpan.hours(25).add(TimeSpan.minutes(30)).add(TimeSpan.seconds(15));
|
|
1060
|
+
expect(ts.toString("full")).toEqual("1d 1h 30m 15s");
|
|
1061
|
+
});
|
|
1062
|
+
});
|
|
1063
|
+
|
|
928
1064
|
describe("schema", () => {
|
|
929
1065
|
it("should parse bigint", () => {
|
|
930
1066
|
const ts = TimeSpan.z.parse(1000000000n);
|
|
@@ -1632,28 +1768,29 @@ describe("DataType", () => {
|
|
|
1632
1768
|
});
|
|
1633
1769
|
|
|
1634
1770
|
const testCases = [
|
|
1635
|
-
{ input: "int8", expected: "int8" },
|
|
1636
|
-
{ input: "int16", expected: "int16" },
|
|
1637
|
-
{ input: "int32", expected: "int32" },
|
|
1638
|
-
{ input: "int64", expected: "int64" },
|
|
1639
|
-
{ input: "uint8", expected: "uint8" },
|
|
1640
|
-
{ input: "uint16", expected: "uint16" },
|
|
1641
|
-
{ input: "uint32", expected: "uint32" },
|
|
1642
|
-
{ input: "uint64", expected: "uint64" },
|
|
1643
|
-
{ input: "float32", expected: "float32" },
|
|
1644
|
-
{ input: "float64", expected: "float64" },
|
|
1645
|
-
{ input: "string", expected: "string" },
|
|
1646
|
-
{ input: "boolean", expected: "boolean" },
|
|
1647
|
-
{ input: "timestamp", expected: "timestamp" },
|
|
1648
|
-
{ input: "uuid", expected: "uuid" },
|
|
1649
|
-
{ input: "json", expected: "json" },
|
|
1771
|
+
{ input: "int8", expected: "int8", short: "i8" },
|
|
1772
|
+
{ input: "int16", expected: "int16", short: "i16" },
|
|
1773
|
+
{ input: "int32", expected: "int32", short: "i32" },
|
|
1774
|
+
{ input: "int64", expected: "int64", short: "i64" },
|
|
1775
|
+
{ input: "uint8", expected: "uint8", short: "u8" },
|
|
1776
|
+
{ input: "uint16", expected: "uint16", short: "u16" },
|
|
1777
|
+
{ input: "uint32", expected: "uint32", short: "u32" },
|
|
1778
|
+
{ input: "uint64", expected: "uint64", short: "u64" },
|
|
1779
|
+
{ input: "float32", expected: "float32", short: "f32" },
|
|
1780
|
+
{ input: "float64", expected: "float64", short: "f64" },
|
|
1781
|
+
{ input: "string", expected: "string", short: "str" },
|
|
1782
|
+
{ input: "boolean", expected: "boolean", short: "bool" },
|
|
1783
|
+
{ input: "timestamp", expected: "timestamp", short: "ts" },
|
|
1784
|
+
{ input: "uuid", expected: "uuid", short: "uuid" },
|
|
1785
|
+
{ input: "json", expected: "json", short: "json" },
|
|
1650
1786
|
];
|
|
1651
1787
|
|
|
1652
|
-
testCases.forEach(({ input, expected }) => {
|
|
1788
|
+
testCases.forEach(({ input, expected, short }) => {
|
|
1653
1789
|
it(`should parse "${input}" to DataType with value "${expected}"`, () => {
|
|
1654
1790
|
const dt = DataType.z.parse(input);
|
|
1655
1791
|
expect(dt).toBeInstanceOf(DataType);
|
|
1656
1792
|
expect(dt.toString()).toBe(expected);
|
|
1793
|
+
expect(dt.toString(true)).toBe(short);
|
|
1657
1794
|
});
|
|
1658
1795
|
});
|
|
1659
1796
|
});
|
package/src/telem/telem.ts
CHANGED
|
@@ -16,6 +16,9 @@ import { type bounds } from "@/spatial";
|
|
|
16
16
|
/** Time zone specification when working with time stamps. */
|
|
17
17
|
export type TZInfo = "UTC" | "local";
|
|
18
18
|
|
|
19
|
+
const SIMPLE_DAYS_IN_YEAR = 365;
|
|
20
|
+
const SIMPLE_DAYS_IN_MONTH = 30;
|
|
21
|
+
|
|
19
22
|
/** Different string formats for time stamps. */
|
|
20
23
|
export type TimeStampStringFormat =
|
|
21
24
|
| "ISO"
|
|
@@ -28,6 +31,9 @@ export type TimeStampStringFormat =
|
|
|
28
31
|
| "shortDate"
|
|
29
32
|
| "dateTime";
|
|
30
33
|
|
|
34
|
+
/** Different string formats for time spans. */
|
|
35
|
+
export type TimeSpanStringFormat = "full" | "semantic";
|
|
36
|
+
|
|
31
37
|
const dateComponentsZ = z.union([
|
|
32
38
|
z.tuple([z.int()]),
|
|
33
39
|
z.tuple([z.int(), z.int().min(1).max(12)]),
|
|
@@ -103,7 +109,9 @@ export class TimeStamp
|
|
|
103
109
|
if (value instanceof Number) value = value.valueOf();
|
|
104
110
|
if (tzInfo === "local") offset = TimeStamp.utcOffset.valueOf();
|
|
105
111
|
if (typeof value === "number")
|
|
106
|
-
if (isFinite(value))
|
|
112
|
+
if (isFinite(value))
|
|
113
|
+
if (value === math.MAX_INT64_NUMBER) value = math.MAX_INT64;
|
|
114
|
+
else value = Math.trunc(value);
|
|
107
115
|
else {
|
|
108
116
|
if (isNaN(value)) value = 0;
|
|
109
117
|
if (value === Infinity) value = TimeStamp.MAX;
|
|
@@ -156,34 +164,6 @@ export class TimeStamp
|
|
|
156
164
|
).valueOf();
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
/**
|
|
160
|
-
* Formats the TimeStamp as a string in the specified format.
|
|
161
|
-
*
|
|
162
|
-
* @param format - The format to use for the string representation.
|
|
163
|
-
* @param tzInfo - The timezone to use when creating the string representation.
|
|
164
|
-
* @returns A string representation of the TimeStamp in the specified format.
|
|
165
|
-
*/
|
|
166
|
-
fString(format: TimeStampStringFormat = "ISO", tzInfo: TZInfo = "UTC"): string {
|
|
167
|
-
switch (format) {
|
|
168
|
-
case "ISODate":
|
|
169
|
-
return this.toISOString(tzInfo).slice(0, 10);
|
|
170
|
-
case "ISOTime":
|
|
171
|
-
return this.toISOString(tzInfo).slice(11, 23);
|
|
172
|
-
case "time":
|
|
173
|
-
return this.timeString(false, tzInfo);
|
|
174
|
-
case "preciseTime":
|
|
175
|
-
return this.timeString(true, tzInfo);
|
|
176
|
-
case "date":
|
|
177
|
-
return this.dateString();
|
|
178
|
-
case "preciseDate":
|
|
179
|
-
return `${this.dateString()} ${this.timeString(true, tzInfo)}`;
|
|
180
|
-
case "dateTime":
|
|
181
|
-
return `${this.dateString()} ${this.timeString(false, tzInfo)}`;
|
|
182
|
-
default:
|
|
183
|
-
return this.toISOString(tzInfo);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
167
|
private toISOString(tzInfo: TZInfo = "UTC"): string {
|
|
188
168
|
if (tzInfo === "UTC") return this.date().toISOString();
|
|
189
169
|
return this.sub(TimeStamp.utcOffset).date().toISOString();
|
|
@@ -213,7 +193,7 @@ export class TimeStamp
|
|
|
213
193
|
* the other timestamp.
|
|
214
194
|
* @param other - The other timestamp.
|
|
215
195
|
*/
|
|
216
|
-
static since(other:
|
|
196
|
+
static since(other: CrudeTimeStamp): TimeSpan {
|
|
217
197
|
return new TimeStamp().span(other);
|
|
218
198
|
}
|
|
219
199
|
|
|
@@ -505,8 +485,8 @@ export class TimeStamp
|
|
|
505
485
|
}
|
|
506
486
|
|
|
507
487
|
/**
|
|
508
|
-
* @
|
|
509
|
-
*
|
|
488
|
+
* @returns the integer millisecond that the timestamp corresponds to within its
|
|
489
|
+
* second.
|
|
510
490
|
*/
|
|
511
491
|
get millisecond(): number {
|
|
512
492
|
return this.date().getUTCMilliseconds();
|
|
@@ -522,9 +502,32 @@ export class TimeStamp
|
|
|
522
502
|
return new TimeStamp(d);
|
|
523
503
|
}
|
|
524
504
|
|
|
525
|
-
/**
|
|
526
|
-
|
|
527
|
-
|
|
505
|
+
/**
|
|
506
|
+
* Returns a string representation of the TimeStamp.
|
|
507
|
+
*
|
|
508
|
+
* @param format - Optional format for the string representation. Defaults to "ISO".
|
|
509
|
+
* @param tzInfo - Optional timezone info. Defaults to "UTC".
|
|
510
|
+
* @returns A string representation of the TimeStamp.
|
|
511
|
+
*/
|
|
512
|
+
toString(format: TimeStampStringFormat = "ISO", tzInfo: TZInfo = "UTC"): string {
|
|
513
|
+
switch (format) {
|
|
514
|
+
case "ISODate":
|
|
515
|
+
return this.toISOString(tzInfo).slice(0, 10);
|
|
516
|
+
case "ISOTime":
|
|
517
|
+
return this.toISOString(tzInfo).slice(11, 23);
|
|
518
|
+
case "time":
|
|
519
|
+
return this.timeString(false, tzInfo);
|
|
520
|
+
case "preciseTime":
|
|
521
|
+
return this.timeString(true, tzInfo);
|
|
522
|
+
case "date":
|
|
523
|
+
return this.dateString();
|
|
524
|
+
case "preciseDate":
|
|
525
|
+
return `${this.dateString()} ${this.timeString(true, tzInfo)}`;
|
|
526
|
+
case "dateTime":
|
|
527
|
+
return `${this.dateString()} ${this.timeString(false, tzInfo)}`;
|
|
528
|
+
default:
|
|
529
|
+
return this.toISOString(tzInfo);
|
|
530
|
+
}
|
|
528
531
|
}
|
|
529
532
|
|
|
530
533
|
/**
|
|
@@ -658,7 +661,7 @@ export class TimeStamp
|
|
|
658
661
|
static readonly DAY = TimeStamp.days(1);
|
|
659
662
|
|
|
660
663
|
/** The maximum possible value for a timestamp */
|
|
661
|
-
static readonly MAX = new TimeStamp(
|
|
664
|
+
static readonly MAX = new TimeStamp(math.MAX_INT64);
|
|
662
665
|
|
|
663
666
|
/** The minimum possible value for a timestamp */
|
|
664
667
|
static readonly MIN = new TimeStamp(0);
|
|
@@ -805,9 +808,15 @@ export class TimeSpan
|
|
|
805
808
|
/**
|
|
806
809
|
* Returns a string representation of the TimeSpan.
|
|
807
810
|
*
|
|
811
|
+
* @param format - Optional format for the string representation. Defaults to "full".
|
|
812
|
+
* - "full": Shows all non-zero units with full precision (e.g., "2d 3h 45m 12s 500ms")
|
|
813
|
+
* - "semantic": Shows 1-2 most significant units (e.g., "2d 3h")
|
|
808
814
|
* @returns A string representation of the TimeSpan.
|
|
809
815
|
*/
|
|
810
|
-
toString(): string {
|
|
816
|
+
toString(format: TimeSpanStringFormat = "full"): string {
|
|
817
|
+
if (format === "semantic") return this.toSemanticString();
|
|
818
|
+
|
|
819
|
+
// Default "full" format
|
|
811
820
|
const totalDays = this.truncate(TimeSpan.DAY);
|
|
812
821
|
const totalHours = this.truncate(TimeSpan.HOUR);
|
|
813
822
|
const totalMinutes = this.truncate(TimeSpan.MINUTE);
|
|
@@ -834,6 +843,107 @@ export class TimeSpan
|
|
|
834
843
|
return str.trim();
|
|
835
844
|
}
|
|
836
845
|
|
|
846
|
+
private toSemanticString(): string {
|
|
847
|
+
const absValue = this.valueOf() < 0n ? -this.valueOf() : this.valueOf();
|
|
848
|
+
const span = new TimeSpan(absValue);
|
|
849
|
+
const isNegative = this.valueOf() < 0n;
|
|
850
|
+
|
|
851
|
+
if (span.valueOf() === 0n) return "0s";
|
|
852
|
+
|
|
853
|
+
if (span.lessThan(TimeSpan.SECOND)) return "< 1s";
|
|
854
|
+
|
|
855
|
+
const totalDays = span.days;
|
|
856
|
+
const totalHours = span.hours;
|
|
857
|
+
const totalMinutes = span.minutes;
|
|
858
|
+
const totalSeconds = span.seconds;
|
|
859
|
+
|
|
860
|
+
const years = Math.floor(totalDays / SIMPLE_DAYS_IN_YEAR);
|
|
861
|
+
const months = Math.floor(totalDays / SIMPLE_DAYS_IN_MONTH);
|
|
862
|
+
const weeks = Math.floor(totalDays / 7);
|
|
863
|
+
const days = Math.floor(totalDays);
|
|
864
|
+
const hours = Math.floor(totalHours);
|
|
865
|
+
const minutes = Math.floor(totalMinutes);
|
|
866
|
+
const seconds = Math.floor(totalSeconds);
|
|
867
|
+
|
|
868
|
+
const prefix = isNegative ? "-" : "";
|
|
869
|
+
|
|
870
|
+
if (years >= 1) {
|
|
871
|
+
let result = `${years}y`;
|
|
872
|
+
if (years < 2) {
|
|
873
|
+
const remainingMonths = Math.floor(
|
|
874
|
+
(totalDays % SIMPLE_DAYS_IN_YEAR) / SIMPLE_DAYS_IN_MONTH,
|
|
875
|
+
);
|
|
876
|
+
if (remainingMonths > 0) result += ` ${remainingMonths}mo`;
|
|
877
|
+
}
|
|
878
|
+
return prefix + result;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// For durations less than 1 month (30 days), prefer weeks if it's exactly divisible
|
|
882
|
+
if (weeks >= 1 && totalDays < SIMPLE_DAYS_IN_MONTH && totalDays % 7 === 0) {
|
|
883
|
+
let result = `${weeks}w`;
|
|
884
|
+
const remainingDays = Math.floor(totalDays % 7);
|
|
885
|
+
const remainingHoursAfterWeeks = Math.floor(totalHours - weeks * 7 * 24);
|
|
886
|
+
|
|
887
|
+
if (weeks < 2)
|
|
888
|
+
if (remainingDays > 0) result += ` ${remainingDays}d`;
|
|
889
|
+
else if (remainingHoursAfterWeeks > 0 && remainingHoursAfterWeeks < 24)
|
|
890
|
+
// Only hours remaining after full weeks (e.g., "1w 1h")
|
|
891
|
+
result += ` ${remainingHoursAfterWeeks}h`;
|
|
892
|
+
|
|
893
|
+
return prefix + result;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
if (months >= 1) {
|
|
897
|
+
let result = `${months}mo`;
|
|
898
|
+
if (months < 3) {
|
|
899
|
+
const remainingDays = Math.floor(totalDays % SIMPLE_DAYS_IN_MONTH);
|
|
900
|
+
if (remainingDays > 0) result += ` ${remainingDays}d`;
|
|
901
|
+
}
|
|
902
|
+
return prefix + result;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (weeks >= 1) {
|
|
906
|
+
let result = `${weeks}w`;
|
|
907
|
+
const remainingDays = Math.floor(totalDays % 7);
|
|
908
|
+
const remainingHoursAfterWeeks = Math.floor(totalHours - weeks * 7 * 24);
|
|
909
|
+
|
|
910
|
+
if (weeks < 2)
|
|
911
|
+
if (remainingDays > 0) result += ` ${remainingDays}d`;
|
|
912
|
+
else if (remainingHoursAfterWeeks > 0 && remainingHoursAfterWeeks < 24)
|
|
913
|
+
// Only hours remaining after full weeks (e.g., "1w 1h")
|
|
914
|
+
result += ` ${remainingHoursAfterWeeks}h`;
|
|
915
|
+
|
|
916
|
+
return prefix + result;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (days >= 1) {
|
|
920
|
+
let result = `${days}d`;
|
|
921
|
+
const remainingHours = Math.floor(totalHours - days * 24);
|
|
922
|
+
if (days < 2 && remainingHours > 0) result += ` ${remainingHours}h`;
|
|
923
|
+
return prefix + result;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (hours >= 1) {
|
|
927
|
+
let result = `${hours}h`;
|
|
928
|
+
if (hours < 3) {
|
|
929
|
+
const remainingMinutes = Math.floor(totalMinutes - hours * 60);
|
|
930
|
+
if (remainingMinutes > 0) result += ` ${remainingMinutes}m`;
|
|
931
|
+
}
|
|
932
|
+
return prefix + result;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (minutes >= 1) {
|
|
936
|
+
let result = `${minutes}m`;
|
|
937
|
+
if (minutes < 5) {
|
|
938
|
+
const remainingSeconds = Math.floor(totalSeconds - minutes * 60);
|
|
939
|
+
if (remainingSeconds > 0) result += ` ${remainingSeconds}s`;
|
|
940
|
+
}
|
|
941
|
+
return prefix + result;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
return `${prefix}${seconds}s`;
|
|
945
|
+
}
|
|
946
|
+
|
|
837
947
|
/**
|
|
838
948
|
* Multiplies the TimeSpan by a scalar value.
|
|
839
949
|
*
|
|
@@ -1017,7 +1127,7 @@ export class TimeSpan
|
|
|
1017
1127
|
static readonly DAY = TimeSpan.days(1);
|
|
1018
1128
|
|
|
1019
1129
|
/** The maximum possible value for a TimeSpan. */
|
|
1020
|
-
static readonly MAX = new TimeSpan(
|
|
1130
|
+
static readonly MAX = new TimeSpan(math.MAX_INT64);
|
|
1021
1131
|
|
|
1022
1132
|
/** The minimum possible value for a TimeSpan. */
|
|
1023
1133
|
static readonly MIN = new TimeSpan(0);
|
|
@@ -1025,7 +1135,7 @@ export class TimeSpan
|
|
|
1025
1135
|
/** The zero value for a TimeSpan. */
|
|
1026
1136
|
static readonly ZERO = new TimeSpan(0);
|
|
1027
1137
|
|
|
1028
|
-
/** A zod schema for validating and transforming
|
|
1138
|
+
/** A zod schema for validating and transforming time spans */
|
|
1029
1139
|
static readonly z = z.union([
|
|
1030
1140
|
z.object({ value: z.bigint() }).transform((v) => new TimeSpan(v.value)),
|
|
1031
1141
|
z.string().transform((n) => new TimeSpan(BigInt(n))),
|
|
@@ -1344,12 +1454,12 @@ export class TimeRange implements primitive.Stringer {
|
|
|
1344
1454
|
}
|
|
1345
1455
|
|
|
1346
1456
|
/**
|
|
1347
|
-
* Checks if the TimeRange
|
|
1457
|
+
* Checks if the TimeRange is zero (both start and end are TimeStamp.ZERO).
|
|
1348
1458
|
*
|
|
1349
|
-
* @returns True if
|
|
1459
|
+
* @returns True if both start and end are TimeStamp.ZERO, false otherwise.
|
|
1350
1460
|
*/
|
|
1351
1461
|
get isZero(): boolean {
|
|
1352
|
-
return this.
|
|
1462
|
+
return this.start.isZero && this.end.isZero;
|
|
1353
1463
|
}
|
|
1354
1464
|
|
|
1355
1465
|
/**
|
|
@@ -1406,7 +1516,7 @@ export class TimeRange implements primitive.Stringer {
|
|
|
1406
1516
|
* @returns A pretty string representation of the TimeRange.
|
|
1407
1517
|
*/
|
|
1408
1518
|
toPrettyString(): string {
|
|
1409
|
-
return `${this.start.
|
|
1519
|
+
return `${this.start.toString("preciseDate")} - ${this.span.toString()}`;
|
|
1410
1520
|
}
|
|
1411
1521
|
|
|
1412
1522
|
/**
|
|
@@ -1495,9 +1605,6 @@ export class TimeRange implements primitive.Stringer {
|
|
|
1495
1605
|
/** The maximum possible time range. */
|
|
1496
1606
|
static readonly MAX = new TimeRange(TimeStamp.MIN, TimeStamp.MAX);
|
|
1497
1607
|
|
|
1498
|
-
/** The minimum possible time range. */
|
|
1499
|
-
static readonly MIN = new TimeRange(TimeStamp.MAX, TimeStamp.MIN);
|
|
1500
|
-
|
|
1501
1608
|
/** A time range whose start and end are both zero. */
|
|
1502
1609
|
static readonly ZERO = new TimeRange(TimeStamp.ZERO, TimeStamp.ZERO);
|
|
1503
1610
|
|
|
@@ -1536,7 +1643,7 @@ export class TimeRange implements primitive.Stringer {
|
|
|
1536
1643
|
.map((r) => r.makeValid())
|
|
1537
1644
|
.sort((a, b) => TimeRange.sort(a, b))
|
|
1538
1645
|
.reduce<TimeRange[]>((simplified, range) => {
|
|
1539
|
-
if (range.isZero) return simplified;
|
|
1646
|
+
if (range.span.isZero) return simplified;
|
|
1540
1647
|
if (simplified.length === 0) {
|
|
1541
1648
|
simplified.push(range);
|
|
1542
1649
|
return simplified;
|
|
@@ -1595,8 +1702,10 @@ export class DataType
|
|
|
1595
1702
|
return others.some((o) => this.equals(o));
|
|
1596
1703
|
}
|
|
1597
1704
|
|
|
1598
|
-
/** @returns a string representation of the DataType.
|
|
1599
|
-
|
|
1705
|
+
/** @returns a string representation of the DataType. If short is true, a 1-4
|
|
1706
|
+
* character representation (i64, str, etc.) is returned instead. */
|
|
1707
|
+
toString(short: boolean = false): string {
|
|
1708
|
+
if (short) return DataType.SHORT_STRINGS.get(this.valueOf()) ?? this.valueOf();
|
|
1600
1709
|
return this.valueOf();
|
|
1601
1710
|
}
|
|
1602
1711
|
|
|
@@ -1739,11 +1848,11 @@ export class DataType
|
|
|
1739
1848
|
static readonly UINT16 = new DataType("uint16");
|
|
1740
1849
|
/** Represents a 8-bit unsigned integer value. */
|
|
1741
1850
|
static readonly UINT8 = new DataType("uint8");
|
|
1742
|
-
/** Represents a boolean value.
|
|
1743
|
-
static readonly BOOLEAN =
|
|
1851
|
+
/** Represents a boolean value. Stored as a 8-bit unsigned integer. */
|
|
1852
|
+
static readonly BOOLEAN = new DataType("boolean");
|
|
1744
1853
|
/** Represents a 64-bit unix epoch. */
|
|
1745
1854
|
static readonly TIMESTAMP = new DataType("timestamp");
|
|
1746
|
-
/** Represents a UUID data type */
|
|
1855
|
+
/** Represents a UUID data type. */
|
|
1747
1856
|
static readonly UUID = new DataType("uuid");
|
|
1748
1857
|
/** Represents a string data type. Strings have an unknown density, and are separate
|
|
1749
1858
|
* by a newline character. */
|
|
@@ -1822,6 +1931,24 @@ export class DataType
|
|
|
1822
1931
|
DataType.JSON,
|
|
1823
1932
|
];
|
|
1824
1933
|
|
|
1934
|
+
private static readonly SHORT_STRINGS = new Map<string, string>([
|
|
1935
|
+
[DataType.UINT8.toString(), "u8"],
|
|
1936
|
+
[DataType.UINT16.toString(), "u16"],
|
|
1937
|
+
[DataType.UINT32.toString(), "u32"],
|
|
1938
|
+
[DataType.UINT64.toString(), "u64"],
|
|
1939
|
+
[DataType.INT8.toString(), "i8"],
|
|
1940
|
+
[DataType.INT16.toString(), "i16"],
|
|
1941
|
+
[DataType.INT32.toString(), "i32"],
|
|
1942
|
+
[DataType.INT64.toString(), "i64"],
|
|
1943
|
+
[DataType.FLOAT32.toString(), "f32"],
|
|
1944
|
+
[DataType.FLOAT64.toString(), "f64"],
|
|
1945
|
+
[DataType.BOOLEAN.toString(), "bool"],
|
|
1946
|
+
[DataType.TIMESTAMP.toString(), "ts"],
|
|
1947
|
+
[DataType.UUID.toString(), "uuid"],
|
|
1948
|
+
[DataType.STRING.toString(), "str"],
|
|
1949
|
+
[DataType.JSON.toString(), "json"],
|
|
1950
|
+
]);
|
|
1951
|
+
|
|
1825
1952
|
static readonly BIG_INT_TYPES = [DataType.INT64, DataType.UINT64, DataType.TIMESTAMP];
|
|
1826
1953
|
|
|
1827
1954
|
/** A zod schema for a DataType. */
|
|
@@ -2055,14 +2182,16 @@ export interface CrudeTimeRange {
|
|
|
2055
2182
|
end: CrudeTimeStamp;
|
|
2056
2183
|
}
|
|
2057
2184
|
|
|
2185
|
+
export const numericTimeRangeZ = z.object({
|
|
2186
|
+
start: z.number(),
|
|
2187
|
+
end: z.number(),
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2058
2190
|
/**
|
|
2059
2191
|
* A time range backed by numbers instead of TimeStamps/BigInts.
|
|
2060
2192
|
* Involves a loss of precision, but can be useful for serialization.
|
|
2061
2193
|
*/
|
|
2062
|
-
export interface NumericTimeRange {
|
|
2063
|
-
start: number;
|
|
2064
|
-
end: number;
|
|
2065
|
-
}
|
|
2194
|
+
export interface NumericTimeRange extends z.infer<typeof numericTimeRangeZ> {}
|
|
2066
2195
|
|
|
2067
2196
|
export const typedArrayZ = z.union([
|
|
2068
2197
|
z.instanceof(Uint8Array),
|
|
@@ -18,7 +18,7 @@ describe("testutil", () => {
|
|
|
18
18
|
expect(testutil.toString(123)).toBe("123");
|
|
19
19
|
expect(testutil.toString(true)).toBe("true");
|
|
20
20
|
expect(testutil.toString(null)).toBe("null");
|
|
21
|
-
expect(testutil.toString(undefined)).
|
|
21
|
+
expect(testutil.toString(undefined)).toBeUndefined();
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it("should handle arrays", () => {
|
|
@@ -19,10 +19,10 @@ describe("zod", () => {
|
|
|
19
19
|
expect(schema.parse("string")).toBe("string");
|
|
20
20
|
});
|
|
21
21
|
it("should parse null as undefined", () => {
|
|
22
|
-
expect(schema.parse(null)).
|
|
22
|
+
expect(schema.parse(null)).toBeUndefined();
|
|
23
23
|
});
|
|
24
24
|
it("should parse undefined as undefined", () => {
|
|
25
|
-
expect(schema.parse(undefined)).
|
|
25
|
+
expect(schema.parse(undefined)).toBeUndefined();
|
|
26
26
|
});
|
|
27
27
|
it("should throw for other values", () => {
|
|
28
28
|
expect(() => schema.parse(1)).toThrow(z.ZodError);
|
package/src/zod/toArray.spec.ts
CHANGED
|
@@ -162,8 +162,14 @@ describe("toArray", () => {
|
|
|
162
162
|
describe("edge cases", () => {
|
|
163
163
|
it("should handle deeply nested arrays", () => {
|
|
164
164
|
const schema = toArray(toArray(z.number()));
|
|
165
|
-
const result = schema.parse([
|
|
166
|
-
|
|
165
|
+
const result = schema.parse([
|
|
166
|
+
[1, 2],
|
|
167
|
+
[3, 4],
|
|
168
|
+
]);
|
|
169
|
+
expect(result).toEqual([
|
|
170
|
+
[1, 2],
|
|
171
|
+
[3, 4],
|
|
172
|
+
]);
|
|
167
173
|
});
|
|
168
174
|
|
|
169
175
|
it("should convert single value to nested array", () => {
|
|
@@ -179,4 +185,4 @@ describe("toArray", () => {
|
|
|
179
185
|
expect(result).toEqual(input);
|
|
180
186
|
});
|
|
181
187
|
});
|
|
182
|
-
});
|
|
188
|
+
});
|