@dereekb/date 13.1.0 → 13.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -1,8 +1,8 @@
1
- import { MS_IN_HOUR, MS_IN_MINUTE, MINUTES_IN_DAY, isISO8601DateString, filterMaybeArrayValues, asArray, dayOfWeek, sortNumbersAscendingFunction, MS_IN_SECOND, SORT_VALUE_LESS_THAN, SORT_VALUE_GREATER_THAN, SORT_VALUE_EQUAL, copyArray, compareFnOrder, MS_IN_DAY, daysOfWeekArray, groupValues, minutesToFractionalHours, safeCompareEquality, DATE_NOW_VALUE, mapIdentityFunction, UTC_TIMEZONE_STRING, cachedGetter, parseISO8601DayStringToUTCDate, isConsideredUtcTimezoneString, isSameNonNullValue, replaceStringsFunction, repeatString, isEqualDate, startOfDayForUTCDateInUTC, range, pushArrayItemsIntoArray, sumOfIntegersBetween, makeValuesGroupMap, lastValue, sortAscendingIndexNumberRefFunction, indexRangeCheckFunction, asGetter, mergeFilterFunctions, isDate as isDate$2, HOURS_IN_DAY, getNextDay, iterablesAreSetEquivalent, daysOfWeekFromEnabledDays, forEachInIterable, enabledDaysFromDaysOfWeek, addToSet, invertFilter, firstValueFromIterable, HashSet, roundNumberUpToStep, protectedFactory, TimeAM, isLogicalDateStringCode as isLogicalDateStringCode$1, dateFromLogicalDate as dateFromLogicalDate$1, flattenArray, splitJoinRemainder } from '@dereekb/util';
1
+ import { MS_IN_HOUR, MS_IN_MINUTE, MINUTES_IN_DAY, isISO8601DateString, filterMaybeArrayValues, asArray, dayOfWeek, sortNumbersAscendingFunction, MS_IN_SECOND, SORT_VALUE_LESS_THAN, SORT_VALUE_GREATER_THAN, SORT_VALUE_EQUAL, copyArray, compareFnOrder, MS_IN_DAY, daysOfWeekArray, groupValues, DATE_NOW_VALUE, mapIdentityFunction, UTC_TIMEZONE_STRING, cachedGetter, parseISO8601DayStringToUTCDate, isConsideredUtcTimezoneString, isSameNonNullValue, repeatString, isEqualDate, startOfDayForUTCDateInUTC, minutesToFractionalHours, range, pushArrayItemsIntoArray, sumOfIntegersBetween, makeValuesGroupMap, lastValue, sortAscendingIndexNumberRefFunction, indexRangeCheckFunction, asGetter, mergeFilterFunctions, isDate as isDate$2, HOURS_IN_DAY, safeCompareEquality, getNextDay, iterablesAreSetEquivalent, daysOfWeekFromEnabledDays, forEachInIterable, enabledDaysFromDaysOfWeek, addToSet, invertFilter, firstValueFromIterable, replaceStringsFunction, HashSet, roundNumberUpToStep, protectedFactory, TimeAM, isLogicalDateStringCode as isLogicalDateStringCode$1, dateFromLogicalDate as dateFromLogicalDate$1, flattenArray, splitJoinRemainder } from '@dereekb/util';
2
2
  export { dateFromDateOrTimeMillisecondsNumber, unixDateTimeSecondsNumberForNow, unixDateTimeSecondsNumberFromDate, unixDateTimeSecondsNumberFromDateOrTimeNumber, unixDateTimeSecondsNumberToDate } from '@dereekb/util';
3
- import { isEqual, isSameDay, isDate as isDate$1, startOfMinute, isValid, parseISO, min, max, isAfter as isAfter$1, isBefore as isBefore$1, set, addMilliseconds, startOfMonth, endOfWeek, startOfWeek, endOfMonth, addDays, addHours, addMinutes, differenceInDays, startOfDay, addMonths, addWeeks, endOfDay, endOfHour, startOfHour, endOfMinute, differenceInMinutes, differenceInHours, millisecondsToHours, millisecondsToMinutes, formatDistanceStrict, formatDistance, formatDistanceToNow, format as format$1, parse, differenceInMilliseconds, getMinutes, getSeconds, getMilliseconds, setWeek, getWeek, getYear, getDay, isPast, addSeconds } from 'date-fns';
4
- import { type } from 'arktype';
3
+ import { isEqual, isSameDay, isDate as isDate$1, startOfMinute, isValid, parseISO, min, max, isAfter as isAfter$1, isBefore as isBefore$1, set, addMilliseconds, startOfMonth, endOfWeek, startOfWeek, endOfMonth, addDays, addHours, addMinutes, differenceInDays, startOfDay, addMonths, addWeeks, endOfDay, endOfHour, startOfHour, endOfMinute, differenceInHours, millisecondsToHours, millisecondsToMinutes, formatDistanceStrict, formatDistance, formatDistanceToNow, format as format$1, differenceInMinutes, parse, differenceInMilliseconds, getMinutes, getSeconds, getMilliseconds, setWeek, getWeek, getYear, getDay, isPast, addSeconds } from 'date-fns';
5
4
  import { toZonedTime, format, formatInTimeZone } from 'date-fns-tz';
