@cratis/fundamentals 7.22.4 → 7.22.6

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.
Files changed (52) hide show
  1. package/DateOnly.ts +28 -1
  2. package/TimeOnly.ts +51 -11
  3. package/TimeSpan.ts +26 -1
  4. package/dist/DateOnly.d.ts +1 -0
  5. package/dist/DateOnly.d.ts.map +1 -1
  6. package/dist/DateOnly.js +21 -1
  7. package/dist/DateOnly.js.map +1 -1
  8. package/dist/TimeOnly.d.ts +2 -1
  9. package/dist/TimeOnly.d.ts.map +1 -1
  10. package/dist/TimeOnly.js +30 -4
  11. package/dist/TimeOnly.js.map +1 -1
  12. package/dist/TimeSpan.d.ts.map +1 -1
  13. package/dist/TimeSpan.js +17 -0
  14. package/dist/TimeSpan.js.map +1 -1
  15. package/dist/cjs/DateOnly.d.ts +1 -0
  16. package/dist/cjs/DateOnly.d.ts.map +1 -1
  17. package/dist/cjs/DateOnly.js +21 -1
  18. package/dist/cjs/DateOnly.js.map +1 -1
  19. package/dist/cjs/TimeOnly.d.ts +2 -1
  20. package/dist/cjs/TimeOnly.d.ts.map +1 -1
  21. package/dist/cjs/TimeOnly.js +30 -4
  22. package/dist/cjs/TimeOnly.js.map +1 -1
  23. package/dist/cjs/TimeSpan.d.ts.map +1 -1
  24. package/dist/cjs/TimeSpan.js +17 -0
  25. package/dist/cjs/TimeSpan.js.map +1 -1
  26. package/dist/esm/DateOnly.d.ts +1 -0
  27. package/dist/esm/DateOnly.d.ts.map +1 -1
  28. package/dist/esm/DateOnly.js +21 -1
  29. package/dist/esm/DateOnly.js.map +1 -1
  30. package/dist/esm/TimeOnly.d.ts +2 -1
  31. package/dist/esm/TimeOnly.d.ts.map +1 -1
  32. package/dist/esm/TimeOnly.js +30 -4
  33. package/dist/esm/TimeOnly.js.map +1 -1
  34. package/dist/esm/TimeSpan.d.ts.map +1 -1
  35. package/dist/esm/TimeSpan.js +17 -0
  36. package/dist/esm/TimeSpan.js.map +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/for_DateOnly/when_parsing_an_out_of_range_calendar_date.ts +13 -0
  39. package/for_DateOnly/when_parsing_february_in_leap_and_common_years.ts +11 -0
  40. package/for_JsonSerializer/when_deserializing_a_value_map_with_out_of_range_temporal_keys.ts +19 -0
  41. package/for_JsonSerializer/when_deserializing_out_of_range_temporal_values.ts +20 -0
  42. package/for_JsonSerializer/when_round_tripping_a_value_map_with_time_only_keys_differing_below_a_millisecond.ts +20 -0
  43. package/for_TimeOnly/when_creating_with_out_of_range_sub_millisecond_ticks.ts +11 -0
  44. package/for_TimeOnly/when_parsing_an_out_of_range_time_of_day.ts +11 -0
  45. package/for_TimeOnly/when_parsing_the_last_moment_of_the_day.ts +10 -0
  46. package/for_TimeOnly/when_parsing_the_shapes_the_server_sends.ts +6 -6
  47. package/for_TimeOnly/when_parsing_times_that_differ_below_a_millisecond.ts +14 -0
  48. package/for_TimeOnly/when_rendering_a_fraction.ts +11 -0
  49. package/for_TimeSpan/when_parsing_out_of_range_time.ts +11 -0
  50. package/for_TimeSpan/when_parsing_the_largest_in_range_time_span.ts +10 -0
  51. package/for_TimeSpan/when_parsing_time_beyond_what_a_time_span_can_hold.ts +11 -0
  52. package/package.json +2 -2
