@nextera.one/tps-standard 0.5.3 → 0.5.34
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/dist/date.d.ts +54 -0
- package/dist/date.js +174 -0
- package/dist/date.js.map +1 -0
- package/dist/drivers/gregorian.d.ts +3 -5
- package/dist/drivers/gregorian.js +26 -19
- package/dist/drivers/gregorian.js.map +1 -1
- package/dist/drivers/hijri.d.ts +1 -16
- package/dist/drivers/hijri.js +9 -102
- package/dist/drivers/hijri.js.map +1 -1
- package/dist/drivers/holocene.d.ts +6 -3
- package/dist/drivers/holocene.js +7 -20
- package/dist/drivers/holocene.js.map +1 -1
- package/dist/drivers/julian.d.ts +3 -10
- package/dist/drivers/julian.js +11 -71
- package/dist/drivers/julian.js.map +1 -1
- package/dist/drivers/persian.d.ts +1 -6
- package/dist/drivers/persian.js +17 -92
- package/dist/drivers/persian.js.map +1 -1
- package/dist/drivers/tps.d.ts +11 -28
- package/dist/drivers/tps.js +8 -58
- package/dist/drivers/tps.js.map +1 -1
- package/dist/drivers/unix.d.ts +5 -6
- package/dist/drivers/unix.js +10 -32
- package/dist/drivers/unix.js.map +1 -1
- package/dist/index.d.ts +6 -477
- package/dist/index.js +33 -978
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +85 -0
- package/dist/types.js +30 -0
- package/dist/types.js.map +1 -0
- package/dist/uid.d.ts +48 -0
- package/dist/uid.js +225 -0
- package/dist/uid.js.map +1 -0
- package/dist/utils/calendar.d.ts +55 -0
- package/dist/utils/calendar.js +136 -0
- package/dist/utils/calendar.js.map +1 -0
- package/dist/utils/env.d.ts +12 -0
- package/dist/utils/env.js +79 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/tps-string.d.ts +12 -0
- package/dist/utils/tps-string.js +164 -0
- package/dist/utils/tps-string.js.map +1 -0
- package/package.json +1 -1
- package/src/date.ts +243 -0
- package/src/drivers/gregorian.ts +29 -27
- package/src/drivers/hijri.ts +13 -113
- package/src/drivers/holocene.ts +11 -12
- package/src/drivers/julian.ts +18 -72
- package/src/drivers/persian.ts +25 -92
- package/src/drivers/tps.ts +16 -55
- package/src/drivers/unix.ts +12 -33
- package/src/index.ts +18 -1446
- package/src/types.ts +107 -0
- package/src/uid.ts +308 -0
- package/src/utils/calendar.ts +161 -0
- package/src/utils/env.ts +88 -0
- package/src/utils/tps-string.ts +166 -0
package/src/drivers/gregorian.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
TPSComponents,
|
|
4
|
-
TimeOrder,
|
|
5
|
-
TPS,
|
|
6
|
-
CalendarMetadata,
|
|
7
|
-
} from "../index";
|
|
1
|
+
import { CalendarDriver, CalendarMetadata, TPSComponents } from "../types";
|
|
2
|
+
import { buildTimePart } from "../utils/tps-string";
|
|
8
3
|
|
|
9
4
|
/**
|
|
10
5
|
* Gregorian calendar driver.
|
|
11
|
-
*
|
|
12
|
-
* and provides implementations for the full `CalendarDriver` interface.
|
|
13
|
-
* The driver also implements the optional helpers, enabling unit tests to
|
|
14
|
-
* exercise `parseDate`, `format`, `validate`, and `getMetadata`.
|
|
6
|
+
* Supports robust validation and canonical Date conversions.
|
|
15
7
|
*/
|
|
16
8
|
export class GregorianDriver implements CalendarDriver {
|
|
17
9
|
readonly code: string = "greg";
|
|
@@ -54,16 +46,13 @@ export class GregorianDriver implements CalendarDriver {
|
|
|
54
46
|
|
|
55
47
|
getFromDate(date: Date): string {
|
|
56
48
|
const comp = this.getComponentsFromDate(date) as TPSComponents;
|
|
57
|
-
|
|
58
|
-
return TPS.buildTimePart(comp);
|
|
49
|
+
return buildTimePart(comp);
|
|
59
50
|
}
|
|
60
51
|
|
|
61
52
|
// --- optional helpers --------------------------------------------------
|
|
62
53
|
|
|
63
54
|
parseDate(input: string, format?: string): Partial<TPSComponents> {
|
|
64
|
-
// Accept ISO-like formats: "YYYY-MM-DD" and optionally time portion
|
|
65
55
|
const s = input.trim();
|
|
66
|
-
// simple regex - not exhaustive
|
|
67
56
|
const m = s.match(
|
|
68
57
|
/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?$/,
|
|
69
58
|
);
|
|
@@ -95,7 +84,6 @@ export class GregorianDriver implements CalendarDriver {
|
|
|
95
84
|
}
|
|
96
85
|
|
|
97
86
|
format(components: Partial<TPSComponents>, format?: string): string {
|
|
98
|
-
// For simplicity we ignore `format` and always produce ISO-ish string
|
|
99
87
|
const y =
|
|
100
88
|
components.year !== undefined
|
|
101
89
|
? String(components.year).padStart(4, "0")
|
|
@@ -136,24 +124,38 @@ export class GregorianDriver implements CalendarDriver {
|
|
|
136
124
|
return out;
|
|
137
125
|
}
|
|
138
126
|
|
|
127
|
+
private isLeap(y: number): boolean {
|
|
128
|
+
return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
139
131
|
validate(input: string | Partial<TPSComponents>): boolean {
|
|
140
132
|
if (typeof input === "string") {
|
|
141
|
-
// basic ISO date with optional time and fractional seconds
|
|
142
133
|
return /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?)?$/.test(
|
|
143
134
|
input.trim(),
|
|
144
135
|
);
|
|
145
136
|
}
|
|
146
137
|
if (typeof input === "object") {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
138
|
+
const y = input.year ?? 0;
|
|
139
|
+
const m = input.month ?? 1;
|
|
140
|
+
const d = input.day ?? 1;
|
|
141
|
+
|
|
142
|
+
if (y < 0 || m < 1 || m > 12 || d < 1) return false;
|
|
143
|
+
|
|
144
|
+
const daysInMonth = [
|
|
145
|
+
31,
|
|
146
|
+
this.isLeap(y) ? 29 : 28,
|
|
147
|
+
31,
|
|
148
|
+
30,
|
|
149
|
+
31,
|
|
150
|
+
30,
|
|
151
|
+
31,
|
|
152
|
+
31,
|
|
153
|
+
30,
|
|
154
|
+
31,
|
|
155
|
+
30,
|
|
156
|
+
31,
|
|
157
|
+
];
|
|
158
|
+
return d <= daysInMonth[m - 1];
|
|
157
159
|
}
|
|
158
160
|
return false;
|
|
159
161
|
}
|
package/src/drivers/hijri.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* This uses the Tabular Islamic Calendar (civil/Type II-A) algorithm
|
|
11
11
|
* based on the formulas from Meeus "Astronomical Algorithms".
|
|
12
12
|
*/
|
|
13
|
-
import { CalendarDriver, CalendarMetadata, TPSComponents
|
|
13
|
+
import { CalendarDriver, CalendarMetadata, TPSComponents } from "../types";
|
|
14
|
+
import { buildTimePart } from "../utils/tps-string";
|
|
15
|
+
import { gregorianToHijri, hijriToGregorian } from "../utils/calendar";
|
|
14
16
|
|
|
15
17
|
export class HijriDriver implements CalendarDriver {
|
|
16
18
|
readonly code = "hij";
|
|
@@ -56,15 +58,8 @@ export class HijriDriver implements CalendarDriver {
|
|
|
56
58
|
"as-Sabt",
|
|
57
59
|
];
|
|
58
60
|
|
|
59
|
-
/** Leap years in a 30-year cycle (civil / Type II-A pattern) */
|
|
60
|
-
private readonly LEAP_YEARS_IN_CYCLE = new Set([
|
|
61
|
-
2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29,
|
|
62
|
-
]);
|
|
63
|
-
|
|
64
|
-
// ── CalendarDriver interface ──────────────────────────────────────────
|
|
65
|
-
|
|
66
61
|
getComponentsFromDate(date: Date): Partial<TPSComponents> {
|
|
67
|
-
const { hy, hm, hd } =
|
|
62
|
+
const { hy, hm, hd } = gregorianToHijri(
|
|
68
63
|
date.getUTCFullYear(),
|
|
69
64
|
date.getUTCMonth() + 1,
|
|
70
65
|
date.getUTCDate(),
|
|
@@ -96,7 +91,7 @@ export class HijriDriver implements CalendarDriver {
|
|
|
96
91
|
}
|
|
97
92
|
const hm = components.month ?? 1;
|
|
98
93
|
const hd = components.day ?? 1;
|
|
99
|
-
const { gy, gm, gd } =
|
|
94
|
+
const { gy, gm, gd } = hijriToGregorian(hy, hm, hd);
|
|
100
95
|
|
|
101
96
|
return new Date(
|
|
102
97
|
Date.UTC(
|
|
@@ -113,7 +108,7 @@ export class HijriDriver implements CalendarDriver {
|
|
|
113
108
|
|
|
114
109
|
getFromDate(date: Date): string {
|
|
115
110
|
const comp = this.getComponentsFromDate(date) as TPSComponents;
|
|
116
|
-
return
|
|
111
|
+
return buildTimePart(comp);
|
|
117
112
|
}
|
|
118
113
|
|
|
119
114
|
parseDate(input: string, format?: string): Partial<TPSComponents> {
|
|
@@ -166,9 +161,8 @@ export class HijriDriver implements CalendarDriver {
|
|
|
166
161
|
fullYear = components.year ?? 0;
|
|
167
162
|
}
|
|
168
163
|
|
|
169
|
-
if (format === "short")
|
|
164
|
+
if (format === "short")
|
|
170
165
|
return `${components.day}/${pad(components.month)}/${fullYear}`;
|
|
171
|
-
}
|
|
172
166
|
if (format === "long") {
|
|
173
167
|
const mn = this.MONTH_NAMES[(components.month ?? 1) - 1];
|
|
174
168
|
return `${components.day} ${mn} ${fullYear}`;
|
|
@@ -192,7 +186,6 @@ export class HijriDriver implements CalendarDriver {
|
|
|
192
186
|
comp = input;
|
|
193
187
|
}
|
|
194
188
|
const { year, month, day } = comp;
|
|
195
|
-
// Reconstruct full year for leap check
|
|
196
189
|
let fullYear: number;
|
|
197
190
|
if (comp.millennium !== undefined) {
|
|
198
191
|
fullYear =
|
|
@@ -205,7 +198,12 @@ export class HijriDriver implements CalendarDriver {
|
|
|
205
198
|
if (fullYear < 1) return false;
|
|
206
199
|
if (!month || month < 1 || month > 12) return false;
|
|
207
200
|
if (!day || day < 1) return false;
|
|
208
|
-
|
|
201
|
+
|
|
202
|
+
// leap check (cycle of 30 years)
|
|
203
|
+
const isLeap = new Set([2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29]).has(
|
|
204
|
+
((fullYear - 1) % 30) + 1,
|
|
205
|
+
);
|
|
206
|
+
const maxDays = month === 12 && isLeap ? 30 : month % 2 === 1 ? 30 : 29;
|
|
209
207
|
return day <= maxDays;
|
|
210
208
|
}
|
|
211
209
|
|
|
@@ -221,102 +219,4 @@ export class HijriDriver implements CalendarDriver {
|
|
|
221
219
|
epochYear: 1,
|
|
222
220
|
};
|
|
223
221
|
}
|
|
224
|
-
|
|
225
|
-
// ── Internal helpers ──────────────────────────────────────────────────
|
|
226
|
-
|
|
227
|
-
private isLeapYear(year: number): boolean {
|
|
228
|
-
return this.LEAP_YEARS_IN_CYCLE.has(((year - 1) % 30) + 1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
private daysInMonth(year: number, month: number): number {
|
|
232
|
-
if (month === 12 && this.isLeapYear(year)) return 30;
|
|
233
|
-
return month % 2 === 1 ? 30 : 29;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// ── Gregorian ↔ Hijri (Tabular algorithm from Meeus) ──────────────────
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Convert Gregorian to Hijri (Tabular Islamic Calendar).
|
|
240
|
-
* Algorithm from "Astronomical Algorithms" by Jean Meeus.
|
|
241
|
-
*/
|
|
242
|
-
private gregorianToHijri(
|
|
243
|
-
gy: number,
|
|
244
|
-
gm: number,
|
|
245
|
-
gd: number,
|
|
246
|
-
): { hy: number; hm: number; hd: number } {
|
|
247
|
-
// Step 1: Gregorian → JDN
|
|
248
|
-
const jdn = this.gregorianToJdn(gy, gm, gd);
|
|
249
|
-
// Step 2: JDN → Hijri
|
|
250
|
-
// L = JDN − 1948440 + 10632
|
|
251
|
-
const L = jdn - 1948440 + 10632;
|
|
252
|
-
// N = floor((L − 1) / 10631)
|
|
253
|
-
const N = Math.floor((L - 1) / 10631);
|
|
254
|
-
// L = L − 10631 × N + 354
|
|
255
|
-
const L2 = L - 10631 * N + 354;
|
|
256
|
-
// J = floor((10985 − L2) / 5316) × floor((50×L2) / 17719) + floor(L2 / 5670) × floor((43×L2) / 15238)
|
|
257
|
-
const J =
|
|
258
|
-
Math.floor((10985 - L2) / 5316) * Math.floor((50 * L2) / 17719) +
|
|
259
|
-
Math.floor(L2 / 5670) * Math.floor((43 * L2) / 15238);
|
|
260
|
-
// L3 = L2 − floor((30 − J) / 15) × floor((17719 × J) / 50) − floor(J / 16) × floor((15238 × J) / 43) + 29
|
|
261
|
-
const L3 =
|
|
262
|
-
L2 -
|
|
263
|
-
Math.floor((30 - J) / 15) * Math.floor((17719 * J) / 50) -
|
|
264
|
-
Math.floor(J / 16) * Math.floor((15238 * J) / 43) +
|
|
265
|
-
29;
|
|
266
|
-
const hm = Math.floor((24 * L3) / 709);
|
|
267
|
-
const hd = L3 - Math.floor((709 * hm) / 24);
|
|
268
|
-
const hy = 30 * N + J - 30;
|
|
269
|
-
return { hy, hm, hd };
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Convert Hijri to Gregorian.
|
|
274
|
-
*/
|
|
275
|
-
private hijriToGregorian(
|
|
276
|
-
hy: number,
|
|
277
|
-
hm: number,
|
|
278
|
-
hd: number,
|
|
279
|
-
): { gy: number; gm: number; gd: number } {
|
|
280
|
-
// Hijri → JDN
|
|
281
|
-
const jdn =
|
|
282
|
-
Math.floor((11 * hy + 3) / 30) +
|
|
283
|
-
354 * hy +
|
|
284
|
-
30 * hm -
|
|
285
|
-
Math.floor((hm - 1) / 2) +
|
|
286
|
-
hd +
|
|
287
|
-
1948440 -
|
|
288
|
-
385;
|
|
289
|
-
// JDN → Gregorian
|
|
290
|
-
return this.jdnToGregorian(jdn);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// ── JDN helpers ───────────────────────────────────────────────────────
|
|
294
|
-
|
|
295
|
-
private gregorianToJdn(gy: number, gm: number, gd: number): number {
|
|
296
|
-
const a = Math.floor((14 - gm) / 12);
|
|
297
|
-
const y = gy + 4800 - a;
|
|
298
|
-
const m = gm + 12 * a - 3;
|
|
299
|
-
return (
|
|
300
|
-
gd +
|
|
301
|
-
Math.floor((153 * m + 2) / 5) +
|
|
302
|
-
365 * y +
|
|
303
|
-
Math.floor(y / 4) -
|
|
304
|
-
Math.floor(y / 100) +
|
|
305
|
-
Math.floor(y / 400) -
|
|
306
|
-
32045
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private jdnToGregorian(jdn: number): { gy: number; gm: number; gd: number } {
|
|
311
|
-
const a = jdn + 32044;
|
|
312
|
-
const b = Math.floor((4 * a + 3) / 146097);
|
|
313
|
-
const c = a - Math.floor((146097 * b) / 4);
|
|
314
|
-
const d = Math.floor((4 * c + 3) / 1461);
|
|
315
|
-
const e = c - Math.floor((1461 * d) / 4);
|
|
316
|
-
const m = Math.floor((5 * e + 2) / 153);
|
|
317
|
-
const gd = e - Math.floor((153 * m + 2) / 5) + 1;
|
|
318
|
-
const gm = m + 3 - 12 * Math.floor(m / 10);
|
|
319
|
-
const gy = 100 * b + d - 4800 + Math.floor(m / 10);
|
|
320
|
-
return { gy, gm, gd };
|
|
321
|
-
}
|
|
322
222
|
}
|
package/src/drivers/holocene.ts
CHANGED
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This is a thin wrapper around GregorianDriver with a year offset.
|
|
11
11
|
*/
|
|
12
|
-
import { CalendarDriver, CalendarMetadata, TPSComponents
|
|
12
|
+
import { CalendarDriver, CalendarMetadata, TPSComponents } from "../types";
|
|
13
|
+
import { buildTimePart } from "../utils/tps-string";
|
|
13
14
|
import { GregorianDriver } from "./gregorian";
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Holocene (Human Era) Calendar Driver
|
|
18
|
+
*/
|
|
15
19
|
export class HoloceneDriver implements CalendarDriver {
|
|
16
20
|
readonly code = "holo";
|
|
17
21
|
readonly name = "Holocene (Human Era)";
|
|
@@ -19,8 +23,6 @@ export class HoloceneDriver implements CalendarDriver {
|
|
|
19
23
|
private readonly gregorian = new GregorianDriver();
|
|
20
24
|
private readonly YEAR_OFFSET = 10000;
|
|
21
25
|
|
|
22
|
-
// ── CalendarDriver interface ──────────────────────────────────────────
|
|
23
|
-
|
|
24
26
|
getComponentsFromDate(date: Date): Partial<TPSComponents> {
|
|
25
27
|
const greg = this.gregorian.getComponentsFromDate(date);
|
|
26
28
|
const fullYear = date.getUTCFullYear() + this.YEAR_OFFSET;
|
|
@@ -57,21 +59,20 @@ export class HoloceneDriver implements CalendarDriver {
|
|
|
57
59
|
|
|
58
60
|
getFromDate(date: Date): string {
|
|
59
61
|
const comp = this.getComponentsFromDate(date) as TPSComponents;
|
|
60
|
-
return
|
|
62
|
+
return buildTimePart(comp);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
parseDate(input: string,
|
|
64
|
-
// Accept ISO-like: "12026-01-09" (Holocene year)
|
|
65
|
+
parseDate(input: string, _format?: string): Partial<TPSComponents> {
|
|
65
66
|
const m = input
|
|
66
67
|
.trim()
|
|
67
68
|
.match(
|
|
68
69
|
/^(\d{4,5})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?$/,
|
|
69
70
|
);
|
|
70
|
-
if (!m)
|
|
71
|
+
if (!m)
|
|
71
72
|
throw new Error(
|
|
72
73
|
`HoloceneDriver.parseDate: unsupported format "${input}"`,
|
|
73
74
|
);
|
|
74
|
-
|
|
75
|
+
|
|
75
76
|
const result: Partial<TPSComponents> = {
|
|
76
77
|
calendar: this.code,
|
|
77
78
|
year: parseInt(m[1], 10),
|
|
@@ -86,9 +87,8 @@ export class HoloceneDriver implements CalendarDriver {
|
|
|
86
87
|
return result;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
format(components: Partial<TPSComponents>,
|
|
90
|
+
format(components: Partial<TPSComponents>, _format?: string): string {
|
|
90
91
|
const pad = (n?: number, w = 2) => String(n ?? 0).padStart(w, "0");
|
|
91
|
-
// Reconstruct full Holocene year from components
|
|
92
92
|
let holoYear: number;
|
|
93
93
|
if (components.millennium !== undefined) {
|
|
94
94
|
const m = components.millennium ?? 0;
|
|
@@ -117,7 +117,6 @@ export class HoloceneDriver implements CalendarDriver {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
if (typeof input === "object") {
|
|
120
|
-
// Delegate day/month validation to Gregorian (same structure)
|
|
121
120
|
return this.gregorian.validate({
|
|
122
121
|
year: input.year,
|
|
123
122
|
month: input.month,
|
|
@@ -146,7 +145,7 @@ export class HoloceneDriver implements CalendarDriver {
|
|
|
146
145
|
],
|
|
147
146
|
isLunar: false,
|
|
148
147
|
monthsPerYear: 12,
|
|
149
|
-
epochYear: -10000,
|
|
148
|
+
epochYear: -10000,
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
151
|
}
|
package/src/drivers/julian.ts
CHANGED
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Conversion uses Julian Day Number algorithms.
|
|
11
11
|
*/
|
|
12
|
-
import { CalendarDriver, CalendarMetadata, TPSComponents
|
|
12
|
+
import { CalendarDriver, CalendarMetadata, TPSComponents } from "../types";
|
|
13
|
+
import { buildTimePart } from "../utils/tps-string";
|
|
14
|
+
import {
|
|
15
|
+
gregorianToJdn,
|
|
16
|
+
jdnToGregorian,
|
|
17
|
+
julianToJdn,
|
|
18
|
+
jdnToJulian,
|
|
19
|
+
} from "../utils/calendar";
|
|
13
20
|
|
|
14
21
|
export class JulianDriver implements CalendarDriver {
|
|
15
22
|
readonly code = "jul";
|
|
@@ -49,15 +56,13 @@ export class JulianDriver implements CalendarDriver {
|
|
|
49
56
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
|
|
50
57
|
];
|
|
51
58
|
|
|
52
|
-
// ── CalendarDriver interface ──────────────────────────────────────────
|
|
53
|
-
|
|
54
59
|
getComponentsFromDate(date: Date): Partial<TPSComponents> {
|
|
55
|
-
const jdn =
|
|
60
|
+
const jdn = gregorianToJdn(
|
|
56
61
|
date.getUTCFullYear(),
|
|
57
62
|
date.getUTCMonth() + 1,
|
|
58
63
|
date.getUTCDate(),
|
|
59
64
|
);
|
|
60
|
-
const { jy, jm, jd } =
|
|
65
|
+
const { jy, jm, jd } = jdnToJulian(jdn);
|
|
61
66
|
|
|
62
67
|
return {
|
|
63
68
|
calendar: this.code,
|
|
@@ -85,8 +90,8 @@ export class JulianDriver implements CalendarDriver {
|
|
|
85
90
|
}
|
|
86
91
|
const jm = components.month ?? 1;
|
|
87
92
|
const jd = components.day ?? 1;
|
|
88
|
-
const jdn =
|
|
89
|
-
const { gy, gm, gd } =
|
|
93
|
+
const jdn = julianToJdn(fullYear, jm, jd);
|
|
94
|
+
const { gy, gm, gd } = jdnToGregorian(jdn);
|
|
90
95
|
|
|
91
96
|
return new Date(
|
|
92
97
|
Date.UTC(
|
|
@@ -103,17 +108,17 @@ export class JulianDriver implements CalendarDriver {
|
|
|
103
108
|
|
|
104
109
|
getFromDate(date: Date): string {
|
|
105
110
|
const comp = this.getComponentsFromDate(date) as TPSComponents;
|
|
106
|
-
return
|
|
111
|
+
return buildTimePart(comp);
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
parseDate(input: string,
|
|
114
|
+
parseDate(input: string, _format?: string): Partial<TPSComponents> {
|
|
110
115
|
const trimmed = input.trim();
|
|
111
116
|
const m = trimmed.match(
|
|
112
117
|
/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?$/,
|
|
113
118
|
);
|
|
114
|
-
if (!m)
|
|
119
|
+
if (!m)
|
|
115
120
|
throw new Error(`JulianDriver.parseDate: unsupported format "${input}"`);
|
|
116
|
-
|
|
121
|
+
|
|
117
122
|
const fullYear = parseInt(m[1], 10);
|
|
118
123
|
const result: Partial<TPSComponents> = {
|
|
119
124
|
calendar: this.code,
|
|
@@ -131,7 +136,7 @@ export class JulianDriver implements CalendarDriver {
|
|
|
131
136
|
return result;
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
format(components: Partial<TPSComponents>,
|
|
139
|
+
format(components: Partial<TPSComponents>, _format?: string): string {
|
|
135
140
|
const pad = (n?: number, w = 2) => String(n ?? 0).padStart(w, "0");
|
|
136
141
|
let fullYear: number;
|
|
137
142
|
if (components.millennium !== undefined) {
|
|
@@ -164,7 +169,6 @@ export class JulianDriver implements CalendarDriver {
|
|
|
164
169
|
);
|
|
165
170
|
}
|
|
166
171
|
if (typeof input === "object") {
|
|
167
|
-
// Reconstruct full year for leap check
|
|
168
172
|
let fullYear: number;
|
|
169
173
|
if (input.millennium !== undefined) {
|
|
170
174
|
fullYear =
|
|
@@ -178,7 +182,7 @@ export class JulianDriver implements CalendarDriver {
|
|
|
178
182
|
if (month === undefined || day === undefined) return false;
|
|
179
183
|
if (month < 1 || month > 12 || day < 1) return false;
|
|
180
184
|
let maxDay = this.DAYS_IN_MONTH[month - 1];
|
|
181
|
-
if (month === 2 &&
|
|
185
|
+
if (month === 2 && fullYear % 4 === 0) maxDay = 29;
|
|
182
186
|
return day <= maxDay;
|
|
183
187
|
}
|
|
184
188
|
return false;
|
|
@@ -194,62 +198,4 @@ export class JulianDriver implements CalendarDriver {
|
|
|
194
198
|
epochYear: 1,
|
|
195
199
|
};
|
|
196
200
|
}
|
|
197
|
-
|
|
198
|
-
// ── Internal helpers ──────────────────────────────────────────────────
|
|
199
|
-
|
|
200
|
-
/** Julian leap year: every 4 years, no century exception */
|
|
201
|
-
private isLeapYear(year: number): boolean {
|
|
202
|
-
return year % 4 === 0;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// ── JDN algorithms ────────────────────────────────────────────────────
|
|
206
|
-
|
|
207
|
-
private julianToJdn(jy: number, jm: number, jd: number): number {
|
|
208
|
-
const a = Math.floor((14 - jm) / 12);
|
|
209
|
-
const y = jy + 4800 - a;
|
|
210
|
-
const m = jm + 12 * a - 3;
|
|
211
|
-
return (
|
|
212
|
-
jd + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - 32083
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private jdnToJulian(jdn: number): { jy: number; jm: number; jd: number } {
|
|
217
|
-
const c = jdn + 32082;
|
|
218
|
-
const d = Math.floor((4 * c + 3) / 1461);
|
|
219
|
-
const e = c - Math.floor((1461 * d) / 4);
|
|
220
|
-
const m = Math.floor((5 * e + 2) / 153);
|
|
221
|
-
const jd = e - Math.floor((153 * m + 2) / 5) + 1;
|
|
222
|
-
const jm = m + 3 - 12 * Math.floor(m / 10);
|
|
223
|
-
const jy = d - 4800 + Math.floor(m / 10);
|
|
224
|
-
return { jy, jm, jd };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** Gregorian → JDN (for converting incoming Gregorian Date) */
|
|
228
|
-
private gregorianToJdn(gy: number, gm: number, gd: number): number {
|
|
229
|
-
const a = Math.floor((14 - gm) / 12);
|
|
230
|
-
const y = gy + 4800 - a;
|
|
231
|
-
const m = gm + 12 * a - 3;
|
|
232
|
-
return (
|
|
233
|
-
gd +
|
|
234
|
-
Math.floor((153 * m + 2) / 5) +
|
|
235
|
-
365 * y +
|
|
236
|
-
Math.floor(y / 4) -
|
|
237
|
-
Math.floor(y / 100) +
|
|
238
|
-
Math.floor(y / 400) -
|
|
239
|
-
32045
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private jdnToGregorian(jdn: number): { gy: number; gm: number; gd: number } {
|
|
244
|
-
const a = jdn + 32044;
|
|
245
|
-
const b = Math.floor((4 * a + 3) / 146097);
|
|
246
|
-
const c = a - Math.floor((146097 * b) / 4);
|
|
247
|
-
const d = Math.floor((4 * c + 3) / 1461);
|
|
248
|
-
const e = c - Math.floor((1461 * d) / 4);
|
|
249
|
-
const m = Math.floor((5 * e + 2) / 153);
|
|
250
|
-
const gd = e - Math.floor((153 * m + 2) / 5) + 1;
|
|
251
|
-
const gm = m + 3 - 12 * Math.floor(m / 10);
|
|
252
|
-
const gy = 100 * b + d - 4800 + Math.floor(m / 10);
|
|
253
|
-
return { gy, gm, gd };
|
|
254
|
-
}
|
|
255
201
|
}
|