5
+ import { type } from 'arktype';
6
6
  import { timeZonesNames } from '@vvo/tzdb';
7
7
  import { interval, startWith, map, distinctUntilChanged } from 'rxjs';
8
8
  import { RRule } from 'rrule';
@@ -686,13 +686,6 @@ function isDateRangeStart(value) {
686
686
  * ```
687
687
  */
688
688
  const sortDateRangeStartAscendingCompareFunction = sortByDateFunction((x) => x.start);
689
- /**
690
- * ArkType schema for {@link DateRange}.
691
- */
692
- const dateRangeType = type({
693
- start: 'Date',
694
- end: 'Date'
695
- });
696
689
  /**
697
690
  * Counts the total number of calendar days spanned by the range, inclusive of both endpoints.
698
691
  * Always returns at least 1, even for same-day ranges.
@@ -868,14 +861,6 @@ var DateRangeType;
868
861
  */
869
862
  DateRangeType["CALENDAR_MONTH"] = "calendar_month";
870
863
  })(DateRangeType || (DateRangeType = {}));
871
- /**
872
- * ArkType schema for {@link DateRangeParams}.
873
- */
874
- const dateRangeParamsType = type({
875
- type: type.enumerated(...Object.values(DateRangeType)),
876
- date: 'Date',
877
- 'distance?': 'number'
878
- });
879
864
  /**
880
865
  * Creates a {@link DateRange} from the given type and optional parameters. Supports many range
881
866
  * strategies including fixed periods (day, week, month), directional ranges, and radii.
@@ -1472,75 +1457,6 @@ function getDaysOfWeekInDateRange(dateRange) {
1472
1457
  return days;
1473
1458
  }
1474
1459
 
1475
- /**
1476
- * ArkType schema for {@link DateDurationSpan}.
1477
- */
1478
- const dateDurationSpanType = type({
1479
- startsAt: 'Date',
1480
- duration: 'number >= 0'
1481
- });
1482
- /**
1483
- * Computes the end date for a duration span by adding the duration to the start time.
1484
- *
1485
- * @param span - the duration span to compute the end for
1486
- * @returns the date when the span ends
1487
- *
1488
- * @example
1489
- * ```ts
1490
- * const span = { startsAt: new Date('2024-01-01T10:00:00Z'), duration: 60 };
1491
- * dateDurationSpanEndDate(span); // 2024-01-01T11:00:00Z
1492
- * ```
1493
- */
1494
- function dateDurationSpanEndDate(span) {
1495
- return addMinutes(span.startsAt, span.duration);
1496
- }
1497
- /**
1498
- * Converts a {@link DateDurationSpan} to a {@link DateRange} with start and end dates.
1499
- *
1500
- * @param span - the duration span to convert
1501
- * @returns a date range from startsAt to startsAt + duration
1502
- */
1503
- function durationSpanToDateRange(span) {
1504
- return {
1505
- start: span.startsAt,
1506
- end: addMinutes(span.startsAt, span.duration)
1507
- };
1508
- }
1509
- /**
1510
- * Creates a {@link DateDurationSpan} from a {@link DateRange} by computing the duration in minutes between start and end.
1511
- *
1512
- * @param dateRange - the date range to convert
1513
- * @returns a duration span with the range's start as startsAt
1514
- */
1515
- function durationSpanFromDateRange(dateRange) {
1516
- return {
1517
- startsAt: dateRange.start,
1518
- duration: differenceInMinutes(dateRange.end, dateRange.start)
1519
- };
1520
- }
1521
- /**
1522
- * Determines whether a duration span is in the past, present, or future relative to the given time.
1523
- *
1524
- * @param span - the duration span to check
1525
- * @param now - reference time (defaults to current time)
1526
- * @returns 'past', 'present', or 'future'
1527
- */
1528
- function durationSpanDateRelativeState(span, now) {
1529
- return dateRangeRelativeState(durationSpanToDateRange(span), now);
1530
- }
1531
- /**
1532
- * Converts a duration span's duration from minutes to fractional hours.
1533
- *
1534
- * @param span - the duration span to measure
1535
- * @returns the duration expressed as fractional hours (e.g. 90 minutes = 1.5)
1536
- */
1537
- function fractionalHoursInDurationSpan(span) {
1538
- return minutesToFractionalHours(span.duration);
1539
- }
1540
- function isSameDurationSpan(a, b) {
1541
- return safeCompareEquality(a, b, (a, b) => a.duration === b.duration && isSameDate(a.startsAt, b.startsAt));
1542
- }
1543
-
1544
1460
  /**
1545
1461
  * String code for the start of the current day.
1546
1462
  */
@@ -2452,158 +2368,6 @@ function copyHoursAndMinutesFromDateWithTimezoneNormal(input, copyFrom, timezone
2452
2368
  return result;
2453
2369
  }
2454
2370
 
2455
- /**
2456
- * Returns all recognized IANA timezone strings, including the explicit UTC entry.
2457
- *
2458
- * @example
2459
- * ```ts
2460
- * const zones = allTimezoneStrings();
2461
- * // ['Africa/Abidjan', ..., 'UTC']
2462
- * ```
2463
- */
2464
- function allTimezoneStrings() {
2465
- return timeZonesNames.concat(UTC_TIMEZONE_STRING);
2466
- }
2467
- /**
2468
- * Lazily-computed set of all known timezone strings for O(1) membership checks.
2469
- *
2470
- * @example
2471
- * ```ts
2472
- * allKnownTimezoneStrings().has('America/New_York'); // true
2473
- * ```
2474
- */
2475
- const allKnownTimezoneStrings = cachedGetter(() => {
2476
- return new Set(allTimezoneStrings());
2477
- });
2478
- /**
2479
- * Lazily-computed array of {@link TimezoneInfo} for every known timezone.
2480
- *
2481
- * Abbreviations are resolved at the time of first access, so results reflect
2482
- * the DST state at that moment.
2483
- */
2484
- const allTimezoneInfos = cachedGetter(() => {
2485
- const now = new Date();
2486
- return allTimezoneStrings().map((x) => timezoneStringToTimezoneInfo(x, now));
2487
- });
2488
- /**
2489
- * Returns the {@link TimezoneInfo} for the current system timezone, falling back to UTC.
2490
- *
2491
- * @example
2492
- * ```ts
2493
- * const info = timezoneInfoForSystem();
2494
- * console.log(info.abbreviation); // e.g., 'CST'
2495
- * ```
2496
- */
2497
- function timezoneInfoForSystem() {
2498
- return timezoneStringToTimezoneInfo(guessCurrentTimezone() ?? UTC_TIMEZONE_STRING);
2499
- }
2500
- /**
2501
- * Returns the short abbreviation (e.g., `"EST"`, `"PDT"`) for the given timezone at the specified date.
2502
- *
2503
- * The date matters because abbreviations change with DST transitions.
2504
- * Returns `"UKNOWN"` if no timezone is provided.
2505
- *
2506
- * @example
2507
- * ```ts
2508
- * getTimezoneAbbreviation('America/New_York'); // 'EST' or 'EDT'
2509
- * ```
2510
- */
2511
- function getTimezoneAbbreviation(timezone, date = new Date()) {
2512
- return timezone === UTC_TIMEZONE_STRING ? UTC_TIMEZONE_STRING : timezone ? formatInTimeZone(date, timezone, 'zzz') : 'UKNOWN';
2513
- }
2514
- /**
2515
- * Returns the full display name (e.g., `"Eastern Standard Time"`) for the given timezone.
2516
- *
2517
- * Returns `"Unknown Timezone"` if no timezone is provided.
2518
- *
2519
- * @example
2520
- * ```ts
2521
- * getTimezoneLongName('America/New_York'); // 'Eastern Standard Time'
2522
- * ```
2523
- */
2524
- function getTimezoneLongName(timezone, date = new Date()) {
2525
- return timezone ? formatInTimeZone(date, timezone, 'zzzz') : 'Unknown Timezone';
2526
- }
2527
- /**
2528
- * Builds a {@link TimezoneInfo} for the given timezone, computing abbreviation and search variants.
2529
- *
2530
- * @example
2531
- * ```ts
2532
- * const info = timezoneStringToTimezoneInfo('America/Chicago');
2533
- * // info.abbreviation => 'CST' or 'CDT'
2534
- * // info.search => 'america chicago'
2535
- * ```
2536
- */
2537
- function timezoneStringToTimezoneInfo(timezone, date = new Date()) {
2538
- const abbreviation = getTimezoneAbbreviation(timezone, date);
2539
- const result = {
2540
- timezone,
2541
- search: timezoneStringToSearchableString(timezone),
2542
- lowercase: timezone.toLowerCase(),
2543
- abbreviation,
2544
- lowercaseAbbreviation: abbreviation.toLowerCase()
2545
- };
2546
- return result;
2547
- }
2548
- /**
2549
- * Filters timezone infos by a search string, matching against the searchable name,
2550
- * lowercase identifier, and abbreviation.
2551
- *
2552
- * For queries longer than 2 characters, substring matching on the searchable name is also used.
2553
- *
2554
- * @example
2555
- * ```ts
2556
- * const results = searchTimezoneInfos('eastern', allTimezoneInfos());
2557
- * ```
2558
- */
2559
- function searchTimezoneInfos(search, infos) {
2560
- const searchString = search.toLocaleLowerCase();
2561
- return infos.filter((x) => (search.length > 2 && x.search.includes(searchString)) || x.lowercase.startsWith(searchString) || x.lowercaseAbbreviation.startsWith(searchString) || x.abbreviation.includes(search) || x.search === x.timezone);
2562
- }
2563
- const timezoneStringToSearchableStringReplace = replaceStringsFunction({
2564
- replace: ['/', '_'],
2565
- replaceWith: ' '
2566
- });
2567
- /**
2568
- * Converts a timezone identifier into a lowercase, space-separated string for search indexing.
2569
- *
2570
- * Replaces `/` and `_` with spaces (e.g., `"America/New_York"` becomes `"america new york"`).
2571
- *
2572
- * @example
2573
- * ```ts
2574
- * timezoneStringToSearchableString('America/New_York'); // 'america new york'
2575
- * ```
2576
- */
2577
- function timezoneStringToSearchableString(timezone) {
2578
- return timezoneStringToSearchableStringReplace(timezone.toLocaleLowerCase());
2579
- }
2580
- /**
2581
- * Checks whether the input string is a recognized IANA timezone identifier.
2582
- *
2583
- * Uses the cached set from {@link allKnownTimezoneStrings} for O(1) lookup.
2584
- *
2585
- * @example
2586
- * ```ts
2587
- * isKnownTimezone('America/New_York'); // true
2588
- * isKnownTimezone('Mars/Olympus'); // false
2589
- * ```
2590
- */
2591
- function isKnownTimezone(input) {
2592
- return allKnownTimezoneStrings().has(input);
2593
- }
2594
-
2595
- /**
2596
- * ArkType schema that validates a string is a recognized IANA timezone.
2597
- *
2598
- * Delegates to {@link isKnownTimezone} for the actual check.
2599
- *
2600
- * @example
2601
- * ```ts
2602
- * const result = knownTimezoneType('America/Denver');
2603
- * ```
2604
- */
2605
- const knownTimezoneType = type('string > 0').narrow((val, ctx) => isKnownTimezone(val) || ctx.mustBe('a known timezone'));
2606
-
2607
2371
  /**
2608
2372
  * Creates a {@link FitDateRangeToDayPeriodFunction} that collapses a multi-day date range into a single-day period within the given timezone.
2609
2373
  *
@@ -3144,12 +2908,6 @@ function parseISO8601DayStringToDate(dayString) {
3144
2908
  function isValidDateCellIndex(input) {
3145
2909
  return input >= 0 && Number.isInteger(input);
3146
2910
  }
3147
- /**
3148
- * ArkType schema for {@link DateCell}.
3149
- */
3150
- const dateCellType = type({
3151
- i: 'number.integer >= 0'
3152
- });
3153
2911
  /**
3154
2912
  * Normalizes a number or {@link DateCell} to a DateCell object.
3155
2913
  *
@@ -3190,13 +2948,6 @@ function dateCellTimingStartsAtForStartOfDay(input = {}) {
3190
2948
  timezone
3191
2949
  };
3192
2950
  }
3193
- /**
3194
- * ArkType schema for {@link DateCellTiming}.
3195
- */
3196
- const dateCellTimingType = dateDurationSpanType.merge({
3197
- end: 'Date',
3198
- timezone: knownTimezoneType
3199
- });
3200
2951
  /**
3201
2952
  * Creates a {@link DateTimezoneUtcNormalInstance} from the input, guaranteeing that a timezone string is configured.
3202
2953
  *
@@ -3782,12 +3533,6 @@ function isValidFullDateCellTiming(timing) {
3782
3533
  return isValid;
3783
3534
  }
3784
3535
 
3785
- /**
3786
- * ArkType schema for {@link DateCellRange}.
3787
- */
3788
- const dateCellRangeType = dateCellType.merge({
3789
- 'to?': 'number.integer >= 0'
3790
- });
3791
3536
  /**
3792
3537
  * Returns true if the input is a DateCellRange.
3793
3538
  *
@@ -5241,19 +4986,81 @@ function isDateWithinDateCellRangeFunction(config) {
5241
4986
  }
5242
4987
 
5243
4988
  /**
5244
- * Creates a filter that passes date cell duration spans whose start time is at or before the given reference time.
5245
- *
5246
- * Useful for identifying events or blocks that have already begun relative to a point in time.
4989
+ * Computes the end date for a duration span by adding the duration to the start time.
5247
4990
  *
5248
- * @param now - Reference time to compare against. Defaults to the current time.
4991
+ * @param span - the duration span to compute the end for
4992
+ * @returns the date when the span ends
5249
4993
  *
5250
4994
  * @example
5251
4995
  * ```ts
5252
- * const hasStarted = dateCellDurationSpanHasStartedFilterFunction(new Date());
5253
- * const startedSpans = allSpans.filter(hasStarted);
4996
+ * const span = { startsAt: new Date('2024-01-01T10:00:00Z'), duration: 60 };
4997
+ * dateDurationSpanEndDate(span); // 2024-01-01T11:00:00Z
5254
4998
  * ```
5255
4999
  */
5256
- function dateCellDurationSpanHasStartedFilterFunction(now = new Date()) {
5000
+ function dateDurationSpanEndDate(span) {
5001
+ return addMinutes(span.startsAt, span.duration);
5002
+ }
5003
+ /**
5004
+ * Converts a {@link DateDurationSpan} to a {@link DateRange} with start and end dates.
5005
+ *
5006
+ * @param span - the duration span to convert
5007
+ * @returns a date range from startsAt to startsAt + duration
5008
+ */
5009
+ function durationSpanToDateRange(span) {
5010
+ return {
5011
+ start: span.startsAt,
5012
+ end: addMinutes(span.startsAt, span.duration)
5013
+ };
5014
+ }
5015
+ /**
5016
+ * Creates a {@link DateDurationSpan} from a {@link DateRange} by computing the duration in minutes between start and end.
5017
+ *
5018
+ * @param dateRange - the date range to convert
5019
+ * @returns a duration span with the range's start as startsAt
5020
+ */
5021
+ function durationSpanFromDateRange(dateRange) {
5022
+ return {
5023
+ startsAt: dateRange.start,
5024
+ duration: differenceInMinutes(dateRange.end, dateRange.start)
5025
+ };
5026
+ }
5027
+ /**
5028
+ * Determines whether a duration span is in the past, present, or future relative to the given time.
5029
+ *
5030
+ * @param span - the duration span to check
5031
+ * @param now - reference time (defaults to current time)
5032
+ * @returns 'past', 'present', or 'future'
5033
+ */
5034
+ function durationSpanDateRelativeState(span, now) {
5035
+ return dateRangeRelativeState(durationSpanToDateRange(span), now);
5036
+ }
5037
+ /**
5038
+ * Converts a duration span's duration from minutes to fractional hours.
5039
+ *
5040
+ * @param span - the duration span to measure
5041
+ * @returns the duration expressed as fractional hours (e.g. 90 minutes = 1.5)
5042
+ */
5043
+ function fractionalHoursInDurationSpan(span) {
5044
+ return minutesToFractionalHours(span.duration);
5045
+ }
5046
+ function isSameDurationSpan(a, b) {
5047
+ return safeCompareEquality(a, b, (a, b) => a.duration === b.duration && isSameDate(a.startsAt, b.startsAt));
5048
+ }
5049
+
5050
+ /**
5051
+ * Creates a filter that passes date cell duration spans whose start time is at or before the given reference time.
5052
+ *
5053
+ * Useful for identifying events or blocks that have already begun relative to a point in time.
5054
+ *
5055
+ * @param now - Reference time to compare against. Defaults to the current time.
5056
+ *
5057
+ * @example
5058
+ * ```ts
5059
+ * const hasStarted = dateCellDurationSpanHasStartedFilterFunction(new Date());
5060
+ * const startedSpans = allSpans.filter(hasStarted);
5061
+ * ```
5062
+ */
5063
+ function dateCellDurationSpanHasStartedFilterFunction(now = new Date()) {
5257
5064
  return (x) => !isAfter$1(x.startsAt, now); // startsAt <= now
5258
5065
  }
5259
5066
  /**
@@ -6059,14 +5866,6 @@ function isSameDateCellSchedule(a, b) {
6059
5866
  return a == b;
6060
5867
  }
6061
5868
  }
6062
- /**
6063
- * ArkType schema for {@link DateCellSchedule}.
6064
- */
6065
- const dateCellScheduleType = type({
6066
- w: [DATE_CELL_SCHEDULE_ENCODED_WEEK_REGEX, '&', 'string'],
6067
- 'd?': 'number.integer >= 0 []',
6068
- 'ex?': 'number.integer >= 0 []'
6069
- });
6070
5869
  /**
6071
5870
  * Returns true if the input is possibly a DateCellScheduleDateRange (has schedule fields and valid start/end dates).
6072
5871
  *
@@ -6576,17 +6375,144 @@ function dateCellIndexsForDateCellScheduleDayCodes(sundayIndex, dayCodes) {
6576
6375
  }
6577
6376
 
6578
6377
  /**
6579
- * ArkType schema that validates a value is a valid {@link DateCellTiming}.
6378
+ * Returns all recognized IANA timezone strings, including the explicit UTC entry.
6379
+ *
6380
+ * @example
6381
+ * ```ts
6382
+ * const zones = allTimezoneStrings();
6383
+ * // ['Africa/Abidjan', ..., 'UTC']
6384
+ * ```
6385
+ */
6386
+ function allTimezoneStrings() {
6387
+ return timeZonesNames.concat(UTC_TIMEZONE_STRING);
6388
+ }
6389
+ /**
6390
+ * Lazily-computed set of all known timezone strings for O(1) membership checks.
6391
+ *
6392
+ * @example
6393
+ * ```ts
6394
+ * allKnownTimezoneStrings().has('America/New_York'); // true
6395
+ * ```
6396
+ */
6397
+ const allKnownTimezoneStrings = cachedGetter(() => {
6398
+ return new Set(allTimezoneStrings());
6399
+ });
6400
+ /**
6401
+ * Lazily-computed array of {@link TimezoneInfo} for every known timezone.
6402
+ *
6403
+ * Abbreviations are resolved at the time of first access, so results reflect
6404
+ * the DST state at that moment.
6405
+ */
6406
+ const allTimezoneInfos = cachedGetter(() => {
6407
+ const now = new Date();
6408
+ return allTimezoneStrings().map((x) => timezoneStringToTimezoneInfo(x, now));
6409
+ });
6410
+ /**
6411
+ * Returns the {@link TimezoneInfo} for the current system timezone, falling back to UTC.
6412
+ *
6413
+ * @example
6414
+ * ```ts
6415
+ * const info = timezoneInfoForSystem();
6416
+ * console.log(info.abbreviation); // e.g., 'CST'
6417
+ * ```
6418
+ */
6419
+ function timezoneInfoForSystem() {
6420
+ return timezoneStringToTimezoneInfo(guessCurrentTimezone() ?? UTC_TIMEZONE_STRING);
6421
+ }
6422
+ /**
6423
+ * Returns the short abbreviation (e.g., `"EST"`, `"PDT"`) for the given timezone at the specified date.
6424
+ *
6425
+ * The date matters because abbreviations change with DST transitions.
6426
+ * Returns `"UKNOWN"` if no timezone is provided.
6427
+ *
6428
+ * @example
6429
+ * ```ts
6430
+ * getTimezoneAbbreviation('America/New_York'); // 'EST' or 'EDT'
6431
+ * ```
6432
+ */
6433
+ function getTimezoneAbbreviation(timezone, date = new Date()) {
6434
+ return timezone === UTC_TIMEZONE_STRING ? UTC_TIMEZONE_STRING : timezone ? formatInTimeZone(date, timezone, 'zzz') : 'UKNOWN';
6435
+ }
6436
+ /**
6437
+ * Returns the full display name (e.g., `"Eastern Standard Time"`) for the given timezone.
6438
+ *
6439
+ * Returns `"Unknown Timezone"` if no timezone is provided.
6440
+ *
6441
+ * @example
6442
+ * ```ts
6443
+ * getTimezoneLongName('America/New_York'); // 'Eastern Standard Time'
6444
+ * ```
6445
+ */
6446
+ function getTimezoneLongName(timezone, date = new Date()) {
6447
+ return timezone ? formatInTimeZone(date, timezone, 'zzzz') : 'Unknown Timezone';
6448
+ }
6449
+ /**
6450
+ * Builds a {@link TimezoneInfo} for the given timezone, computing abbreviation and search variants.
6451
+ *
6452
+ * @example
6453
+ * ```ts
6454
+ * const info = timezoneStringToTimezoneInfo('America/Chicago');
6455
+ * // info.abbreviation => 'CST' or 'CDT'
6456
+ * // info.search => 'america chicago'
6457
+ * ```
6458
+ */
6459
+ function timezoneStringToTimezoneInfo(timezone, date = new Date()) {
6460
+ const abbreviation = getTimezoneAbbreviation(timezone, date);
6461
+ const result = {
6462
+ timezone,
6463
+ search: timezoneStringToSearchableString(timezone),
6464
+ lowercase: timezone.toLowerCase(),
6465
+ abbreviation,
6466
+ lowercaseAbbreviation: abbreviation.toLowerCase()
6467
+ };
6468
+ return result;
6469
+ }
6470
+ /**
6471
+ * Filters timezone infos by a search string, matching against the searchable name,
6472
+ * lowercase identifier, and abbreviation.
6473
+ *
6474
+ * For queries longer than 2 characters, substring matching on the searchable name is also used.
6475
+ *
6476
+ * @example
6477
+ * ```ts
6478
+ * const results = searchTimezoneInfos('eastern', allTimezoneInfos());
6479
+ * ```
6580
6480
  */
6581
- const validDateCellTimingType = type('unknown').narrow((val, ctx) => isValidDateCellTiming(val) || ctx.mustBe('a valid DateCellTiming'));
6481
+ function searchTimezoneInfos(search, infos) {
6482
+ const searchString = search.toLocaleLowerCase();
6483
+ return infos.filter((x) => (search.length > 2 && x.search.includes(searchString)) || x.lowercase.startsWith(searchString) || x.lowercaseAbbreviation.startsWith(searchString) || x.abbreviation.includes(search) || x.search === x.timezone);
6484
+ }
6485
+ const timezoneStringToSearchableStringReplace = replaceStringsFunction({
6486
+ replace: ['/', '_'],
6487
+ replaceWith: ' '
6488
+ });
6582
6489
  /**
6583
- * ArkType schema that validates a value is a valid {@link DateCellRange} (non-negative indexes, `to >= i`).
6490
+ * Converts a timezone identifier into a lowercase, space-separated string for search indexing.
6491
+ *
6492
+ * Replaces `/` and `_` with spaces (e.g., `"America/New_York"` becomes `"america new york"`).
6493
+ *
6494
+ * @example
6495
+ * ```ts
6496
+ * timezoneStringToSearchableString('America/New_York'); // 'america new york'
6497
+ * ```
6584
6498
  */
6585
- const validDateCellRangeType = type('unknown').narrow((val, ctx) => isValidDateCellRange(val) || ctx.mustBe('a valid DateCellRange'));
6499
+ function timezoneStringToSearchableString(timezone) {
6500
+ return timezoneStringToSearchableStringReplace(timezone.toLocaleLowerCase());
6501
+ }
6586
6502
  /**
6587
- * ArkType schema that validates a value is a sorted array of non-overlapping {@link DateCellRange} values.
6503
+ * Checks whether the input string is a recognized IANA timezone identifier.
6504
+ *
6505
+ * Uses the cached set from {@link allKnownTimezoneStrings} for O(1) lookup.
6506
+ *
6507
+ * @example
6508
+ * ```ts
6509
+ * isKnownTimezone('America/New_York'); // true
6510
+ * isKnownTimezone('Mars/Olympus'); // false
6511
+ * ```
6588
6512
  */
6589
- const validDateCellRangeSeriesType = type('unknown').narrow((val, ctx) => isValidDateCellRangeSeries(val) || ctx.mustBe('a valid DateCellRange series with items sorted in ascending order and no repeat indexes'));
6513
+ function isKnownTimezone(input) {
6514
+ return allKnownTimezoneStrings().has(input);
6515
+ }
6590
6516
 
6591
6517
  /**
6592
6518
  * Distinguishes between time-based calendar events and all-day/multi-day events.
@@ -6602,12 +6528,6 @@ var CalendarDateType;
6602
6528
  */
6603
6529
  CalendarDateType["DAYS"] = "days";
6604
6530
  })(CalendarDateType || (CalendarDateType = {}));
6605
- /**
6606
- * ArkType schema for {@link CalendarDate}.
6607
- */
6608
- const calendarDateType = dateDurationSpanType.merge({
6609
- type: type.enumerated(...Object.values(CalendarDateType))
6610
- });
6611
6531
  /**
6612
6532
  * Creates a {@link CalendarDateFactory} that produces all-day calendar events with timezone-aware start times.
6613
6533
  *
@@ -6677,6 +6597,151 @@ function calendarDateForDateDurationSpan(dateDurationSpan) {
6677
6597
  };
6678
6598
  }
6679
6599
 
6600
+ // MARK: Timezone
6601
+ /**
6602
+ * ArkType DTO schema that validates a string is a recognized IANA timezone.
6603
+ *
6604
+ * Delegates to {@link isKnownTimezone} for the actual check.
6605
+ *
6606
+ * Intended for validating and parsing DTO/JSON data where timezones arrive as strings.
6607
+ *
6608
+ * @example
6609
+ * ```ts
6610
+ * const result = knownTimezoneType('America/Denver');
6611
+ * ```
6612
+ */
6613
+ const knownTimezoneType = type('string > 0').narrow((val, ctx) => (val != null && isKnownTimezone(val)) || ctx.mustBe('a known timezone'));
6614
+ // MARK: DateDurationSpan
6615
+ /**
6616
+ * ArkType DTO schema for {@link DateDurationSpan}.
6617
+ *
6618
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6619
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6620
+ * into the corresponding runtime types.
6621
+ */
6622
+ const dateDurationSpanType = type({
6623
+ startsAt: 'string.date.parse',
6624
+ duration: 'number >= 0'
6625
+ });
6626
+ // MARK: DateRange
6627
+ /**
6628
+ * ArkType DTO schema for {@link DateRange}.
6629
+ *
6630
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6631
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6632
+ * into the corresponding runtime types.
6633
+ */
6634
+ const dateRangeType = type({
6635
+ start: 'string.date.parse',
6636
+ end: 'string.date.parse'
6637
+ });
6638
+ // MARK: DateRangeParams
6639
+ /**
6640
+ * ArkType DTO schema for {@link DateRangeParams}.
6641
+ *
6642
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6643
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6644
+ * into the corresponding runtime types.
6645
+ */
6646
+ const dateRangeParamsType = type({
6647
+ type: type.enumerated(...Object.values(DateRangeType)),
6648
+ date: 'string.date.parse',
6649
+ 'distance?': 'number'
6650
+ });
6651
+ // MARK: DateCell
6652
+ /**
6653
+ * ArkType DTO schema for {@link DateCell}.
6654
+ *
6655
+ * Validates a cell index from JSON/DTO input.
6656
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6657
+ * into the corresponding runtime types.
6658
+ */
6659
+ const dateCellType = type({
6660
+ i: 'number.integer >= 0'
6661
+ });
6662
+ // MARK: DateCellRange
6663
+ /**
6664
+ * ArkType DTO schema for {@link DateCellRange}.
6665
+ *
6666
+ * Validates cell range indexes from JSON/DTO input.
6667
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6668
+ * into the corresponding runtime types.
6669
+ */
6670
+ const dateCellRangeType = dateCellType.merge({
6671
+ 'to?': 'number.integer >= 0'
6672
+ });
6673
+ // MARK: DateCellTiming
6674
+ /**
6675
+ * ArkType DTO schema for {@link DateCellTiming}.
6676
+ *
6677
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6678
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6679
+ * into the corresponding runtime types.
6680
+ */
6681
+ const dateCellTimingType = dateDurationSpanType.merge({
6682
+ end: 'string.date.parse',
6683
+ timezone: knownTimezoneType
6684
+ });
6685
+ // MARK: CalendarDate
6686
+ /**
6687
+ * ArkType DTO schema for {@link CalendarDate}.
6688
+ *
6689
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6690
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6691
+ * into the corresponding runtime types.
6692
+ */
6693
+ const calendarDateType = dateDurationSpanType.merge({
6694
+ type: type.enumerated(...Object.values(CalendarDateType))
6695
+ });
6696
+ // MARK: DateCellSchedule
6697
+ /**
6698
+ * ArkType DTO schema for {@link DateCellSchedule}.
6699
+ *
6700
+ * Validates schedule data from JSON/DTO input.
6701
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6702
+ * into the corresponding runtime types.
6703
+ */
6704
+ const dateCellScheduleType = type({
6705
+ w: [DATE_CELL_SCHEDULE_ENCODED_WEEK_REGEX, '&', 'string'],
6706
+ 'd?': 'number.integer >= 0 []',
6707
+ 'ex?': 'number.integer >= 0 []'
6708
+ });
6709
+ // MARK: ModelRecurrenceInfo
6710
+ /**
6711
+ * ArkType DTO schema for {@link ModelRecurrenceInfo}.
6712
+ *
6713
+ * Parses date strings from JSON/DTO input into Date objects using `string.date.parse`.
6714
+ * Use this schema when validating and converting serialized data (e.g., API responses, JSON payloads)
6715
+ * into the corresponding runtime types.
6716
+ */
6717
+ const modelRecurrenceInfoType = type({
6718
+ 'timezone?': 'string',
6719
+ rrule: 'string',
6720
+ start: 'string.date.parse',
6721
+ end: 'string.date.parse',
6722
+ 'forever?': 'boolean'
6723
+ });
6724
+ // MARK: Validators
6725
+ /**
6726
+ * ArkType DTO schema that validates a value is a valid {@link DateCellTiming}.
6727
+ *
6728
+ * Parses date strings from JSON/DTO input into Date objects, then validates the resulting timing
6729
+ * using {@link isValidDateCellTiming}.
6730
+ */
6731
+ const validDateCellTimingType = dateCellTimingType.narrow((val, ctx) => (val != null && isValidDateCellTiming(val)) || ctx.mustBe('a valid DateCellTiming'));
6732
+ /**
6733
+ * ArkType DTO schema that validates a value is a valid {@link DateCellRange} (non-negative indexes, `to >= i`).
6734
+ *
6735
+ * Validates cell range data from JSON/DTO input.
6736
+ */
6737
+ const validDateCellRangeType = dateCellRangeType.narrow((val, ctx) => (val != null && isValidDateCellRange(val)) || ctx.mustBe('a valid DateCellRange'));
6738
+ /**
6739
+ * ArkType DTO schema that validates a value is a sorted array of non-overlapping {@link DateCellRange} values.
6740
+ *
6741
+ * Validates cell range series data from JSON/DTO input.
6742
+ */
6743
+ const validDateCellRangeSeriesType = type(dateCellRangeType.array()).narrow((val, ctx) => (val != null && isValidDateCellRangeSeries(val)) || ctx.mustBe('a valid DateCellRange series with items sorted in ascending order and no repeat indexes'));
6744
+
6680
6745
  /**
6681
6746
  * A {@link HashSet} specialized for Date values, using the millisecond timestamp as the hash key.
6682
6747
  *
@@ -8780,16 +8845,6 @@ class DateRRuleUtility {
8780
8845
  }
8781
8846
  }
8782
8847
 
8783
- /**
8784
- * ArkType schema for {@link ModelRecurrenceInfo}.
8785
- */
8786
- const modelRecurrenceInfoType = type({
8787
- 'timezone?': 'string',
8788
- rrule: 'string',
8789
- start: 'Date',
8790
- end: 'Date',
8791
- 'forever?': 'boolean'
8792
- });
8793
8848
  /**
8794
8849
  * Stateless utility for converting between recurrence input
8795
8850
  * ({@link ModelRecurrenceStart}) and the indexed storage form