@optique/temporal 1.0.0-dev.921 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -46,8 +46,15 @@ function instant(options = {}) {
46
46
  const metavar = options.metavar ?? "TIMESTAMP";
47
47
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
48
48
  return {
49
- $mode: "sync",
49
+ mode: "sync",
50
50
  metavar,
51
+ get placeholder() {
52
+ try {
53
+ return Temporal.Instant.from("1970-01-01T00:00:00Z");
54
+ } catch {
55
+ return void 0;
56
+ }
57
+ },
51
58
  parse(input) {
52
59
  ensureTemporal();
53
60
  try {
@@ -86,8 +93,15 @@ function duration(options = {}) {
86
93
  const metavar = options.metavar ?? "DURATION";
87
94
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
88
95
  return {
89
- $mode: "sync",
96
+ mode: "sync",
90
97
  metavar,
98
+ get placeholder() {
99
+ try {
100
+ return Temporal.Duration.from("PT0S");
101
+ } catch {
102
+ return void 0;
103
+ }
104
+ },
91
105
  parse(input) {
92
106
  ensureTemporal();
93
107
  try {
@@ -125,8 +139,15 @@ function zonedDateTime(options = {}) {
125
139
  const metavar = options.metavar ?? "ZONED_DATETIME";
126
140
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
127
141
  return {
128
- $mode: "sync",
142
+ mode: "sync",
129
143
  metavar,
144
+ get placeholder() {
145
+ try {
146
+ return Temporal.ZonedDateTime.from("1970-01-01T00:00:00+00:00[UTC]");
147
+ } catch {
148
+ return void 0;
149
+ }
150
+ },
130
151
  parse(input) {
131
152
  ensureTemporal();
132
153
  try {
@@ -148,6 +169,64 @@ function zonedDateTime(options = {}) {
148
169
  };
149
170
  }
150
171
  /**
172
+ * Optional RFC 9557 calendar annotation suffix, e.g. `[u-ca=gregory]`.
173
+ * Accepts case-insensitive values and the optional critical flag (`!`).
174
+ * Used by all plain Temporal regexes to accept `toString()` output for
175
+ * non-ISO calendars.
176
+ */
177
+ const CALENDAR_ANNOTATION = String.raw`(\[!?u-ca=[a-zA-Z0-9\-]+\])?`;
178
+ /**
179
+ * Required RFC 9557 calendar annotation (non-optional variant used when the
180
+ * annotation must be present, e.g. for reference-day/year forms).
181
+ */
182
+ const CALENDAR_ANNOTATION_REQUIRED = String.raw`\[!?u-ca=[a-zA-Z0-9\-]+\]`;
183
+ /**
184
+ * Year portion: either 4 digits (`YYYY`) or a sign-prefixed 6-digit expanded
185
+ * year (`+YYYYYY` / `-YYYYYY`).
186
+ */
187
+ const YEAR = String.raw`([+-]\d{6}|\d{4})`;
188
+ /** ISO 8601 fractional seconds with `.` or `,` separator. */
189
+ const FRACTIONAL = String.raw`[.,]\d+`;
190
+ /** Extended date: `YYYY-MM-DD` or `±YYYYYY-MM-DD`. */
191
+ const DATE_EXTENDED = `${YEAR}-\\d{2}-\\d{2}`;
192
+ /** Basic date: `YYYYMMDD` or `±YYYYYYMMDD`. */
193
+ const DATE_BASIC = `${YEAR}\\d{4}`;
194
+ /** Extended time: `HH:MM[:SS[.frac]]`. */
195
+ const TIME_EXTENDED = `\\d{2}:\\d{2}(:\\d{2}(${FRACTIONAL})?)?`;
196
+ /** Basic time: `HH`, `HHMM`, or `HHMMSS[.frac]`. */
197
+ const TIME_BASIC = `\\d{2}(\\d{2}(\\d{2}(${FRACTIONAL})?)?)?`;
198
+ /**
199
+ * Matches YYYY-MM-DD (extended) or YYYYMMDD (basic) date forms only (no time
200
+ * component). Both forms accept expanded years and calendar annotations.
201
+ */
202
+ const PLAIN_DATE_RE = /* @__PURE__ */ new RegExp(`^(${DATE_EXTENDED}|${DATE_BASIC})${CALENDAR_ANNOTATION}$`);
203
+ /**
204
+ * Matches time forms only (no date prefix). Accepts extended
205
+ * (`HH:MM[:SS[.frac]]`), basic (`HH`, `HHMM`, `HHMMSS[.frac]`), and
206
+ * `T`-prefixed variants. Calendar annotations are accepted for consistency
207
+ * with `Temporal.PlainTime.from()` on polyfill runtimes.
208
+ */
209
+ const PLAIN_TIME_RE = /* @__PURE__ */ new RegExp(`^[Tt]?(${TIME_EXTENDED}|${TIME_BASIC})${CALENDAR_ANNOTATION}$`);
210
+ /**
211
+ * Matches date-time strings with both date and time parts. Accepts extended,
212
+ * basic, and mixed forms (e.g. `2020-01-23T170436`). The separator may be
213
+ * `T`, `t`, or a space. The time portion may be reduced-precision (hour
214
+ * only).
215
+ */
216
+ const PLAIN_DATETIME_RE = /* @__PURE__ */ new RegExp(`^(${DATE_EXTENDED}|${DATE_BASIC})[Tt ](${TIME_EXTENDED}|${TIME_BASIC})${CALENDAR_ANNOTATION}$`);
217
+ /**
218
+ * Matches YYYY-MM (extended) or YYYYMM (basic), or a full date
219
+ * (extended or basic) with a required calendar annotation (the reference day
220
+ * is emitted by `toString()` for non-ISO calendars).
221
+ */
222
+ const PLAIN_YEAR_MONTH_RE = /* @__PURE__ */ new RegExp(`^(${YEAR}-\\d{2}(${CALENDAR_ANNOTATION}|-\\d{2}${CALENDAR_ANNOTATION_REQUIRED})|${YEAR}\\d{2}(${CALENDAR_ANNOTATION}|\\d{2}${CALENDAR_ANNOTATION_REQUIRED}))$`);
223
+ /**
224
+ * Matches MM-DD, --MM-DD, MMDD, or --MMDD month-day forms, or a full date
225
+ * (extended or basic) with a required calendar annotation (the reference year
226
+ * is emitted by `toString()` for non-ISO calendars).
227
+ */
228
+ const PLAIN_MONTH_DAY_RE = /* @__PURE__ */ new RegExp(`^((--)?(\\d{2}-\\d{2}|\\d{4})${CALENDAR_ANNOTATION}|(${DATE_EXTENDED}|${DATE_BASIC})${CALENDAR_ANNOTATION_REQUIRED})$`);
229
+ /**
151
230
  * Creates a ValueParser for parsing Temporal.PlainDate from ISO 8601 date strings.
152
231
  *
153
232
  * Accepts strings like:
@@ -164,11 +243,19 @@ function plainDate(options = {}) {
164
243
  const metavar = options.metavar ?? "DATE";
165
244
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
166
245
  return {
167
- $mode: "sync",
246
+ mode: "sync",
168
247
  metavar,
248
+ get placeholder() {
249
+ try {
250
+ return Temporal.PlainDate.from("1970-01-01");
251
+ } catch {
252
+ return void 0;
253
+ }
254
+ },
169
255
  parse(input) {
170
256
  ensureTemporal();
171
257
  try {
258
+ if (!PLAIN_DATE_RE.test(input)) throw new RangeError();
172
259
  const value = Temporal.PlainDate.from(input);
173
260
  return {
174
261
  success: true,
@@ -203,11 +290,19 @@ function plainTime(options = {}) {
203
290
  const metavar = options.metavar ?? "TIME";
204
291
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
205
292
  return {
206
- $mode: "sync",
293
+ mode: "sync",
207
294
  metavar,
295
+ get placeholder() {
296
+ try {
297
+ return Temporal.PlainTime.from("00:00:00");
298
+ } catch {
299
+ return void 0;
300
+ }
301
+ },
208
302
  parse(input) {
209
303
  ensureTemporal();
210
304
  try {
305
+ if (!PLAIN_TIME_RE.test(input)) throw new RangeError();
211
306
  const value = Temporal.PlainTime.from(input);
212
307
  return {
213
308
  success: true,
@@ -242,11 +337,19 @@ function plainDateTime(options = {}) {
242
337
  const metavar = options.metavar ?? "DATETIME";
243
338
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
244
339
  return {
245
- $mode: "sync",
340
+ mode: "sync",
246
341
  metavar,
342
+ get placeholder() {
343
+ try {
344
+ return Temporal.PlainDateTime.from("1970-01-01T00:00:00");
345
+ } catch {
346
+ return void 0;
347
+ }
348
+ },
247
349
  parse(input) {
248
350
  ensureTemporal();
249
351
  try {
352
+ if (!PLAIN_DATETIME_RE.test(input)) throw new RangeError();
250
353
  const value = Temporal.PlainDateTime.from(input);
251
354
  return {
252
355
  success: true,
@@ -281,11 +384,19 @@ function plainYearMonth(options = {}) {
281
384
  const metavar = options.metavar ?? "YEAR-MONTH";
282
385
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
283
386
  return {
284
- $mode: "sync",
387
+ mode: "sync",
285
388
  metavar,
389
+ get placeholder() {
390
+ try {
391
+ return Temporal.PlainYearMonth.from("1970-01");
392
+ } catch {
393
+ return void 0;
394
+ }
395
+ },
286
396
  parse(input) {
287
397
  ensureTemporal();
288
398
  try {
399
+ if (!PLAIN_YEAR_MONTH_RE.test(input)) throw new RangeError();
289
400
  const value = Temporal.PlainYearMonth.from(input);
290
401
  return {
291
402
  success: true,
@@ -308,8 +419,8 @@ function plainYearMonth(options = {}) {
308
419
  *
309
420
  * Accepts strings like:
310
421
  *
311
- * - `"--01-23"`
312
- * - `"--12-31"`
422
+ * - `"01-23"`
423
+ * - `"12-31"`
313
424
  *
314
425
  * @param options Configuration options for the plain month-day parser.
315
426
  * @returns A ValueParser that parses strings into Temporal.PlainMonthDay values.
@@ -317,14 +428,22 @@ function plainYearMonth(options = {}) {
317
428
  * at runtime.
318
429
  */
319
430
  function plainMonthDay(options = {}) {
320
- const metavar = options.metavar ?? "--MONTH-DAY";
431
+ const metavar = options.metavar ?? "MONTH-DAY";
321
432
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
322
433
  return {
323
- $mode: "sync",
434
+ mode: "sync",
324
435
  metavar,
436
+ get placeholder() {
437
+ try {
438
+ return Temporal.PlainMonthDay.from("01-01");
439
+ } catch {
440
+ return void 0;
441
+ }
442
+ },
325
443
  parse(input) {
326
444
  ensureTemporal();
327
445
  try {
446
+ if (!PLAIN_MONTH_DAY_RE.test(input)) throw new RangeError();
328
447
  const value = Temporal.PlainMonthDay.from(input);
329
448
  return {
330
449
  success: true,
@@ -333,7 +452,7 @@ function plainMonthDay(options = {}) {
333
452
  } catch {
334
453
  return {
335
454
  success: false,
336
- error: options.errors?.invalidFormat ? typeof options.errors.invalidFormat === "function" ? options.errors.invalidFormat(input) : options.errors.invalidFormat : __optique_core_message.message`Invalid month-day: ${input}. Expected ISO 8601 format like ${"--01-23"}.`
455
+ error: options.errors?.invalidFormat ? typeof options.errors.invalidFormat === "function" ? options.errors.invalidFormat(input) : options.errors.invalidFormat : __optique_core_message.message`Invalid month-day: ${input}. Expected ISO 8601 format like ${"01-23"}.`
337
456
  };
338
457
  }
339
458
  },
@@ -343,6 +462,59 @@ function plainMonthDay(options = {}) {
343
462
  };
344
463
  }
345
464
  /**
465
+ * Single-segment IANA timezone identifiers accepted across all supported
466
+ * runtimes (Deno, Node.js, Bun). This tuple is the single source of truth:
467
+ * {@link SingleSegmentTimeZone} is derived from it, and the runtime lookup
468
+ * {@link singleSegmentTimeZoneLookup} is built from it.
469
+ */
470
+ const singleSegmentTimeZoneList = [
471
+ "UTC",
472
+ "GMT",
473
+ "GMT0",
474
+ "GMT+0",
475
+ "GMT-0",
476
+ "UCT",
477
+ "Universal",
478
+ "Greenwich",
479
+ "Zulu",
480
+ "EST",
481
+ "MST",
482
+ "HST",
483
+ "CET",
484
+ "MET",
485
+ "WET",
486
+ "EET",
487
+ "EST5EDT",
488
+ "CST6CDT",
489
+ "MST7MDT",
490
+ "PST8PDT",
491
+ "Cuba",
492
+ "Egypt",
493
+ "Eire",
494
+ "GB",
495
+ "GB-Eire",
496
+ "Hongkong",
497
+ "Iceland",
498
+ "Iran",
499
+ "Israel",
500
+ "Jamaica",
501
+ "Japan",
502
+ "Kwajalein",
503
+ "Libya",
504
+ "Navajo",
505
+ "NZ",
506
+ "NZ-CHAT",
507
+ "Poland",
508
+ "Portugal",
509
+ "PRC",
510
+ "ROC",
511
+ "ROK",
512
+ "Singapore",
513
+ "Turkey",
514
+ "W-SU"
515
+ ];
516
+ const singleSegmentTimeZoneLookup = new Map(singleSegmentTimeZoneList.map((timeZone$1) => [timeZone$1.toLowerCase(), timeZone$1]));
517
+ /**
346
518
  * Creates a ValueParser for parsing IANA Time Zone Database identifiers.
347
519
  *
348
520
  * Accepts strings like:
@@ -351,6 +523,8 @@ function plainMonthDay(options = {}) {
351
523
  * - `"America/New_York"`
352
524
  * - `"Europe/London"`
353
525
  * - `"UTC"`
526
+ * - `"GMT"`
527
+ * - `"EST"`
354
528
  *
355
529
  * @param options Configuration options for the timezone parser.
356
530
  * @returns A ValueParser that parses and validates timezone identifiers.
@@ -361,8 +535,9 @@ function timeZone(options = {}) {
361
535
  const metavar = options.metavar ?? "TIMEZONE";
362
536
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
363
537
  return {
364
- $mode: "sync",
538
+ mode: "sync",
365
539
  metavar,
540
+ placeholder: "UTC",
366
541
  parse(input) {
367
542
  ensureTemporal();
368
543
  try {
@@ -372,6 +547,14 @@ function timeZone(options = {}) {
372
547
  day: 1,
373
548
  timeZone: input
374
549
  });
550
+ if (!input.includes("/")) {
551
+ const canonical = singleSegmentTimeZoneLookup.get(input.toLowerCase());
552
+ if (canonical == null) throw new RangeError();
553
+ return {
554
+ success: true,
555
+ value: canonical
556
+ };
557
+ }
375
558
  return {
376
559
  success: true,
377
560
  value: input
package/dist/index.d.cts CHANGED
@@ -11,17 +11,21 @@ import { NonEmptyString } from "@optique/core/nonempty";
11
11
  * convention:
12
12
  *
13
13
  * - Two-level: `"Asia/Seoul"`, `"America/New_York"`, `"Europe/London"`
14
- * - Three-level: `"America/Argentina/Buenos_Aires"`, `"America/Kentucky/Louisville"`
15
- * - Special case: `"UTC"`
14
+ * - Three-level: `"America/Argentina/Buenos_Aires"`,
15
+ * `"America/Kentucky/Louisville"`
16
+ * - Standard single-segment: `"UTC"`, `"GMT"`, `"Universal"`
17
+ * - POSIX abbreviations: `"EST"`, `"CET"`, `"EST5EDT"`
18
+ * - Deprecated aliases: `"Japan"`, `"Singapore"`, `"Cuba"`
16
19
  *
17
20
  * @example
18
21
  * ```typescript
19
22
  * const seoul: TimeZone = "Asia/Seoul";
20
23
  * const utc: TimeZone = "UTC";
24
+ * const gmt: TimeZone = "GMT";
21
25
  * const buenosAires: TimeZone = "America/Argentina/Buenos_Aires";
22
26
  * ```
23
27
  */
24
- type TimeZone = `${string}/${string}/${string}` | `${string}/${string}` | "UTC";
28
+ type TimeZone = `${string}/${string}/${string}` | `${string}/${string}` | SingleSegmentTimeZone;
25
29
  /**
26
30
  * Options for creating an instant parser.
27
31
  */
@@ -197,8 +201,8 @@ interface PlainMonthDayOptions {
197
201
  /**
198
202
  * The metavariable name for this parser. This is used in help messages to
199
203
  * indicate what kind of value this parser expects. Usually a single
200
- * word in uppercase, like `--MONTH-DAY`.
201
- * @default `"--MONTH-DAY"`
204
+ * word in uppercase, like `MONTH-DAY`.
205
+ * @default `"MONTH-DAY"`
202
206
  */
203
207
  readonly metavar?: NonEmptyString;
204
208
  /**
@@ -342,8 +346,8 @@ declare function plainYearMonth(options?: PlainYearMonthOptions): ValueParser<"s
342
346
  *
343
347
  * Accepts strings like:
344
348
  *
345
- * - `"--01-23"`
346
- * - `"--12-31"`
349
+ * - `"01-23"`
350
+ * - `"12-31"`
347
351
  *
348
352
  * @param options Configuration options for the plain month-day parser.
349
353
  * @returns A ValueParser that parses strings into Temporal.PlainMonthDay values.
@@ -351,6 +355,14 @@ declare function plainYearMonth(options?: PlainYearMonthOptions): ValueParser<"s
351
355
  * at runtime.
352
356
  */
353
357
  declare function plainMonthDay(options?: PlainMonthDayOptions): ValueParser<"sync", Temporal.PlainMonthDay>;
358
+ /**
359
+ * Single-segment IANA timezone identifiers accepted across all supported
360
+ * runtimes (Deno, Node.js, Bun). This tuple is the single source of truth:
361
+ * {@link SingleSegmentTimeZone} is derived from it, and the runtime lookup
362
+ * {@link singleSegmentTimeZoneLookup} is built from it.
363
+ */
364
+ declare const singleSegmentTimeZoneList: readonly ["UTC", "GMT", "GMT0", "GMT+0", "GMT-0", "UCT", "Universal", "Greenwich", "Zulu", "EST", "MST", "HST", "CET", "MET", "WET", "EET", "EST5EDT", "CST6CDT", "MST7MDT", "PST8PDT", "Cuba", "Egypt", "Eire", "GB", "GB-Eire", "Hongkong", "Iceland", "Iran", "Israel", "Jamaica", "Japan", "Kwajalein", "Libya", "Navajo", "NZ", "NZ-CHAT", "Poland", "Portugal", "PRC", "ROC", "ROK", "Singapore", "Turkey", "W-SU"];
365
+ type SingleSegmentTimeZone = typeof singleSegmentTimeZoneList[number];
354
366
  /**
355
367
  * Creates a ValueParser for parsing IANA Time Zone Database identifiers.
356
368
  *
@@ -360,6 +372,8 @@ declare function plainMonthDay(options?: PlainMonthDayOptions): ValueParser<"syn
360
372
  * - `"America/New_York"`
361
373
  * - `"Europe/London"`
362
374
  * - `"UTC"`
375
+ * - `"GMT"`
376
+ * - `"EST"`
363
377
  *
364
378
  * @param options Configuration options for the timezone parser.
365
379
  * @returns A ValueParser that parses and validates timezone identifiers.
package/dist/index.d.ts CHANGED
@@ -12,17 +12,21 @@ import { ValueParser } from "@optique/core/valueparser";
12
12
  * convention:
13
13
  *
14
14
  * - Two-level: `"Asia/Seoul"`, `"America/New_York"`, `"Europe/London"`
15
- * - Three-level: `"America/Argentina/Buenos_Aires"`, `"America/Kentucky/Louisville"`
16
- * - Special case: `"UTC"`
15
+ * - Three-level: `"America/Argentina/Buenos_Aires"`,
16
+ * `"America/Kentucky/Louisville"`
17
+ * - Standard single-segment: `"UTC"`, `"GMT"`, `"Universal"`
18
+ * - POSIX abbreviations: `"EST"`, `"CET"`, `"EST5EDT"`
19
+ * - Deprecated aliases: `"Japan"`, `"Singapore"`, `"Cuba"`
17
20
  *
18
21
  * @example
19
22
  * ```typescript
20
23
  * const seoul: TimeZone = "Asia/Seoul";
21
24
  * const utc: TimeZone = "UTC";
25
+ * const gmt: TimeZone = "GMT";
22
26
  * const buenosAires: TimeZone = "America/Argentina/Buenos_Aires";
23
27
  * ```
24
28
  */
25
- type TimeZone = `${string}/${string}/${string}` | `${string}/${string}` | "UTC";
29
+ type TimeZone = `${string}/${string}/${string}` | `${string}/${string}` | SingleSegmentTimeZone;
26
30
  /**
27
31
  * Options for creating an instant parser.
28
32
  */
@@ -198,8 +202,8 @@ interface PlainMonthDayOptions {
198
202
  /**
199
203
  * The metavariable name for this parser. This is used in help messages to
200
204
  * indicate what kind of value this parser expects. Usually a single
201
- * word in uppercase, like `--MONTH-DAY`.
202
- * @default `"--MONTH-DAY"`
205
+ * word in uppercase, like `MONTH-DAY`.
206
+ * @default `"MONTH-DAY"`
203
207
  */
204
208
  readonly metavar?: NonEmptyString;
205
209
  /**
@@ -343,8 +347,8 @@ declare function plainYearMonth(options?: PlainYearMonthOptions): ValueParser<"s
343
347
  *
344
348
  * Accepts strings like:
345
349
  *
346
- * - `"--01-23"`
347
- * - `"--12-31"`
350
+ * - `"01-23"`
351
+ * - `"12-31"`
348
352
  *
349
353
  * @param options Configuration options for the plain month-day parser.
350
354
  * @returns A ValueParser that parses strings into Temporal.PlainMonthDay values.
@@ -352,6 +356,14 @@ declare function plainYearMonth(options?: PlainYearMonthOptions): ValueParser<"s
352
356
  * at runtime.
353
357
  */
354
358
  declare function plainMonthDay(options?: PlainMonthDayOptions): ValueParser<"sync", Temporal.PlainMonthDay>;
359
+ /**
360
+ * Single-segment IANA timezone identifiers accepted across all supported
361
+ * runtimes (Deno, Node.js, Bun). This tuple is the single source of truth:
362
+ * {@link SingleSegmentTimeZone} is derived from it, and the runtime lookup
363
+ * {@link singleSegmentTimeZoneLookup} is built from it.
364
+ */
365
+ declare const singleSegmentTimeZoneList: readonly ["UTC", "GMT", "GMT0", "GMT+0", "GMT-0", "UCT", "Universal", "Greenwich", "Zulu", "EST", "MST", "HST", "CET", "MET", "WET", "EET", "EST5EDT", "CST6CDT", "MST7MDT", "PST8PDT", "Cuba", "Egypt", "Eire", "GB", "GB-Eire", "Hongkong", "Iceland", "Iran", "Israel", "Jamaica", "Japan", "Kwajalein", "Libya", "Navajo", "NZ", "NZ-CHAT", "Poland", "Portugal", "PRC", "ROC", "ROK", "Singapore", "Turkey", "W-SU"];
366
+ type SingleSegmentTimeZone = typeof singleSegmentTimeZoneList[number];
355
367
  /**
356
368
  * Creates a ValueParser for parsing IANA Time Zone Database identifiers.
357
369
  *
@@ -361,6 +373,8 @@ declare function plainMonthDay(options?: PlainMonthDayOptions): ValueParser<"syn
361
373
  * - `"America/New_York"`
362
374
  * - `"Europe/London"`
363
375
  * - `"UTC"`
376
+ * - `"GMT"`
377
+ * - `"EST"`
364
378
  *
365
379
  * @param options Configuration options for the timezone parser.
366
380
  * @returns A ValueParser that parses and validates timezone identifiers.
package/dist/index.js CHANGED
@@ -23,8 +23,15 @@ function instant(options = {}) {
23
23
  const metavar = options.metavar ?? "TIMESTAMP";
24
24
  ensureNonEmptyString(metavar);
25
25
  return {
26
- $mode: "sync",
26
+ mode: "sync",
27
27
  metavar,
28
+ get placeholder() {
29
+ try {
30
+ return Temporal.Instant.from("1970-01-01T00:00:00Z");
31
+ } catch {
32
+ return void 0;
33
+ }
34
+ },
28
35
  parse(input) {
29
36
  ensureTemporal();
30
37
  try {
@@ -63,8 +70,15 @@ function duration(options = {}) {
63
70
  const metavar = options.metavar ?? "DURATION";
64
71
  ensureNonEmptyString(metavar);
65
72
  return {
66
- $mode: "sync",
73
+ mode: "sync",
67
74
  metavar,
75
+ get placeholder() {
76
+ try {
77
+ return Temporal.Duration.from("PT0S");
78
+ } catch {
79
+ return void 0;
80
+ }
81
+ },
68
82
  parse(input) {
69
83
  ensureTemporal();
70
84
  try {
@@ -102,8 +116,15 @@ function zonedDateTime(options = {}) {
102
116
  const metavar = options.metavar ?? "ZONED_DATETIME";
103
117
  ensureNonEmptyString(metavar);
104
118
  return {
105
- $mode: "sync",
119
+ mode: "sync",
106
120
  metavar,
121
+ get placeholder() {
122
+ try {
123
+ return Temporal.ZonedDateTime.from("1970-01-01T00:00:00+00:00[UTC]");
124
+ } catch {
125
+ return void 0;
126
+ }
127
+ },
107
128
  parse(input) {
108
129
  ensureTemporal();
109
130
  try {
@@ -125,6 +146,64 @@ function zonedDateTime(options = {}) {
125
146
  };
126
147
  }
127
148
  /**
149
+ * Optional RFC 9557 calendar annotation suffix, e.g. `[u-ca=gregory]`.
150
+ * Accepts case-insensitive values and the optional critical flag (`!`).
151
+ * Used by all plain Temporal regexes to accept `toString()` output for
152
+ * non-ISO calendars.
153
+ */
154
+ const CALENDAR_ANNOTATION = String.raw`(\[!?u-ca=[a-zA-Z0-9\-]+\])?`;
155
+ /**
156
+ * Required RFC 9557 calendar annotation (non-optional variant used when the
157
+ * annotation must be present, e.g. for reference-day/year forms).
158
+ */
159
+ const CALENDAR_ANNOTATION_REQUIRED = String.raw`\[!?u-ca=[a-zA-Z0-9\-]+\]`;
160
+ /**
161
+ * Year portion: either 4 digits (`YYYY`) or a sign-prefixed 6-digit expanded
162
+ * year (`+YYYYYY` / `-YYYYYY`).
163
+ */
164
+ const YEAR = String.raw`([+-]\d{6}|\d{4})`;
165
+ /** ISO 8601 fractional seconds with `.` or `,` separator. */
166
+ const FRACTIONAL = String.raw`[.,]\d+`;
167
+ /** Extended date: `YYYY-MM-DD` or `±YYYYYY-MM-DD`. */
168
+ const DATE_EXTENDED = `${YEAR}-\\d{2}-\\d{2}`;
169
+ /** Basic date: `YYYYMMDD` or `±YYYYYYMMDD`. */
170
+ const DATE_BASIC = `${YEAR}\\d{4}`;
171
+ /** Extended time: `HH:MM[:SS[.frac]]`. */
172
+ const TIME_EXTENDED = `\\d{2}:\\d{2}(:\\d{2}(${FRACTIONAL})?)?`;
173
+ /** Basic time: `HH`, `HHMM`, or `HHMMSS[.frac]`. */
174
+ const TIME_BASIC = `\\d{2}(\\d{2}(\\d{2}(${FRACTIONAL})?)?)?`;
175
+ /**
176
+ * Matches YYYY-MM-DD (extended) or YYYYMMDD (basic) date forms only (no time
177
+ * component). Both forms accept expanded years and calendar annotations.
178
+ */
179
+ const PLAIN_DATE_RE = /* @__PURE__ */ new RegExp(`^(${DATE_EXTENDED}|${DATE_BASIC})${CALENDAR_ANNOTATION}$`);
180
+ /**
181
+ * Matches time forms only (no date prefix). Accepts extended
182
+ * (`HH:MM[:SS[.frac]]`), basic (`HH`, `HHMM`, `HHMMSS[.frac]`), and
183
+ * `T`-prefixed variants. Calendar annotations are accepted for consistency
184
+ * with `Temporal.PlainTime.from()` on polyfill runtimes.
185
+ */
186
+ const PLAIN_TIME_RE = /* @__PURE__ */ new RegExp(`^[Tt]?(${TIME_EXTENDED}|${TIME_BASIC})${CALENDAR_ANNOTATION}$`);
187
+ /**
188
+ * Matches date-time strings with both date and time parts. Accepts extended,
189
+ * basic, and mixed forms (e.g. `2020-01-23T170436`). The separator may be
190
+ * `T`, `t`, or a space. The time portion may be reduced-precision (hour
191
+ * only).
192
+ */
193
+ const PLAIN_DATETIME_RE = /* @__PURE__ */ new RegExp(`^(${DATE_EXTENDED}|${DATE_BASIC})[Tt ](${TIME_EXTENDED}|${TIME_BASIC})${CALENDAR_ANNOTATION}$`);
194
+ /**
195
+ * Matches YYYY-MM (extended) or YYYYMM (basic), or a full date
196
+ * (extended or basic) with a required calendar annotation (the reference day
197
+ * is emitted by `toString()` for non-ISO calendars).
198
+ */
199
+ const PLAIN_YEAR_MONTH_RE = /* @__PURE__ */ new RegExp(`^(${YEAR}-\\d{2}(${CALENDAR_ANNOTATION}|-\\d{2}${CALENDAR_ANNOTATION_REQUIRED})|${YEAR}\\d{2}(${CALENDAR_ANNOTATION}|\\d{2}${CALENDAR_ANNOTATION_REQUIRED}))$`);
200
+ /**
201
+ * Matches MM-DD, --MM-DD, MMDD, or --MMDD month-day forms, or a full date
202
+ * (extended or basic) with a required calendar annotation (the reference year
203
+ * is emitted by `toString()` for non-ISO calendars).
204
+ */
205
+ const PLAIN_MONTH_DAY_RE = /* @__PURE__ */ new RegExp(`^((--)?(\\d{2}-\\d{2}|\\d{4})${CALENDAR_ANNOTATION}|(${DATE_EXTENDED}|${DATE_BASIC})${CALENDAR_ANNOTATION_REQUIRED})$`);
206
+ /**
128
207
  * Creates a ValueParser for parsing Temporal.PlainDate from ISO 8601 date strings.
129
208
  *
130
209
  * Accepts strings like:
@@ -141,11 +220,19 @@ function plainDate(options = {}) {
141
220
  const metavar = options.metavar ?? "DATE";
142
221
  ensureNonEmptyString(metavar);
143
222
  return {
144
- $mode: "sync",
223
+ mode: "sync",
145
224
  metavar,
225
+ get placeholder() {
226
+ try {
227
+ return Temporal.PlainDate.from("1970-01-01");
228
+ } catch {
229
+ return void 0;
230
+ }
231
+ },
146
232
  parse(input) {
147
233
  ensureTemporal();
148
234
  try {
235
+ if (!PLAIN_DATE_RE.test(input)) throw new RangeError();
149
236
  const value = Temporal.PlainDate.from(input);
150
237
  return {
151
238
  success: true,
@@ -180,11 +267,19 @@ function plainTime(options = {}) {
180
267
  const metavar = options.metavar ?? "TIME";
181
268
  ensureNonEmptyString(metavar);
182
269
  return {
183
- $mode: "sync",
270
+ mode: "sync",
184
271
  metavar,
272
+ get placeholder() {
273
+ try {
274
+ return Temporal.PlainTime.from("00:00:00");
275
+ } catch {
276
+ return void 0;
277
+ }
278
+ },
185
279
  parse(input) {
186
280
  ensureTemporal();
187
281
  try {
282
+ if (!PLAIN_TIME_RE.test(input)) throw new RangeError();
188
283
  const value = Temporal.PlainTime.from(input);
189
284
  return {
190
285
  success: true,
@@ -219,11 +314,19 @@ function plainDateTime(options = {}) {
219
314
  const metavar = options.metavar ?? "DATETIME";
220
315
  ensureNonEmptyString(metavar);
221
316
  return {
222
- $mode: "sync",
317
+ mode: "sync",
223
318
  metavar,
319
+ get placeholder() {
320
+ try {
321
+ return Temporal.PlainDateTime.from("1970-01-01T00:00:00");
322
+ } catch {
323
+ return void 0;
324
+ }
325
+ },
224
326
  parse(input) {
225
327
  ensureTemporal();
226
328
  try {
329
+ if (!PLAIN_DATETIME_RE.test(input)) throw new RangeError();
227
330
  const value = Temporal.PlainDateTime.from(input);
228
331
  return {
229
332
  success: true,
@@ -258,11 +361,19 @@ function plainYearMonth(options = {}) {
258
361
  const metavar = options.metavar ?? "YEAR-MONTH";
259
362
  ensureNonEmptyString(metavar);
260
363
  return {
261
- $mode: "sync",
364
+ mode: "sync",
262
365
  metavar,
366
+ get placeholder() {
367
+ try {
368
+ return Temporal.PlainYearMonth.from("1970-01");
369
+ } catch {
370
+ return void 0;
371
+ }
372
+ },
263
373
  parse(input) {
264
374
  ensureTemporal();
265
375
  try {
376
+ if (!PLAIN_YEAR_MONTH_RE.test(input)) throw new RangeError();
266
377
  const value = Temporal.PlainYearMonth.from(input);
267
378
  return {
268
379
  success: true,
@@ -285,8 +396,8 @@ function plainYearMonth(options = {}) {
285
396
  *
286
397
  * Accepts strings like:
287
398
  *
288
- * - `"--01-23"`
289
- * - `"--12-31"`
399
+ * - `"01-23"`
400
+ * - `"12-31"`
290
401
  *
291
402
  * @param options Configuration options for the plain month-day parser.
292
403
  * @returns A ValueParser that parses strings into Temporal.PlainMonthDay values.
@@ -294,14 +405,22 @@ function plainYearMonth(options = {}) {
294
405
  * at runtime.
295
406
  */
296
407
  function plainMonthDay(options = {}) {
297
- const metavar = options.metavar ?? "--MONTH-DAY";
408
+ const metavar = options.metavar ?? "MONTH-DAY";
298
409
  ensureNonEmptyString(metavar);
299
410
  return {
300
- $mode: "sync",
411
+ mode: "sync",
301
412
  metavar,
413
+ get placeholder() {
414
+ try {
415
+ return Temporal.PlainMonthDay.from("01-01");
416
+ } catch {
417
+ return void 0;
418
+ }
419
+ },
302
420
  parse(input) {
303
421
  ensureTemporal();
304
422
  try {
423
+ if (!PLAIN_MONTH_DAY_RE.test(input)) throw new RangeError();
305
424
  const value = Temporal.PlainMonthDay.from(input);
306
425
  return {
307
426
  success: true,
@@ -310,7 +429,7 @@ function plainMonthDay(options = {}) {
310
429
  } catch {
311
430
  return {
312
431
  success: false,
313
- error: options.errors?.invalidFormat ? typeof options.errors.invalidFormat === "function" ? options.errors.invalidFormat(input) : options.errors.invalidFormat : message`Invalid month-day: ${input}. Expected ISO 8601 format like ${"--01-23"}.`
432
+ error: options.errors?.invalidFormat ? typeof options.errors.invalidFormat === "function" ? options.errors.invalidFormat(input) : options.errors.invalidFormat : message`Invalid month-day: ${input}. Expected ISO 8601 format like ${"01-23"}.`
314
433
  };
315
434
  }
316
435
  },
@@ -320,6 +439,59 @@ function plainMonthDay(options = {}) {
320
439
  };
321
440
  }
322
441
  /**
442
+ * Single-segment IANA timezone identifiers accepted across all supported
443
+ * runtimes (Deno, Node.js, Bun). This tuple is the single source of truth:
444
+ * {@link SingleSegmentTimeZone} is derived from it, and the runtime lookup
445
+ * {@link singleSegmentTimeZoneLookup} is built from it.
446
+ */
447
+ const singleSegmentTimeZoneList = [
448
+ "UTC",
449
+ "GMT",
450
+ "GMT0",
451
+ "GMT+0",
452
+ "GMT-0",
453
+ "UCT",
454
+ "Universal",
455
+ "Greenwich",
456
+ "Zulu",
457
+ "EST",
458
+ "MST",
459
+ "HST",
460
+ "CET",
461
+ "MET",
462
+ "WET",
463
+ "EET",
464
+ "EST5EDT",
465
+ "CST6CDT",
466
+ "MST7MDT",
467
+ "PST8PDT",
468
+ "Cuba",
469
+ "Egypt",
470
+ "Eire",
471
+ "GB",
472
+ "GB-Eire",
473
+ "Hongkong",
474
+ "Iceland",
475
+ "Iran",
476
+ "Israel",
477
+ "Jamaica",
478
+ "Japan",
479
+ "Kwajalein",
480
+ "Libya",
481
+ "Navajo",
482
+ "NZ",
483
+ "NZ-CHAT",
484
+ "Poland",
485
+ "Portugal",
486
+ "PRC",
487
+ "ROC",
488
+ "ROK",
489
+ "Singapore",
490
+ "Turkey",
491
+ "W-SU"
492
+ ];
493
+ const singleSegmentTimeZoneLookup = new Map(singleSegmentTimeZoneList.map((timeZone$1) => [timeZone$1.toLowerCase(), timeZone$1]));
494
+ /**
323
495
  * Creates a ValueParser for parsing IANA Time Zone Database identifiers.
324
496
  *
325
497
  * Accepts strings like:
@@ -328,6 +500,8 @@ function plainMonthDay(options = {}) {
328
500
  * - `"America/New_York"`
329
501
  * - `"Europe/London"`
330
502
  * - `"UTC"`
503
+ * - `"GMT"`
504
+ * - `"EST"`
331
505
  *
332
506
  * @param options Configuration options for the timezone parser.
333
507
  * @returns A ValueParser that parses and validates timezone identifiers.
@@ -338,8 +512,9 @@ function timeZone(options = {}) {
338
512
  const metavar = options.metavar ?? "TIMEZONE";
339
513
  ensureNonEmptyString(metavar);
340
514
  return {
341
- $mode: "sync",
515
+ mode: "sync",
342
516
  metavar,
517
+ placeholder: "UTC",
343
518
  parse(input) {
344
519
  ensureTemporal();
345
520
  try {
@@ -349,6 +524,14 @@ function timeZone(options = {}) {
349
524
  day: 1,
350
525
  timeZone: input
351
526
  });
527
+ if (!input.includes("/")) {
528
+ const canonical = singleSegmentTimeZoneLookup.get(input.toLowerCase());
529
+ if (canonical == null) throw new RangeError();
530
+ return {
531
+ success: true,
532
+ value: canonical
533
+ };
534
+ }
352
535
  return {
353
536
  success: true,
354
537
  value: input
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/temporal",
3
- "version": "1.0.0-dev.921+754748bd",
3
+ "version": "1.0.0",
4
4
  "description": "Temporal value parsers for Optique",
5
5
  "keywords": [
6
6
  "CLI",
@@ -56,7 +56,7 @@
56
56
  "sideEffects": false,
57
57
  "dependencies": {
58
58
  "@js-temporal/polyfill": "^0.5.1",
59
- "@optique/core": "1.0.0-dev.921+754748bd"
59
+ "@optique/core": "1.0.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^20.19.9",