package/DateOnly.ts CHANGED
@@ -58,6 +58,7 @@ export class DateOnly {
58
58
  * Parses the ISO-8601 calendar date the server sends, `yyyy-MM-dd`.
59
59
  * @param value The value to parse.
60
60
  * @returns The {@link DateOnly}.
61
+ * @throws {Error} If the format is invalid or a component is out of range, as C# rejects it.
61
62
  */
62
63
  static parse(value: string): DateOnly {
63
64
  const match = dateOnlyRegex.exec(value);
@@ -65,7 +66,33 @@ export class DateOnly {
65
66
  throw new Error(`Invalid DateOnly format: ${value}`);
66
67
  }
67
68
 
68
- return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
69
+ const year = parseInt(match[1], 10);
70
+ const month = parseInt(match[2], 10);
71
+ const day = parseInt(match[3], 10);
72
+
73
+ if (year < 1) {
74
+ throw new Error(`Invalid DateOnly: year ${year} is out of range 1-9999 in '${value}'`);
75
+ }
76
+
77
+ if (month < 1 || month > 12) {
78
+ throw new Error(`Invalid DateOnly: month ${month} is out of range 1-12 in '${value}'`);
79
+ }
80
+
81
+ const daysInMonth = DateOnly.daysInMonth(year, month);
82
+ if (day < 1 || day > daysInMonth) {
83
+ throw new Error(`Invalid DateOnly: day ${day} is out of range 1-${daysInMonth} for ${match[1]}-${match[2]} in '${value}'`);
84
+ }
85
+
86
+ return DateOnly.from(year, month, day);
87
+ }
88
+
89
+ private static daysInMonth(year: number, month: number): number {
90
+ if (month === 2) {
91
+ const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
92
+ return isLeapYear ? 29 : 28;
93
+ }
94
+
95
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
69
96
  }
70
97
 
71
98
  /**
package/TimeOnly.ts CHANGED
@@ -40,20 +40,34 @@ export class TimeOnly {
40
40
  @field(Number)
41
41
  millisecond!: number;
42
42
 
43
+ /**
44
+ * The remaining 100 nanosecond ticks below the millisecond, 0 through 9999. Carries the last four digits of
45
+ * the seven-digit fraction a C# `TimeOnly` holds, so two times that differ below a millisecond stay different.
46
+ */
47
+ @field(Number)
48
+ subMillisecondTicks!: number;
49
+
43
50
  /**
44
51
  * Creates a {@link TimeOnly} from its parts.
45
52
  * @param hour The hour.
46
53
  * @param minute The minute.
47
54
  * @param second The second.
48
55
  * @param millisecond The millisecond.
56
+ * @param subMillisecondTicks The 100 nanosecond ticks below the millisecond, 0 through 9999.
49
57
  * @returns The {@link TimeOnly}.
58
+ * @throws {Error} If `subMillisecondTicks` is not a whole number from 0 through 9999.
50
59
  */
51
- static from(hour: number, minute: number, second: number = 0, millisecond: number = 0): TimeOnly {
60
+ static from(hour: number, minute: number, second: number = 0, millisecond: number = 0, subMillisecondTicks: number = 0): TimeOnly {
61
+ if (!Number.isInteger(subMillisecondTicks) || subMillisecondTicks < 0 || subMillisecondTicks > 9999) {
62
+ throw new Error(`Invalid TimeOnly: subMillisecondTicks ${subMillisecondTicks} is out of range 0-9999`);
63
+ }
64
+
52
65
  const timeOnly = new TimeOnly();
53
66
  timeOnly.hour = hour;
54
67
  timeOnly.minute = minute;
55
68
  timeOnly.second = second;
56
69
  timeOnly.millisecond = millisecond;
70
+ timeOnly.subMillisecondTicks = subMillisecondTicks;
57
71
  return timeOnly;
58
72
  }
59
73
 
@@ -62,9 +76,10 @@ export class TimeOnly {
62
76
  * @param value The value to parse.
63
77
  * @returns The {@link TimeOnly}.
64
78
  * @remarks
65
- * The seconds and the fraction are both optional because the server omits them when they are zero, and the
66
- * fraction carries up to seven digits where JavaScript holds three - so it is truncated rather than rounded,
67
- * which keeps a parse followed by a render from moving the value forward.
79
+ * The seconds and the fraction are both optional because the server omits them when they are zero. The
80
+ * fraction carries up to seven digits, all of which are kept, so C# times that differ below a millisecond
81
+ * stay distinct. Components outside the ranges C# accepts are rejected.
82
+ * @throws {Error} If the format is invalid or a component is out of range.
68
83
  */
69
84
  static parse(value: string): TimeOnly {
70
85
  const match = timeOnlyRegex.exec(value);
@@ -72,17 +87,36 @@ export class TimeOnly {
72
87
  throw new Error(`Invalid TimeOnly format: ${value}`);
73
88
  }
74
89
 
75
- const fraction = match[4] ? match[4].padEnd(3, '0').substring(0, 3) : '0';
90
+ const hour = parseInt(match[1], 10);
91
+ const minute = parseInt(match[2], 10);
92
+ const second = match[3] ? parseInt(match[3], 10) : 0;
93
+
94
+ if (hour > 23) {
95
+ throw new Error(`Invalid TimeOnly: hour ${hour} is out of range 0-23 in '${value}'`);
96
+ }
97
+
98
+ if (minute > 59) {
99
+ throw new Error(`Invalid TimeOnly: minute ${minute} is out of range 0-59 in '${value}'`);
100
+ }
101
+
102
+ if (second > 59) {
103
+ throw new Error(`Invalid TimeOnly: second ${second} is out of range 0-59 in '${value}'`);
104
+ }
105
+
106
+ const fraction = (match[4] ?? '').padEnd(7, '0');
76
107
 
77
108
  return TimeOnly.from(
78
- parseInt(match[1], 10),
79
- parseInt(match[2], 10),
80
- match[3] ? parseInt(match[3], 10) : 0,
81
- parseInt(fraction, 10));
109
+ hour,
110
+ minute,
111
+ second,
112
+ parseInt(fraction.substring(0, 3), 10),
113
+ parseInt(fraction.substring(3), 10));
82
114
  }
83
115
 
84
116
  /**
85
- * Gets the ISO-8601 representation, `HH:mm:ss` - or `HH:mm:ss.fff` when there is a fractional part.
117
+ * Gets the ISO-8601 representation: `HH:mm:ss`, or `HH:mm:ss.fff` when there is a millisecond part, or the
118
+ * full seven-digit `HH:mm:ss.fffffff` when there is a non-zero part below the millisecond. The seven-digit form
119
+ * is padded, never trimmed, so the two fractional forms are the only ones produced; the parser accepts 1-7 digits.
86
120
  * @returns The string.
87
121
  */
88
122
  toString(): string {
@@ -90,6 +124,11 @@ export class TimeOnly {
90
124
  const minute = this.minute.toString().padStart(2, '0');
91
125
  const second = this.second.toString().padStart(2, '0');
92
126
  const time = `${hour}:${minute}:${second}`;
127
+ const subMillisecondTicks = this.subMillisecondTicks ?? 0;
128
+ if (subMillisecondTicks > 0) {
129
+ return `${time}.${this.millisecond.toString().padStart(3, '0')}${subMillisecondTicks.toString().padStart(4, '0')}`;
130
+ }
131
+
93
132
  return this.millisecond > 0 ? `${time}.${this.millisecond.toString().padStart(3, '0')}` : time;
94
133
  }
95
134
 
@@ -115,6 +154,7 @@ export class TimeOnly {
115
154
  this.hour === other.hour &&
116
155
  this.minute === other.minute &&
117
156
  this.second === other.second &&
118
- this.millisecond === other.millisecond;
157
+ this.millisecond === other.millisecond &&
158
+ (this.subMillisecondTicks ?? 0) === (other.subMillisecondTicks ?? 0);
119
159
  }
120
160
  }
package/TimeSpan.ts CHANGED
@@ -64,7 +64,7 @@ export class TimeSpan {
64
64
  * The string format should match: [-][d.]hh:mm:ss[.fffffff]
65
65
  * @param {string} value String representation of TimeSpan.
66
66
  * @returns {TimeSpan} A TimeSpan instance with all properties populated.
67
- * @throws {Error} If the string format is invalid.
67
+ * @throws {Error} If the string format is invalid or hours, minutes or seconds are out of range, as C# rejects it.
68
68
  */
69
69
  static parse(value: string): TimeSpan {
70
70
  const match = timeSpanRegex.exec(value);
@@ -77,6 +77,24 @@ export class TimeSpan {
77
77
  const hours = parseInt(match[3], 10);
78
78
  const minutes = parseInt(match[4], 10);
79
79
  const seconds = parseInt(match[5], 10);
80
+
81
+ if (hours > 23) {
82
+ throw new Error(`Invalid TimeSpan: hours ${hours} is out of range 0-23 in '${value}'`);
83
+ }
84
+
85
+ if (minutes > 59) {
86
+ throw new Error(`Invalid TimeSpan: minutes ${minutes} is out of range 0-59 in '${value}'`);
87
+ }
88
+
89
+ if (seconds > 59) {
90
+ throw new Error(`Invalid TimeSpan: seconds ${seconds} is out of range 0-59 in '${value}'`);
91
+ }
92
+
93
+ const maxDays = 10675199;
94
+ if (days > maxDays) {
95
+ throw new Error(`Invalid TimeSpan: days ${days} is out of range 0-${maxDays} in '${value}'`);
96
+ }
97
+
80
98
  const fractionalSeconds = match[6] ? match[6].padEnd(7, '0') : '0000000';
81
99
 
82
100
  const ticksPerDay = 864000000000;
@@ -84,6 +102,13 @@ export class TimeSpan {
84
102
  const ticksPerMinute = 600000000;
85
103
  const ticksPerSecond = 10000000;
86
104
 
105
+ // TimeSpan.MaxValue is 10675199.02:48:05.4775807 and MinValue one tick further on the negative side.
106
+ const remainderTicks =
107
+ hours * ticksPerHour + minutes * ticksPerMinute + seconds * ticksPerSecond + parseInt(fractionalSeconds, 10);
108
+ if (days === maxDays && remainderTicks > (isNegative ? 100854775808 : 100854775807)) {
109
+ throw new Error(`Invalid TimeSpan: '${value}' is outside the range a TimeSpan can hold`);
110
+ }
111
+
87
112
  const ticks =
88
113
  days * ticksPerDay +
89
114
  hours * ticksPerHour +
@@ -6,6 +6,7 @@ export declare class DateOnly {
6
6
  day: number;
7
7
  static from(year: number, month: number, day: number): DateOnly;
8
8
  static parse(value: string): DateOnly;
9
+ private static daysInMonth;
9
10
  static fromDate(date: Date): DateOnly;
10
11
  toDate(): Date;
11
12
  toString(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"DateOnly.d.ts","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,KAAK,EAAG,MAAM,CAAC;IAMf,GAAG,EAAG,MAAM,CAAC;IASb,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAM9D;IAOD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOpC;IAUD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAEpC;IASD,MAAM,IAAI,IAAI,CAEb;IAMD,QAAQ,IAAI,MAAM,CAIjB;IASD,MAAM,IAAI,MAAM,CAEf;IAOD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAElD;CACJ"}
1
+ {"version":3,"file":"DateOnly.d.ts","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,KAAK,EAAG,MAAM,CAAC;IAMf,GAAG,EAAG,MAAM,CAAC;IASb,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAM9D;IAQD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAwBpC;IAED,OAAO,CAAC,MAAM,CAAC,WAAW;IAiB1B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAEpC;IASD,MAAM,IAAI,IAAI,CAEb;IAMD,QAAQ,IAAI,MAAM,CAIjB;IASD,MAAM,IAAI,MAAM,CAEf;IAOD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAElD;CACJ"}
package/dist/DateOnly.js CHANGED
@@ -19,7 +19,27 @@ export class DateOnly {
19
19
  if (match === null) {
20
20
  throw new Error(`Invalid DateOnly format: ${value}`);
21
21
  }
22
- return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
22
+ const year = parseInt(match[1], 10);
23
+ const month = parseInt(match[2], 10);
24
+ const day = parseInt(match[3], 10);
25
+ if (year < 1) {
26
+ throw new Error(`Invalid DateOnly: year ${year} is out of range 1-9999 in '${value}'`);
27
+ }
28
+ if (month < 1 || month > 12) {
29
+ throw new Error(`Invalid DateOnly: month ${month} is out of range 1-12 in '${value}'`);
30
+ }
31
+ const daysInMonth = DateOnly.daysInMonth(year, month);
32
+ if (day < 1 || day > daysInMonth) {
33
+ throw new Error(`Invalid DateOnly: day ${day} is out of range 1-${daysInMonth} for ${match[1]}-${match[2]} in '${value}'`);
34
+ }
35
+ return DateOnly.from(year, month, day);
36
+ }
37
+ static daysInMonth(year, month) {
38
+ if (month === 2) {
39
+ const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
40
+ return isLeapYear ? 29 : 28;
41
+ }
42
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
23
43
  }
24
44
  static fromDate(date) {
25
45
  return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());
@@ -1 +1 @@
1
- {"version":3,"file":"DateOnly.js","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,2BAA2B,CAAC;AAclD,MAAM,OAAO,QAAQ;IACjB,MAAM,CAAU,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC;IAMvC,IAAI,CAAU;IAMd,KAAK,CAAU;IAMf,GAAG,CAAU;IASb,MAAM,CAAC,IAAI,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW;QAChD,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACvB,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC;QACnB,OAAO,QAAQ,CAAC;IACpB,CAAC;IAOD,MAAM,CAAC,KAAK,CAAC,KAAa;QACtB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACjG,CAAC;IAUD,MAAM,CAAC,QAAQ,CAAC,IAAU;QACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;IASD,MAAM;QACF,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAMD,QAAQ;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACjD,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;IACtE,CAAC;IASD,MAAM;QACF,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAOD,MAAM,CAAC,KAAkC;QACrC,OAAO,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC;IACvG,CAAC;CACJ;AA/FG;IADC,KAAK,CAAC,MAAM,CAAC;;sCACA;AAMd;IADC,KAAK,CAAC,MAAM,CAAC;;uCACC;AAMf;IADC,KAAK,CAAC,MAAM,CAAC;;qCACD"}
1
+ {"version":3,"file":"DateOnly.js","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,8 @@ export declare class TimeOnly {
5
5
  minute: number;
6
6
  second: number;
7
7
  millisecond: number;
8
- static from(hour: number, minute: number, second?: number, millisecond?: number): TimeOnly;
8
+ subMillisecondTicks: number;
9
+ static from(hour: number, minute: number, second?: number, millisecond?: number, subMillisecondTicks?: number): TimeOnly;
9
10
  static parse(value: string): TimeOnly;
10
11
  toString(): string;
11
12
  toJSON(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"TimeOnly.d.ts","sourceRoot":"","sources":["../TimeOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,MAAM,EAAG,MAAM,CAAC;IAMhB,MAAM,EAAG,MAAM,CAAC;IAMhB,WAAW,EAAG,MAAM,CAAC;IAUrB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,EAAE,WAAW,GAAE,MAAU,GAAG,QAAQ,CAO/F;IAWD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAapC;IAMD,QAAQ,IAAI,MAAM,CAMjB;IAUD,MAAM,IAAI,MAAM,CAEf;IAOD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAMlD;CACJ"}
1
+ {"version":3,"file":"TimeOnly.d.ts","sourceRoot":"","sources":["../TimeOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,MAAM,EAAG,MAAM,CAAC;IAMhB,MAAM,EAAG,MAAM,CAAC;IAMhB,WAAW,EAAG,MAAM,CAAC;IAOrB,mBAAmB,EAAG,MAAM,CAAC;IAY7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,EAAE,WAAW,GAAE,MAAU,EAAE,mBAAmB,GAAE,MAAU,GAAG,QAAQ,CAYhI;IAYD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CA8BpC;IAQD,QAAQ,IAAI,MAAM,CAWjB;IAUD,MAAM,IAAI,MAAM,CAEf;IAOD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAOlD;CACJ"}
package/dist/TimeOnly.js CHANGED
@@ -8,12 +8,17 @@ export class TimeOnly {
8
8
  minute;
9
9
  second;
10
10
  millisecond;
11
- static from(hour, minute, second = 0, millisecond = 0) {
11
+ subMillisecondTicks;
12
+ static from(hour, minute, second = 0, millisecond = 0, subMillisecondTicks = 0) {
13
+ if (!Number.isInteger(subMillisecondTicks) || subMillisecondTicks < 0 || subMillisecondTicks > 9999) {
14
+ throw new Error(`Invalid TimeOnly: subMillisecondTicks ${subMillisecondTicks} is out of range 0-9999`);
15
+ }
12
16
  const timeOnly = new TimeOnly();
13
17
  timeOnly.hour = hour;
14
18
  timeOnly.minute = minute;
15
19
  timeOnly.second = second;
16
20
  timeOnly.millisecond = millisecond;
21
+ timeOnly.subMillisecondTicks = subMillisecondTicks;
17
22
  return timeOnly;
18
23
  }
19
24
  static parse(value) {
@@ -21,14 +26,30 @@ export class TimeOnly {
21
26
  if (match === null) {
22
27
  throw new Error(`Invalid TimeOnly format: ${value}`);
23
28
  }
24
- const fraction = match[4] ? match[4].padEnd(3, '0').substring(0, 3) : '0';
25
- return TimeOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), match[3] ? parseInt(match[3], 10) : 0, parseInt(fraction, 10));
29
+ const hour = parseInt(match[1], 10);
30
+ const minute = parseInt(match[2], 10);
31
+ const second = match[3] ? parseInt(match[3], 10) : 0;
32
+ if (hour > 23) {
33
+ throw new Error(`Invalid TimeOnly: hour ${hour} is out of range 0-23 in '${value}'`);
34
+ }
35
+ if (minute > 59) {
36
+ throw new Error(`Invalid TimeOnly: minute ${minute} is out of range 0-59 in '${value}'`);
37
+ }
38
+ if (second > 59) {
39
+ throw new Error(`Invalid TimeOnly: second ${second} is out of range 0-59 in '${value}'`);
40
+ }
41
+ const fraction = (match[4] ?? '').padEnd(7, '0');
42
+ return TimeOnly.from(hour, minute, second, parseInt(fraction.substring(0, 3), 10), parseInt(fraction.substring(3), 10));
26
43
  }
27
44
  toString() {
28
45
  const hour = this.hour.toString().padStart(2, '0');
29
46
  const minute = this.minute.toString().padStart(2, '0');
30
47
  const second = this.second.toString().padStart(2, '0');
31
48
  const time = `${hour}:${minute}:${second}`;
49
+ const subMillisecondTicks = this.subMillisecondTicks ?? 0;
50
+ if (subMillisecondTicks > 0) {
51
+ return `${time}.${this.millisecond.toString().padStart(3, '0')}${subMillisecondTicks.toString().padStart(4, '0')}`;
52
+ }
32
53
  return this.millisecond > 0 ? `${time}.${this.millisecond.toString().padStart(3, '0')}` : time;
33
54
  }
34
55
  toJSON() {
@@ -39,7 +60,8 @@ export class TimeOnly {
39
60
  this.hour === other.hour &&
40
61
  this.minute === other.minute &&
41
62
  this.second === other.second &&
42
- this.millisecond === other.millisecond;
63
+ this.millisecond === other.millisecond &&
64
+ (this.subMillisecondTicks ?? 0) === (other.subMillisecondTicks ?? 0);
43
65
  }
44
66
  }
45
67
  __decorate([
@@ -58,4 +80,8 @@ __decorate([
58
80
  field(Number),
59
81
  __metadata("design:type", Number)
60
82
  ], TimeOnly.prototype, "millisecond", void 0);
83
+ __decorate([
84
+ field(Number),
85
+ __metadata("design:type", Number)
86
+ ], TimeOnly.prototype, "subMillisecondTicks", void 0);
61
87
  //# sourceMappingURL=TimeOnly.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimeOnly.js","sourceRoot":"","sources":["../TimeOnly.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TimeOnly.js","sourceRoot":"","sources":["../TimeOnly.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TimeSpan.d.ts","sourceRoot":"","sources":["../TimeSpan.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAIvC,KAAK,EAAG,MAAM,CAAC;IAGf,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,CAAC;IAGf,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAGtB,WAAW,EAAG,MAAM,CAAC;IAGrB,OAAO,EAAG,MAAM,CAAC;IAGjB,OAAO,EAAG,MAAM,CAAC;IAGjB,SAAS,EAAG,MAAM,CAAC;IAGnB,UAAU,EAAG,MAAM,CAAC;IAGpB,iBAAiB,EAAG,MAAM,CAAC;IAG3B,iBAAiB,EAAG,MAAM,CAAC;IAG3B,gBAAgB,EAAG,MAAM,CAAC;IAG1B,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAStB,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CA6CpC;IAOD,QAAQ,IAAI,MAAM,CAmCjB;IAOD,MAAM,IAAI,MAAM,CAEf;CACJ"}
1
+ {"version":3,"file":"TimeSpan.d.ts","sourceRoot":"","sources":["../TimeSpan.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAIvC,KAAK,EAAG,MAAM,CAAC;IAGf,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,CAAC;IAGf,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAGtB,WAAW,EAAG,MAAM,CAAC;IAGrB,OAAO,EAAG,MAAM,CAAC;IAGjB,OAAO,EAAG,MAAM,CAAC;IAGjB,SAAS,EAAG,MAAM,CAAC;IAGnB,UAAU,EAAG,MAAM,CAAC;IAGpB,iBAAiB,EAAG,MAAM,CAAC;IAG3B,iBAAiB,EAAG,MAAM,CAAC;IAG3B,gBAAgB,EAAG,MAAM,CAAC;IAG1B,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAStB,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAsEpC;IAOD,QAAQ,IAAI,MAAM,CAmCjB;IAOD,MAAM,IAAI,MAAM,CAEf;CACJ"}
package/dist/TimeSpan.js CHANGED
@@ -29,11 +29,28 @@ export class TimeSpan {
29
29
  const hours = parseInt(match[3], 10);
30
30
  const minutes = parseInt(match[4], 10);
31
31
  const seconds = parseInt(match[5], 10);
32
+ if (hours > 23) {
33
+ throw new Error(`Invalid TimeSpan: hours ${hours} is out of range 0-23 in '${value}'`);
34
+ }
35
+ if (minutes > 59) {
36
+ throw new Error(`Invalid TimeSpan: minutes ${minutes} is out of range 0-59 in '${value}'`);
37
+ }
38
+ if (seconds > 59) {
39
+ throw new Error(`Invalid TimeSpan: seconds ${seconds} is out of range 0-59 in '${value}'`);
40
+ }
41
+ const maxDays = 10675199;
42
+ if (days > maxDays) {
43
+ throw new Error(`Invalid TimeSpan: days ${days} is out of range 0-${maxDays} in '${value}'`);
44
+ }
32
45
  const fractionalSeconds = match[6] ? match[6].padEnd(7, '0') : '0000000';
33
46
  const ticksPerDay = 864000000000;
34
47
  const ticksPerHour = 36000000000;
35
48
  const ticksPerMinute = 600000000;
36
49
  const ticksPerSecond = 10000000;
50
+ const remainderTicks = hours * ticksPerHour + minutes * ticksPerMinute + seconds * ticksPerSecond + parseInt(fractionalSeconds, 10);
51
+ if (days === maxDays && remainderTicks > (isNegative ? 100854775808 : 100854775807)) {
52
+ throw new Error(`Invalid TimeSpan: '${value}' is outside the range a TimeSpan can hold`);
53
+ }
37
54
  const ticks = days * ticksPerDay +
38
55
  hours * ticksPerHour +
39
56
  minutes * ticksPerMinute +
@@ -1 +1 @@
1
- {"version":3,"file":"TimeSpan.js","sourceRoot":"","sources":["../TimeSpan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TimeSpan.js","sourceRoot":"","sources":["../TimeSpan.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ export declare class DateOnly {
6
6
  day: number;
7
7
  static from(year: number, month: number, day: number): DateOnly;
8
8
  static parse(value: string): DateOnly;
9
+ private static daysInMonth;
9
10
  static fromDate(date: Date): DateOnly;
10
11
  toDate(): Date;
11
12
  toString(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"DateOnly.d.ts","sourceRoot":"","sources":["../../DateOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,KAAK,EAAG,MAAM,CAAC;IAMf,GAAG,EAAG,MAAM,CAAC;IASb,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ;IAa/D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAiBrC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ;IAWrC,MAAM,IAAI,IAAI;IAQd,QAAQ,IAAI,MAAM;IAalB,MAAM,IAAI,MAAM;IAShB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;CAGtD"}
1
+ {"version":3,"file":"DateOnly.d.ts","sourceRoot":"","sources":["../../DateOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,KAAK,EAAG,MAAM,CAAC;IAMf,GAAG,EAAG,MAAM,CAAC;IASb,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ;IAc/D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IA0BrC,OAAO,CAAC,MAAM,CAAC,WAAW;IAiB1B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ;IAWrC,MAAM,IAAI,IAAI;IAQd,QAAQ,IAAI,MAAM;IAalB,MAAM,IAAI,MAAM;IAShB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;CAGtD"}
@@ -22,7 +22,27 @@ class DateOnly {
22
22
  if (match === null) {
23
23
  throw new Error(`Invalid DateOnly format: ${value}`);
24
24
  }
25
- return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
25
+ const year = parseInt(match[1], 10);
26
+ const month = parseInt(match[2], 10);
27
+ const day = parseInt(match[3], 10);
28
+ if (year < 1) {
29
+ throw new Error(`Invalid DateOnly: year ${year} is out of range 1-9999 in '${value}'`);
30
+ }
31
+ if (month < 1 || month > 12) {
32
+ throw new Error(`Invalid DateOnly: month ${month} is out of range 1-12 in '${value}'`);
33
+ }
34
+ const daysInMonth = DateOnly.daysInMonth(year, month);
35
+ if (day < 1 || day > daysInMonth) {
36
+ throw new Error(`Invalid DateOnly: day ${day} is out of range 1-${daysInMonth} for ${match[1]}-${match[2]} in '${value}'`);
37
+ }
38
+ return DateOnly.from(year, month, day);
39
+ }
40
+ static daysInMonth(year, month) {
41
+ if (month === 2) {
42
+ const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
43
+ return isLeapYear ? 29 : 28;
44
+ }
45
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
26
46
  }
27
47
  static fromDate(date) {
28
48
  return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());
@@ -1 +1 @@
1
- {"version":3,"file":"DateOnly.js","sources":["../../DateOnly.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { field } from './fieldDecorator';\nimport { typeKey } from './typeKey';\n\nconst dateOnlyRegex = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\n/**\n * Represents a date with no time and no time zone - a day on a calendar.\n * @remarks\n * Deliberately not a JavaScript `Date`. A `Date` is an instant, and a calendar date is not one: turning\n * `2026-05-12` into a `Date` produces UTC midnight, which every browser-local getter west of UTC reads back as\n * the 11th. That is a wrong answer that looks right, and it is only wrong for users in some time zones - which is\n * how it survives development in others.\n *\n * Holding the three parts the server sent means there is no instant to convert and nothing to shift. Where a\n * `Date` is genuinely wanted - to feed a date picker, or to do arithmetic - {@link toDate} constructs one in the\n * local time zone, and the choice is then visible at the call site making it.\n */\nexport class DateOnly {\n static readonly [typeKey] = 'DateOnly';\n\n /**\n * The year.\n */\n @field(Number)\n year!: number;\n\n /**\n * The month, 1 through 12.\n */\n @field(Number)\n month!: number;\n\n /**\n * The day of the month, 1 through 31.\n */\n @field(Number)\n day!: number;\n\n /**\n * Creates a {@link DateOnly} from its parts.\n * @param year The year.\n * @param month The month, 1 through 12.\n * @param day The day of the month.\n * @returns The {@link DateOnly}.\n */\n static from(year: number, month: number, day: number): DateOnly {\n const dateOnly = new DateOnly();\n dateOnly.year = year;\n dateOnly.month = month;\n dateOnly.day = day;\n return dateOnly;\n }\n\n /**\n * Parses the ISO-8601 calendar date the server sends, `yyyy-MM-dd`.\n * @param value The value to parse.\n * @returns The {@link DateOnly}.\n */\n static parse(value: string): DateOnly {\n const match = dateOnlyRegex.exec(value);\n if (match === null) {\n throw new Error(`Invalid DateOnly format: ${value}`);\n }\n\n return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));\n }\n\n /**\n * Creates a {@link DateOnly} for the calendar date a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date} falls on in the local time zone.\n * @param date The date to take the calendar date of.\n * @returns The {@link DateOnly}.\n * @remarks\n * Reads the local parts rather than the UTC ones, because the calendar date a moment falls on is a question\n * only a time zone can answer, and the local one is the one the person looking at the screen is in.\n */\n static fromDate(date: Date): DateOnly {\n return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());\n }\n\n /**\n * Converts to a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date} at midnight in the local time zone.\n * @returns The {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date}.\n * @remarks\n * Local rather than UTC, so the date a caller reads back with `getDate()` is the one they started with. This\n * invents a time that was never sent, which is why it is a method to call rather than what the value is.\n */\n toDate(): Date {\n return new Date(this.year, this.month - 1, this.day);\n }\n\n /**\n * Gets the ISO-8601 representation, `yyyy-MM-dd` - the same form the server sent.\n * @returns The string.\n */\n toString(): string {\n const month = this.month.toString().padStart(2, '0');\n const day = this.day.toString().padStart(2, '0');\n return `${this.year.toString().padStart(4, '0')}-${month}-${day}`;\n }\n\n /**\n * Gets the ISO-8601 representation used when this is serialized as JSON.\n *\n * Without this, `JSON.stringify` falls back to enumerating `year`, `month` and `day`, so a\n * request body carries a component object where the server expects the scalar string.\n * @returns The string.\n */\n toJSON(): string {\n return this.toString();\n }\n\n /**\n * Determines whether this is the same calendar date as another.\n * @param other The other date.\n * @returns True when they are the same date.\n */\n equals(other: DateOnly | undefined | null): boolean {\n return !!other && this.year === other.year && this.month === other.month && this.day === other.day;\n }\n}\n"],"names":["typeKey","__decorate","field"],"mappings":";;;;;;AAMA,MAAM,aAAa,GAAG,2BAA2B;MAcpC,QAAQ,CAAA;AACjB,IAAA,QAAiBA,eAAO,IAAI,UAAU;AAMtC,IAAA,IAAI;AAMJ,IAAA,KAAK;AAML,IAAA,GAAG;AASH,IAAA,OAAO,IAAI,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW,EAAA;AAChD,QAAA,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE;AAC/B,QAAA,QAAQ,CAAC,IAAI,GAAG,IAAI;AACpB,QAAA,QAAQ,CAAC,KAAK,GAAG,KAAK;AACtB,QAAA,QAAQ,CAAC,GAAG,GAAG,GAAG;AAClB,QAAA,OAAO,QAAQ;IACnB;IAOA,OAAO,KAAK,CAAC,KAAa,EAAA;QACtB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;AACvC,QAAA,IAAI,KAAK,KAAK,IAAI,EAAE;AAChB,YAAA,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAA,CAAE,CAAC;QACxD;AAEA,QAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChG;IAUA,OAAO,QAAQ,CAAC,IAAU,EAAA;QACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IACjF;IASA,MAAM,GAAA;AACF,QAAA,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC;IACxD;IAMA,QAAQ,GAAA;AACJ,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;AACpD,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;AAChD,QAAA,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,EAAI,GAAG,EAAE;IACrE;IASA,MAAM,GAAA;AACF,QAAA,OAAO,IAAI,CAAC,QAAQ,EAAE;IAC1B;AAOA,IAAA,MAAM,CAAC,KAAkC,EAAA;QACrC,OAAO,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG;IACtG;;AA9FAC,iBAAA,CAAA;IADCC,oBAAK,CAAC,MAAM,CAAC;;AACA,CAAA,EAAA,QAAA,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,CAAA;AAMdD,iBAAA,CAAA;IADCC,oBAAK,CAAC,MAAM,CAAC;;AACC,CAAA,EAAA,QAAA,CAAA,SAAA,EAAA,OAAA,EAAA,MAAA,CAAA;AAMfD,iBAAA,CAAA;IADCC,oBAAK,CAAC,MAAM,CAAC;;AACD,CAAA,EAAA,QAAA,CAAA,SAAA,EAAA,KAAA,EAAA,MAAA,CAAA;;"}
1
+ {"version":3,"file":"DateOnly.js","sources":["../../DateOnly.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { field } from './fieldDecorator';\nimport { typeKey } from './typeKey';\n\nconst dateOnlyRegex = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\n/**\n * Represents a date with no time and no time zone - a day on a calendar.\n * @remarks\n * Deliberately not a JavaScript `Date`. A `Date` is an instant, and a calendar date is not one: turning\n * `2026-05-12` into a `Date` produces UTC midnight, which every browser-local getter west of UTC reads back as\n * the 11th. That is a wrong answer that looks right, and it is only wrong for users in some time zones - which is\n * how it survives development in others.\n *\n * Holding the three parts the server sent means there is no instant to convert and nothing to shift. Where a\n * `Date` is genuinely wanted - to feed a date picker, or to do arithmetic - {@link toDate} constructs one in the\n * local time zone, and the choice is then visible at the call site making it.\n */\nexport class DateOnly {\n static readonly [typeKey] = 'DateOnly';\n\n /**\n * The year.\n */\n @field(Number)\n year!: number;\n\n /**\n * The month, 1 through 12.\n */\n @field(Number)\n month!: number;\n\n /**\n * The day of the month, 1 through 31.\n */\n @field(Number)\n day!: number;\n\n /**\n * Creates a {@link DateOnly} from its parts.\n * @param year The year.\n * @param month The month, 1 through 12.\n * @param day The day of the month.\n * @returns The {@link DateOnly}.\n */\n static from(year: number, month: number, day: number): DateOnly {\n const dateOnly = new DateOnly();\n dateOnly.year = year;\n dateOnly.month = month;\n dateOnly.day = day;\n return dateOnly;\n }\n\n /**\n * Parses the ISO-8601 calendar date the server sends, `yyyy-MM-dd`.\n * @param value The value to parse.\n * @returns The {@link DateOnly}.\n * @throws {Error} If the format is invalid or a component is out of range, as C# rejects it.\n */\n static parse(value: string): DateOnly {\n const match = dateOnlyRegex.exec(value);\n if (match === null) {\n throw new Error(`Invalid DateOnly format: ${value}`);\n }\n\n const year = parseInt(match[1], 10);\n const month = parseInt(match[2], 10);\n const day = parseInt(match[3], 10);\n\n if (year < 1) {\n throw new Error(`Invalid DateOnly: year ${year} is out of range 1-9999 in '${value}'`);\n }\n\n if (month < 1 || month > 12) {\n throw new Error(`Invalid DateOnly: month ${month} is out of range 1-12 in '${value}'`);\n }\n\n const daysInMonth = DateOnly.daysInMonth(year, month);\n if (day < 1 || day > daysInMonth) {\n throw new Error(`Invalid DateOnly: day ${day} is out of range 1-${daysInMonth} for ${match[1]}-${match[2]} in '${value}'`);\n }\n\n return DateOnly.from(year, month, day);\n }\n\n private static daysInMonth(year: number, month: number): number {\n if (month === 2) {\n const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n return isLeapYear ? 29 : 28;\n }\n\n return [4, 6, 9, 11].includes(month) ? 30 : 31;\n }\n\n /**\n * Creates a {@link DateOnly} for the calendar date a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date} falls on in the local time zone.\n * @param date The date to take the calendar date of.\n * @returns The {@link DateOnly}.\n * @remarks\n * Reads the local parts rather than the UTC ones, because the calendar date a moment falls on is a question\n * only a time zone can answer, and the local one is the one the person looking at the screen is in.\n */\n static fromDate(date: Date): DateOnly {\n return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());\n }\n\n /**\n * Converts to a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date} at midnight in the local time zone.\n * @returns The {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date}.\n * @remarks\n * Local rather than UTC, so the date a caller reads back with `getDate()` is the one they started with. This\n * invents a time that was never sent, which is why it is a method to call rather than what the value is.\n */\n toDate(): Date {\n return new Date(this.year, this.month - 1, this.day);\n }\n\n /**\n * Gets the ISO-8601 representation, `yyyy-MM-dd` - the same form the server sent.\n * @returns The string.\n */\n toString(): string {\n const month = this.month.toString().padStart(2, '0');\n const day = this.day.toString().padStart(2, '0');\n return `${this.year.toString().padStart(4, '0')}-${month}-${day}`;\n }\n\n /**\n * Gets the ISO-8601 representation used when this is serialized as JSON.\n *\n * Without this, `JSON.stringify` falls back to enumerating `year`, `month` and `day`, so a\n * request body carries a component object where the server expects the scalar string.\n * @returns The string.\n */\n toJSON(): string {\n return this.toString();\n }\n\n /**\n * Determines whether this is the same calendar date as another.\n * @param other The other date.\n * @returns True when they are the same date.\n */\n equals(other: DateOnly | undefined | null): boolean {\n return !!other && this.year === other.year && this.month === other.month && this.day === other.day;\n }\n}\n"],"names":["typeKey","__decorate","field"],"mappings":"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"}
@@ -5,7 +5,8 @@ export declare class TimeOnly {
5
5
  minute: number;
6
6
  second: number;
7
7
  millisecond: number;
8
- static from(hour: number, minute: number, second?: number, millisecond?: number): TimeOnly;
8
+ subMillisecondTicks: number;
9
+ static from(hour: number, minute: number, second?: number, millisecond?: number, subMillisecondTicks?: number): TimeOnly;
9
10
  static parse(value: string): TimeOnly;
10
11
  toString(): string;
11
12
  toJSON(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"TimeOnly.d.ts","sourceRoot":"","sources":["../../TimeOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,MAAM,EAAG,MAAM,CAAC;IAMhB,MAAM,EAAG,MAAM,CAAC;IAMhB,WAAW,EAAG,MAAM,CAAC;IAUrB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,EAAE,WAAW,GAAE,MAAU,GAAG,QAAQ;IAkBhG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAmBrC,QAAQ,IAAI,MAAM;IAgBlB,MAAM,IAAI,MAAM;IAShB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;CAOtD"}
1
+ {"version":3,"file":"TimeOnly.d.ts","sourceRoot":"","sources":["../../TimeOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,MAAM,EAAG,MAAM,CAAC;IAMhB,MAAM,EAAG,MAAM,CAAC;IAMhB,WAAW,EAAG,MAAM,CAAC;IAOrB,mBAAmB,EAAG,MAAM,CAAC;IAY7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,EAAE,WAAW,GAAE,MAAU,EAAE,mBAAmB,GAAE,MAAU,GAAG,QAAQ;IAwBjI,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAsCrC,QAAQ,IAAI,MAAM;IAqBlB,MAAM,IAAI,MAAM;IAShB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;CAQtD"}
@@ -11,12 +11,17 @@ class TimeOnly {
11
11
  minute;
12
12
  second;
13
13
  millisecond;
14
- static from(hour, minute, second = 0, millisecond = 0) {
14
+ subMillisecondTicks;
15
+ static from(hour, minute, second = 0, millisecond = 0, subMillisecondTicks = 0) {
16
+ if (!Number.isInteger(subMillisecondTicks) || subMillisecondTicks < 0 || subMillisecondTicks > 9999) {
17
+ throw new Error(`Invalid TimeOnly: subMillisecondTicks ${subMillisecondTicks} is out of range 0-9999`);
18
+ }
15
19
  const timeOnly = new TimeOnly();
16
20
  timeOnly.hour = hour;
17
21
  timeOnly.minute = minute;
18
22
  timeOnly.second = second;
19
23
  timeOnly.millisecond = millisecond;
24
+ timeOnly.subMillisecondTicks = subMillisecondTicks;
20
25
  return timeOnly;
21
26
  }
22
27
  static parse(value) {
@@ -24,14 +29,30 @@ class TimeOnly {
24
29
  if (match === null) {
25
30
  throw new Error(`Invalid TimeOnly format: ${value}`);
26
31
  }
27
- const fraction = match[4] ? match[4].padEnd(3, '0').substring(0, 3) : '0';
28
- return TimeOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), match[3] ? parseInt(match[3], 10) : 0, parseInt(fraction, 10));
32
+ const hour = parseInt(match[1], 10);
33
+ const minute = parseInt(match[2], 10);
34
+ const second = match[3] ? parseInt(match[3], 10) : 0;
35
+ if (hour > 23) {
36
+ throw new Error(`Invalid TimeOnly: hour ${hour} is out of range 0-23 in '${value}'`);
37
+ }
38
+ if (minute > 59) {
39
+ throw new Error(`Invalid TimeOnly: minute ${minute} is out of range 0-59 in '${value}'`);
40
+ }
41
+ if (second > 59) {
42
+ throw new Error(`Invalid TimeOnly: second ${second} is out of range 0-59 in '${value}'`);
43
+ }
44
+ const fraction = (match[4] ?? '').padEnd(7, '0');
45
+ return TimeOnly.from(hour, minute, second, parseInt(fraction.substring(0, 3), 10), parseInt(fraction.substring(3), 10));
29
46
  }
30
47
  toString() {
31
48
  const hour = this.hour.toString().padStart(2, '0');
32
49
  const minute = this.minute.toString().padStart(2, '0');
33
50
  const second = this.second.toString().padStart(2, '0');
34
51
  const time = `${hour}:${minute}:${second}`;
52
+ const subMillisecondTicks = this.subMillisecondTicks ?? 0;
53
+ if (subMillisecondTicks > 0) {
54
+ return `${time}.${this.millisecond.toString().padStart(3, '0')}${subMillisecondTicks.toString().padStart(4, '0')}`;
55
+ }
35
56
  return this.millisecond > 0 ? `${time}.${this.millisecond.toString().padStart(3, '0')}` : time;
36
57
  }
37
58
  toJSON() {
@@ -42,7 +63,8 @@ class TimeOnly {
42
63
  this.hour === other.hour &&
43
64
  this.minute === other.minute &&
44
65
  this.second === other.second &&
45
- this.millisecond === other.millisecond;
66
+ this.millisecond === other.millisecond &&
67
+ (this.subMillisecondTicks ?? 0) === (other.subMillisecondTicks ?? 0);
46
68
  }
47
69
  }
48
70
  _tslib.__decorate([
@@ -61,6 +83,10 @@ _tslib.__decorate([
61
83
  fieldDecorator.field(Number),
62
84
  _tslib.__metadata("design:type", Number)
63
85
  ], TimeOnly.prototype, "millisecond", void 0);
86
+ _tslib.__decorate([
87
+ fieldDecorator.field(Number),
88
+ _tslib.__metadata("design:type", Number)
89
+ ], TimeOnly.prototype, "subMillisecondTicks", void 0);
64
90
 
65
91
  exports.TimeOnly = TimeOnly;
66
92
  //# sourceMappingURL=TimeOnly.js.map