@fable-org/fable-library-js 2.6.0 → 2.7.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.
@@ -0,0 +1,142 @@
1
+ import { toInt64, fromFloat64 } from "./BigInt.js";
2
+ import { fromTicks as TimeSpan_fromTicks, totalNanoseconds as TimeSpan_totalNanoseconds } from "./TimeSpanTemporal.js";
3
+ import { Exception } from "./Util.js";
4
+ export const PlainTime = Temporal.PlainTime;
5
+ // The generic equality/comparison/hashing helpers in Util.ts dispatch at runtime
6
+ // on .NET-style Equals/CompareTo/GetHashCode methods. Attach them so TimeOnly
7
+ // also works in generic contexts: records, tuples, erased generics, etc.
8
+ const proto = Temporal.PlainTime.prototype;
9
+ proto.Equals = function (other) { return this.equals(other); };
10
+ proto.CompareTo = function (other) { return Temporal.PlainTime.compare(this, other); };
11
+ proto.GetHashCode = function () { return hash(this); };
12
+ // Lets String.Format format this value without String.ts importing this module (see String.ts)
13
+ proto[Symbol.for("Fable.DateTimeFormattable")] = function (format) { return toString(this, format); };
14
+ const nanosecondsPerDay = 86400000000000;
15
+ const nanosecondsPerDayBig = 86400000000000n;
16
+ function totalNanoseconds(t) {
17
+ return ((t.hour * 60 + t.minute) * 60 + t.second) * 1000000000
18
+ + t.millisecond * 1000000 + t.microsecond * 1000 + t.nanosecond;
19
+ }
20
+ function fromNanoseconds(n) {
21
+ return new Temporal.PlainTime(Math.floor(n / 3600000000000), Math.floor(n / 60000000000) % 60, Math.floor(n / 1000000000) % 60, Math.floor(n / 1000000) % 1000, Math.floor(n / 1000) % 1000, n % 1000);
22
+ }
23
+ export function create(h = 0, m = 0, s = 0, ms = 0, us = 0) {
24
+ return new Temporal.PlainTime(h, m, s, ms, us);
25
+ }
26
+ export function fromTicks(ticks) {
27
+ return fromNanoseconds(Number(BigInt(ticks) * 100n));
28
+ }
29
+ export function fromTimeSpan(timeSpan) {
30
+ const ns = Number(TimeSpan_totalNanoseconds(timeSpan));
31
+ if (ns < 0 || ns >= nanosecondsPerDay)
32
+ throw new Exception("The TimeSpan describes an unrepresentable TimeOnly.");
33
+ return fromNanoseconds(ns);
34
+ }
35
+ export function fromDateTime(d) {
36
+ // Under the Temporal representation a DateTime is a PlainDateTime (kind-agnostic wall-clock)
37
+ return new Temporal.PlainTime(d.hour, d.minute, d.second, d.millisecond, d.microsecond, d.nanosecond);
38
+ }
39
+ export function minValue() {
40
+ return new Temporal.PlainTime();
41
+ }
42
+ export function maxValue() {
43
+ // This is "23:59:59.9999999" (.NET tick precision)
44
+ return new Temporal.PlainTime(23, 59, 59, 999, 999, 900);
45
+ }
46
+ export function ticks(t) {
47
+ return toInt64(fromFloat64(totalNanoseconds(t) / 100));
48
+ }
49
+ export function toTimeSpan(t) {
50
+ // TimeOnly is tick-precise, so the nanosecond count is always a multiple of 100.
51
+ return TimeSpan_fromTicks(Math.round(totalNanoseconds(t) / 100));
52
+ }
53
+ // Kept in bigint: a TimeOnly itself always fits a float64 exactly (under 2^47 ns),
54
+ // but the amount added does not — a TimeSpan of more than ~104 days already
55
+ // exceeds Number.MAX_SAFE_INTEGER in nanoseconds, and .NET wraps it modulo a day
56
+ // rather than saturating, so the low ticks are the part that must survive.
57
+ function addNanoseconds(t, deltaNs, wrappedDays) {
58
+ const totalNs = BigInt(totalNanoseconds(t)) + deltaNs;
59
+ let days = totalNs / nanosecondsPerDayBig;
60
+ let remainder = totalNs % nanosecondsPerDayBig;
61
+ // BigInt division truncates toward zero; .NET wraps toward the previous day.
62
+ if (remainder < 0n) {
63
+ remainder += nanosecondsPerDayBig;
64
+ days -= 1n;
65
+ }
66
+ if (wrappedDays !== undefined) {
67
+ wrappedDays.contents = Number(days);
68
+ }
69
+ return fromNanoseconds(Number(remainder));
70
+ }
71
+ export function add(t, ts, wrappedDays) {
72
+ return addNanoseconds(t, TimeSpan_totalNanoseconds(ts), wrappedDays);
73
+ }
74
+ export function addHours(t, h) {
75
+ // Scale to ticks first, as .NET does, then widen — `h` hours in nanoseconds
76
+ // overflows the exact float64 range two orders of magnitude sooner.
77
+ return addNanoseconds(t, BigInt(Math.round(h * 36000000000)) * 100n);
78
+ }
79
+ export function addMinutes(t, m) {
80
+ return addNanoseconds(t, BigInt(Math.round(m * 600000000)) * 100n);
81
+ }
82
+ export function isBetween(t, start, end) {
83
+ return Temporal.PlainTime.compare(start, end) <= 0
84
+ ? (Temporal.PlainTime.compare(start, t) <= 0 && Temporal.PlainTime.compare(end, t) > 0)
85
+ : (Temporal.PlainTime.compare(start, t) <= 0 || Temporal.PlainTime.compare(end, t) > 0);
86
+ }
87
+ export function equals(x, y) {
88
+ return x.equals(y);
89
+ }
90
+ export function compare(x, y) {
91
+ return Temporal.PlainTime.compare(x, y);
92
+ }
93
+ export function hash(t) {
94
+ return totalNanoseconds(t) % 2147483647;
95
+ }
96
+ export function op_Subtraction(left, right) {
97
+ // Returns the elapsed TimeSpan, wrapping around midnight
98
+ const ns = (totalNanoseconds(left) - totalNanoseconds(right) + nanosecondsPerDay) % nanosecondsPerDay;
99
+ return TimeSpan_fromTicks(Math.round(ns / 100));
100
+ }
101
+ export function toString(t, format = "t", _provider) {
102
+ switch (format) {
103
+ case "t":
104
+ return t.toString({ smallestUnit: "minute" });
105
+ case "r":
106
+ case "R":
107
+ case "T":
108
+ return t.toString({ smallestUnit: "second" });
109
+ case "o":
110
+ case "O":
111
+ // .NET tick precision is 7 fractional digits
112
+ return t.toString({ fractionalSecondDigits: 7 });
113
+ default:
114
+ throw new Exception("Custom formats are not supported");
115
+ }
116
+ }
117
+ export function parse(str) {
118
+ // Allowed format types:
119
+ // hh:mm
120
+ // hh:mm:ss
121
+ // hh:mm:ss.fffffff
122
+ const r = /^\s*([0-1]?\d|2[0-3])\s*:\s*([0-5]?\d)(\s*:\s*([0-5]?\d)(\.(\d+))?)?\s*$/.exec(str);
123
+ if (r != null && r[1] != null && r[2] != null) {
124
+ const h = +r[1];
125
+ const m = +r[2];
126
+ const s = r[4] != null ? +r[4] : 0;
127
+ // .NET resolves the fraction down to a single tick (7 digits) and truncates
128
+ // anything finer, so the digits are read as ticks rather than milliseconds.
129
+ const subSecondTicks = r[6] != null ? +r[6].padEnd(7, "0").substring(0, 7) : 0;
130
+ return fromNanoseconds(((h * 60 + m) * 60 + s) * 1000000000 + subSecondTicks * 100);
131
+ }
132
+ throw new Exception(`String '${str}' was not recognized as a valid TimeOnly.`);
133
+ }
134
+ export function tryParse(v, defValue) {
135
+ try {
136
+ defValue.contents = parse(v);
137
+ return true;
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }
@@ -0,0 +1,259 @@
1
+ import { toInt64 } from "./BigInt.js";
2
+ import { Exception, padWithZeros } from "./Util.js";
3
+ export const Duration = Temporal.Duration;
4
+ // The generic equality/comparison/hashing helpers in Util.ts dispatch at runtime
5
+ // on .NET-style Equals/CompareTo/GetHashCode methods. Attach them so TimeSpan
6
+ // also works in generic contexts: records, tuples, erased generics, etc.
7
+ const proto = Temporal.Duration.prototype;
8
+ proto.Equals = function (other) { return Temporal.Duration.compare(this, other) === 0; };
9
+ proto.CompareTo = function (other) { return Temporal.Duration.compare(this, other); };
10
+ proto.GetHashCode = function () { return hash(this); };
11
+ // Lets String.Format format this value without String.ts importing this module (see String.ts)
12
+ proto[Symbol.for("Fable.DateTimeFormattable")] = function (format) { return toString(this, format); };
13
+ const nsPerMillisecond = 1000000n;
14
+ const nsPerSecond = 1000000000n;
15
+ const nsPerMinute = 60000000000n;
16
+ const nsPerHour = 3600000000000n;
17
+ const nsPerDay = 86400000000000n;
18
+ export function totalNanoseconds(ts) {
19
+ return BigInt(ts.days) * nsPerDay
20
+ + BigInt(ts.hours) * nsPerHour
21
+ + BigInt(ts.minutes) * nsPerMinute
22
+ + BigInt(ts.seconds) * nsPerSecond
23
+ + BigInt(ts.milliseconds) * nsPerMillisecond
24
+ + BigInt(ts.microseconds) * 1000n
25
+ + BigInt(ts.nanoseconds);
26
+ }
27
+ function fromNanoseconds(totalNs) {
28
+ const negative = totalNs < 0n;
29
+ // A .NET TimeSpan is a whole number of 100ns ticks, so anything finer than a
30
+ // tick is dropped (toward zero, as .NET does) rather than carried in the
31
+ // Duration's nanosecond field — otherwise values that cannot exist in .NET
32
+ // would leak out of Parse/FromMilliseconds/arithmetic.
33
+ let n = (negative ? -totalNs : totalNs) / 100n * 100n;
34
+ const days = Number(n / nsPerDay);
35
+ n %= nsPerDay;
36
+ const hours = Number(n / nsPerHour);
37
+ n %= nsPerHour;
38
+ const minutes = Number(n / nsPerMinute);
39
+ n %= nsPerMinute;
40
+ const seconds = Number(n / nsPerSecond);
41
+ n %= nsPerSecond;
42
+ const milliseconds = Number(n / nsPerMillisecond);
43
+ n %= nsPerMillisecond;
44
+ const microseconds = Number(n / 1000n);
45
+ n %= 1000n;
46
+ const d = new Temporal.Duration(0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, Number(n));
47
+ return negative ? d.negated() : d;
48
+ }
49
+ // Converts a possibly fractional unit count into an exact nanosecond amount,
50
+ // keeping the integer part exact for the full Int64 tick range.
51
+ function unitToNanoseconds(value, nsPerUnit) {
52
+ const whole = Math.trunc(value);
53
+ const frac = value - whole;
54
+ return BigInt(whole) * nsPerUnit + BigInt(Math.round(frac * Number(nsPerUnit)));
55
+ }
56
+ export function create(d = 0, h = 0, m = 0, s = 0, ms = 0, us = 0) {
57
+ switch (arguments.length) {
58
+ case 1:
59
+ // ticks
60
+ return fromTicks(arguments[0]);
61
+ case 3:
62
+ // h,m,s
63
+ d = 0, h = arguments[0], m = arguments[1], s = arguments[2], ms = 0;
64
+ break;
65
+ default:
66
+ // d,h,m,s,ms,us
67
+ break;
68
+ }
69
+ return fromNanoseconds(unitToNanoseconds(d, nsPerDay) + unitToNanoseconds(h, nsPerHour) + unitToNanoseconds(m, nsPerMinute)
70
+ + unitToNanoseconds(s, nsPerSecond) + unitToNanoseconds(ms, nsPerMillisecond) + unitToNanoseconds(us, 1000n));
71
+ }
72
+ export function fromTicks(ticks) {
73
+ return fromNanoseconds(BigInt(ticks) * 100n);
74
+ }
75
+ export function zero() {
76
+ return new Temporal.Duration();
77
+ }
78
+ const ticksPerUnit = {
79
+ days: 864000000000,
80
+ hours: 36000000000,
81
+ minutes: 600000000,
82
+ seconds: 10000000,
83
+ milliseconds: 10000,
84
+ };
85
+ // The DateTime/DateTimeOffset/TimeOnly Add* members take a double and are
86
+ // tick-precise in .NET: the value is scaled to ticks and rounded, so
87
+ // AddSeconds(0.0000001) moves exactly one tick and AddDays(1.5) exactly 36 hours.
88
+ // Temporal's own `add({ days: 1.5 })` rejects non-integers, so the scaling has to
89
+ // happen here.
90
+ export function fromUnits(value, unit) {
91
+ return fromTicks(BigInt(Math.round(value * ticksPerUnit[unit])));
92
+ }
93
+ export function fromDays(d, h = 0, m = 0n, s = 0n, ms = 0n) {
94
+ return create(d, h, Number(m), Number(s), Number(ms));
95
+ }
96
+ export function fromHours(h, m = 0n, s = 0n, ms = 0n) {
97
+ return create(0, h, Number(m), Number(s), Number(ms));
98
+ }
99
+ export function fromMinutes(m, s = 0n, ms = 0n) {
100
+ return create(0, 0, Number(m), Number(s), Number(ms));
101
+ }
102
+ export function fromSeconds(s, ms = 0n) {
103
+ return create(0, 0, 0, Number(s), Number(ms));
104
+ }
105
+ export function fromMilliseconds(ms) {
106
+ return fromNanoseconds(unitToNanoseconds(Number(ms), nsPerMillisecond));
107
+ }
108
+ export function ticks(ts) {
109
+ return toInt64(totalNanoseconds(ts) / 100n);
110
+ }
111
+ // .NET computes each total as (double)Ticks / TicksPerUnit; match that exactly
112
+ // (Temporal's own .total() balances differently and can be 1 ULP off).
113
+ function totalTicks(ts) {
114
+ return Number(totalNanoseconds(ts) / 100n);
115
+ }
116
+ export function totalDays(ts) {
117
+ return totalTicks(ts) / 864000000000;
118
+ }
119
+ export function totalHours(ts) {
120
+ return totalTicks(ts) / 36000000000;
121
+ }
122
+ export function totalMinutes(ts) {
123
+ return totalTicks(ts) / 600000000;
124
+ }
125
+ export function totalSeconds(ts) {
126
+ return totalTicks(ts) / 10000000;
127
+ }
128
+ export function totalMilliseconds(ts) {
129
+ return totalTicks(ts) / 10000;
130
+ }
131
+ export function negate(ts) {
132
+ return ts.negated();
133
+ }
134
+ export function add(ts1, ts2) {
135
+ return fromNanoseconds(totalNanoseconds(ts1) + totalNanoseconds(ts2));
136
+ }
137
+ export function subtract(ts1, ts2) {
138
+ return fromNanoseconds(totalNanoseconds(ts1) - totalNanoseconds(ts2));
139
+ }
140
+ // .NET scales the tick count as a double and rounds: `new TimeSpan((long)Math
141
+ // .Round(Ticks * factor))`. Scaling ticks rather than nanoseconds keeps the
142
+ // magnitude — and so the rounding — identical to .NET's.
143
+ export function multiply(ts, factor) {
144
+ return fromTicks(BigInt(Math.round(Number(ticks(ts)) * factor)));
145
+ }
146
+ export function divide(ts, factor) {
147
+ return fromTicks(BigInt(Math.round(Number(ticks(ts)) / factor)));
148
+ }
149
+ export function divideByTimeSpan(ts, other) {
150
+ return Number(ticks(ts)) / Number(ticks(other));
151
+ }
152
+ export const op_Addition = add;
153
+ export const op_Subtraction = subtract;
154
+ export const op_Multiply = multiply;
155
+ export const op_UnaryNegation = negate;
156
+ export function compare(x, y) {
157
+ return Temporal.Duration.compare(x, y);
158
+ }
159
+ export const compareTo = compare;
160
+ export function equals(x, y) {
161
+ return Temporal.Duration.compare(x, y) === 0;
162
+ }
163
+ export function hash(ts) {
164
+ return Number(totalNanoseconds(ts) % 2147483647n);
165
+ }
166
+ export function duration(ts) {
167
+ return ts.abs();
168
+ }
169
+ export function toString(ts, format = "c", _provider) {
170
+ if (["c", "g", "G"].indexOf(format) === -1) {
171
+ throw new Exception("Custom formats are not supported");
172
+ }
173
+ const d = Math.abs(ts.days);
174
+ const h = Math.abs(ts.hours);
175
+ const m = Math.abs(ts.minutes);
176
+ const s = Math.abs(ts.seconds);
177
+ const ticks = Math.abs(ts.milliseconds) * 10000
178
+ + Math.abs(ts.microseconds) * 10
179
+ + Math.trunc(Math.abs(ts.nanoseconds) / 100);
180
+ const sign = ts.sign < 0 ? "-" : "";
181
+ const days = d === 0 && format !== "G" ? "" : format === "c" ? d + "." : d + ":";
182
+ const time = `${format === "g" ? h : padWithZeros(h, 2)}:${padWithZeros(m, 2)}:${padWithZeros(s, 2)}`;
183
+ // .NET prints seven tick digits for "c" and "G", trims trailing zeros for "g", and omits a zero fraction for "c" and "g".
184
+ const fraction = format === "g"
185
+ ? (ticks === 0 ? "" : "." + padWithZeros(ticks, 7).replace(/0+$/, ""))
186
+ : (ticks === 0 && format === "c" ? "" : "." + padWithZeros(ticks, 7));
187
+ return `${sign}${days}${time}${fraction}`;
188
+ }
189
+ export function parse(str) {
190
+ const firstDot = str.search("\\.");
191
+ const firstColon = str.search("\\:");
192
+ if (firstDot === -1 && firstColon === -1) { // There is only a day ex: 4
193
+ const d = parseInt(str, 0);
194
+ if (isNaN(d)) {
195
+ throw new Exception(`String '${str}' was not recognized as a valid TimeSpan.`);
196
+ }
197
+ else {
198
+ return create(d, 0, 0, 0, 0);
199
+ }
200
+ }
201
+ if (firstColon > 0) { // process time part
202
+ // WIP: (-?)(((\d+)\.)?([0-9]|0[0-9]|1[0-9]|2[0-3]):(\d+)(:\d+(\.\d{1,7})?)?|\d+(?:(?!\.)))
203
+ const r = /^(-?)((\d+)\.)?(?:0*)([0-9]|0[0-9]|1[0-9]|2[0-3]):(?:0*)([0-5][0-9]|[0-9])(:(?:0*)([0-5][0-9]|[0-9]))?\.?(\d+)?$/.exec(str);
204
+ if (r != null && r[4] != null && r[5] != null) {
205
+ let d = 0;
206
+ let ms = 0;
207
+ let s = 0;
208
+ const sign = r[1] != null && r[1] === "-" ? -1 : 1;
209
+ const h = +r[4];
210
+ const m = +r[5];
211
+ if (r[3] != null) {
212
+ d = +r[3];
213
+ }
214
+ if (r[7] != null) {
215
+ s = +r[7];
216
+ }
217
+ if (r[8] != null) {
218
+ // Depending on the number of decimals passed, we need to adapt the numbers
219
+ switch (r[8].length) {
220
+ case 1:
221
+ ms = +r[8] * 100;
222
+ break;
223
+ case 2:
224
+ ms = +r[8] * 10;
225
+ break;
226
+ case 3:
227
+ ms = +r[8];
228
+ break;
229
+ case 4:
230
+ ms = +r[8] / 10;
231
+ break;
232
+ case 5:
233
+ ms = +r[8] / 100;
234
+ break;
235
+ case 6:
236
+ ms = +r[8] / 1000;
237
+ break;
238
+ case 7:
239
+ ms = +r[8] / 10000;
240
+ break;
241
+ default:
242
+ throw new Exception(`String '${str}' was not recognized as a valid TimeSpan.`);
243
+ }
244
+ }
245
+ const ts = create(d, h, m, s, ms);
246
+ return sign < 0 ? negate(ts) : ts;
247
+ }
248
+ }
249
+ throw new Exception(`String '${str}' was not recognized as a valid TimeSpan.`);
250
+ }
251
+ export function tryParse(v, defValue) {
252
+ try {
253
+ defValue.contents = parse(v);
254
+ return true;
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "name": "@fable-org/fable-library-js",
6
- "version": "2.6.0",
6
+ "version": "2.7.0",
7
7
  "description": "Core library used by F# projects compiled with fable.io",
8
8
  "author": "Fable Contributors",
9
9
  "license": "MIT",