@dereekb/date 10.1.29 → 10.2.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/index.cjs.js CHANGED
@@ -1973,6 +1973,8 @@ function utcDayForDate(date) {
1973
1973
  * For example, if it is currently 9PM:
1974
1974
  * - if 10PM on any day is passed then 9PM the next day will be returned.
1975
1975
  * - if 11PM on any day is passed, 11PM today will be returned.
1976
+ *
1977
+ * @deprecated Fails in the rare case where it is the first two hours of a day in a daylight savings zone when daylight savings changes.
1976
1978
  */
1977
1979
  function takeNextUpcomingTime(date, roundDownToMinute) {
1978
1980
  date = copyHoursAndMinutesFromDateToToday(date, roundDownToMinute);
@@ -1983,18 +1985,24 @@ function takeNextUpcomingTime(date, roundDownToMinute) {
1983
1985
  }
1984
1986
  /**
1985
1987
  * Creates a new date and copies the hours/minutes from the previous date and applies them to a date for today.
1988
+ *
1989
+ * @deprecated Fails in the rare case where it is the first two hours of a day in a daylight savings zone when daylight savings changes.
1986
1990
  */
1987
1991
  function copyHoursAndMinutesFromDateToToday(fromDate, roundDownToMinute) {
1988
1992
  return copyHoursAndMinutesFromDate(new Date(), fromDate, roundDownToMinute);
1989
1993
  }
1990
1994
  /**
1991
1995
  * Copies the hours/minutes from now to the target date.
1996
+ *
1997
+ * @deprecated Fails in the rare case where it is the first two hours of a day in a daylight savings zone when daylight savings changes.
1992
1998
  */
1993
1999
  function copyHoursAndMinutesFromNow(target, roundDownToMinute) {
1994
2000
  return copyHoursAndMinutesFromDate(target, new Date(), roundDownToMinute);
1995
2001
  }
1996
2002
  /**
1997
2003
  * Creates a new date and copies the hours/minutes from the input date to the target date.
2004
+ *
2005
+ * @deprecated Fails in the rare case where it is the first two hours of a day in a daylight savings zone when daylight savings changes.
1998
2006
  */
1999
2007
  function copyHoursAndMinutesFromDate(target, fromDate, roundDownToMinute) {
2000
2008
  return copyHoursAndMinutesToDate({
@@ -2034,22 +2042,56 @@ const copyHoursAndMinutesToToday = copyHoursAndMinutesToDate;
2034
2042
  * Removes the seconds and milliseconds from the input date, or returns the current date with no seconds or milliseconds.
2035
2043
  */
2036
2044
  function roundDownToMinute(date = new Date()) {
2037
- return dateFns.set(date, {
2038
- seconds: 0,
2039
- milliseconds: 0
2040
- });
2045
+ return roundDateDownTo(date, 'minute');
2041
2046
  }
2042
2047
  /**
2043
- * Removes all minutes,
2048
+ * Removes the minutes, seconds and milliseconds from the input date, or returns the current date with no mkinutes, seconds or milliseconds.
2049
+ */
2050
+ function roundDownToHour(date = new Date()) {
2051
+ return roundDateDownTo(date, 'hour');
2052
+ }
2053
+ function roundDateDownTo(date, roundToUnit) {
2054
+ return roundDateToDate(date, roundToUnit, 'floor');
2055
+ }
2056
+ function roundDateTo(date, roundToUnit, roundType = 'floor') {
2057
+ return typeof date === 'number' ? roundDateToUnixDateTimeNumber(date, roundToUnit, roundType) : roundDateTo(date, roundToUnit, roundType);
2058
+ }
2059
+ function roundDateToDate(date, roundToUnit, roundType = 'floor') {
2060
+ return new Date(roundDateToUnixDateTimeNumber(date, roundToUnit, roundType));
2061
+ }
2062
+ /**
2063
+ * Rounds the input Date value down to the nearest hour, minute, or second.
2064
+ *
2044
2065
  * @param date
2066
+ * @param roundToUnit
2067
+ * @param roundType
2045
2068
  * @returns
2046
2069
  */
2047
- function roundDownToHour(date) {
2048
- return dateFns.set(date, {
2049
- minutes: 0,
2050
- seconds: 0,
2051
- milliseconds: 0
2052
- });
2070
+ function roundDateToUnixDateTimeNumber(date, roundToUnit, roundType = 'floor') {
2071
+ const inputTimeUnrounded = typeof date === 'number' ? date : date.getTime();
2072
+ let roundAmount = 0;
2073
+ switch (roundToUnit) {
2074
+ case 'hour':
2075
+ roundAmount = util.MS_IN_HOUR;
2076
+ break;
2077
+ case 'second':
2078
+ roundAmount = util.MS_IN_SECOND;
2079
+ break;
2080
+ default:
2081
+ case 'minute':
2082
+ roundAmount = util.MS_IN_MINUTE;
2083
+ break;
2084
+ }
2085
+ const secondsAndMs = inputTimeUnrounded % roundAmount; // determine the number of seconds and milliseconds (prepare to round to nearest minute)
2086
+ let roundedTime = inputTimeUnrounded;
2087
+ if (secondsAndMs !== 0) {
2088
+ roundedTime = inputTimeUnrounded - secondsAndMs; // remove seconds and ms as it will throw off the final tzOffset
2089
+ if (roundType === 'ceil') {
2090
+ roundedTime += roundAmount; // round up by adding the units
2091
+ }
2092
+ }
2093
+
2094
+ return roundedTime;
2053
2095
  }
2054
2096
  function reduceDatesFunction(reduceDates) {
2055
2097
  return inputDates => {
@@ -2840,6 +2882,58 @@ function isSameDurationSpan(a, b) {
2840
2882
  return util.safeCompareEquality(a, b, (a, b) => a.duration === b.duration && isSameDate(a.startsAt, b.startsAt));
2841
2883
  }
2842
2884
 
2885
+ const DATE_TODAY_START_VALUE = 'today_start';
2886
+ const DATE_TODAY_END_VALUE = 'today_end';
2887
+ const DATE_WEEK_START_VALUE = 'this_week_start';
2888
+ const DATE_WEEK_END_VALUE = 'this_week_end';
2889
+ function logicalDateStringCodeDateFactory(logicalDateStringCode) {
2890
+ let mapFn;
2891
+ switch (logicalDateStringCode.toLocaleLowerCase()) {
2892
+ case util.DATE_NOW_VALUE:
2893
+ mapFn = util.mapIdentityFunction();
2894
+ break;
2895
+ case DATE_TODAY_START_VALUE:
2896
+ mapFn = dateFns.startOfDay;
2897
+ break;
2898
+ case DATE_TODAY_END_VALUE:
2899
+ mapFn = dateFns.endOfDay;
2900
+ break;
2901
+ case DATE_WEEK_START_VALUE:
2902
+ mapFn = dateFns.startOfWeek;
2903
+ break;
2904
+ case DATE_WEEK_END_VALUE:
2905
+ mapFn = dateFns.endOfWeek;
2906
+ break;
2907
+ default:
2908
+ throw new Error(`Unknown logical date string "${logicalDateStringCode}"`);
2909
+ }
2910
+ return (now = new Date()) => mapFn(now);
2911
+ }
2912
+ function dateFromLogicalDate(logicalDate, now = new Date()) {
2913
+ let result;
2914
+ if (typeof logicalDate === 'string') {
2915
+ result = logicalDateStringCodeDateFactory(logicalDate)(now);
2916
+ } else {
2917
+ result = logicalDate;
2918
+ }
2919
+ return result;
2920
+ }
2921
+ function isLogicalDateStringCode(logicalDate) {
2922
+ let isLogicalDateStringCode = false;
2923
+ if (typeof logicalDate === 'string') {
2924
+ switch (logicalDate.toLocaleLowerCase()) {
2925
+ case util.DATE_NOW_VALUE:
2926
+ case DATE_TODAY_START_VALUE:
2927
+ case DATE_TODAY_END_VALUE:
2928
+ case DATE_WEEK_START_VALUE:
2929
+ case DATE_WEEK_END_VALUE:
2930
+ isLogicalDateStringCode = true;
2931
+ break;
2932
+ }
2933
+ }
2934
+ return isLogicalDateStringCode;
2935
+ }
2936
+
2843
2937
  /**
2844
2938
  * Returns true if any DateTimezoneConversionConfig configuration value is provided.
2845
2939
  *
@@ -2874,7 +2968,8 @@ function isSameDateTimezoneConversionConfig(a, b) {
2874
2968
  * @returns
2875
2969
  */
2876
2970
  function getCurrentSystemOffsetInMs(date) {
2877
- return minutesToMs(getCurrentSystemOffsetInMinutes(date));
2971
+ const systemTimezone = requireCurrentTimezone();
2972
+ return calculateTimezoneOffset(systemTimezone, date);
2878
2973
  }
2879
2974
  /**
2880
2975
  * Returns the current system time offset in hours.
@@ -2883,20 +2978,25 @@ function getCurrentSystemOffsetInMs(date) {
2883
2978
  * @returns
2884
2979
  */
2885
2980
  function getCurrentSystemOffsetInHours(date) {
2886
- return dateFns.minutesToHours(getCurrentSystemOffsetInMinutes(date));
2981
+ return dateFns.millisecondsToHours(getCurrentSystemOffsetInMs(date));
2887
2982
  }
2888
2983
  /**
2889
- * Equivalent to -date.getTimezoneOffset().
2984
+ * Returns the system offset for the input date, in minutes.
2985
+ *
2986
+ * The offset corresponds positively with the UTC offset, so UTC-6 is negative 6 hours, in milliseconds.
2890
2987
  *
2891
2988
  * @param date
2892
2989
  * @returns
2893
2990
  */
2894
2991
  function getCurrentSystemOffsetInMinutes(date) {
2895
- return -date.getTimezoneOffset();
2992
+ return dateFns.millisecondsToMinutes(getCurrentSystemOffsetInMs(date));
2896
2993
  }
2897
2994
  /**
2898
2995
  * Returns the timezone offset in milliseconds.
2899
2996
  *
2997
+ * This is preferential to Date.getTimezoneOffset() or date-fns's getTimezoneOffset() as those are currently wrong for the first
2998
+ * two hours when daylight savings changes.
2999
+ *
2900
3000
  * I.E. GMT-5 = -5 hours (in milliseconds)
2901
3001
  *
2902
3002
  * @param timezone
@@ -2910,14 +3010,17 @@ function calculateTimezoneOffset(timezone, date) {
2910
3010
 
2911
3011
  const tzOffset = getTimezoneOffset(timezone, date);
2912
3012
  */
2913
- // WORKAROUND: This is the current workaround. Performance hit seems negligible for all UI use cases.
2914
- const zoneDate = dateFnsTz.utcToZonedTime(date, timezone);
3013
+ /*
3014
+ * WORKAROUND: This is the current workaround. Performance hit seems negligible for all UI use cases.
3015
+ */
3016
+ // inputTimeDate.setSeconds(0); // NOTE: setting seconds/milliseconds during the daylight savings epoch will also remove an hour
3017
+ // inputTimeDate.setMilliseconds(0); // do not clear seconds in this way.
3018
+ const inputTimeUnrounded = date.getTime();
3019
+ const secondsAndMs = inputTimeUnrounded % util.MS_IN_MINUTE; // determine the number of seconds and milliseconds (prepare to round to nearest minute)
3020
+ const inputTime = inputTimeUnrounded - secondsAndMs; // remove seconds and ms as it will throw off the final tzOffset
3021
+ const zoneDate = dateFnsTz.utcToZonedTime(inputTime, timezone);
2915
3022
  const zoneDateStr = dateFnsTz.format(zoneDate, 'yyyy-MM-dd HH:mm'); // ignore seconds, etc.
2916
3023
  const zoneDateTime = new Date(zoneDateStr + 'Z').getTime();
2917
- const inputTime = dateFns.set(date, {
2918
- seconds: 0,
2919
- milliseconds: 0
2920
- }).getTime();
2921
3024
  const tzOffset = zoneDateTime - inputTime;
2922
3025
  return tzOffset;
2923
3026
  }
@@ -2976,6 +3079,7 @@ class DateTimezoneUtcNormalInstance {
2976
3079
  return timezone !== null && timezone !== void 0 ? timezone : this.config.useSystemTimezone ? guessCurrentTimezone() : undefined;
2977
3080
  }
2978
3081
  constructor(config) {
3082
+ this._setOnDate = util.cachedGetter(() => setOnDateWithTimezoneNormalFunction(this));
2979
3083
  let getOffsetInMsFn;
2980
3084
  function useTimezone(timezone) {
2981
3085
  getOffsetInMsFn = date => calculateTimezoneOffset(timezone, date);
@@ -3045,10 +3149,44 @@ class DateTimezoneUtcNormalInstance {
3045
3149
  }
3046
3150
  this.hasConversion = hasConversion;
3047
3151
  }
3048
- _computeOffsetDate(date, from, to) {
3152
+ convertDate(date, from, to) {
3049
3153
  return dateFns.addMilliseconds(date, this._getOffset(date, from, to));
3050
3154
  }
3155
+ /**
3156
+ * A "safer" conversion that will return a "mirrored" offset. Only functional with a "to" UTC value.
3157
+ *
3158
+ * This is required in cases where "reverse" offset will be used and must be consistent so they reverse in both directions the same amount compared to the base.
3159
+ *
3160
+ * For example, when daylight savings changed on November 3, 2024 the offset returned was 5 but to get back to the original an offset of 6 was required.
3161
+ * This is where some contextual data was not being used. This function uses that contextual data to make sure the reverse will be consistent.
3162
+ *
3163
+ * @param baseDate The base date. Should have been derived from the originalContextDate using the convertDate() function
3164
+ * @param originalContextDate Original date used to derive the baseDate.
3165
+ * @param fromOrTo the "type" of date the originalContextDate is
3166
+ */
3167
+ safeMirroredConvertDate(baseDate, originalContextDate, contextType, safeConvert = true) {
3168
+ if (contextType === 'base') {
3169
+ return {
3170
+ date: baseDate,
3171
+ daylightSavingsOffset: 0
3172
+ };
3173
+ } else {
3174
+ const reverseConversion = this.convertDate(baseDate, contextType, 'base');
3175
+ // in some cases where daylight savings ends (november 3rd),
3176
+ // the input startsAt time will not be properly recovered due to loss of timezone information
3177
+ // (cannot determine whether or not to apply the -5 or -6 offset after daylight savings ends)
3178
+ const daylightSavingsOffset = safeConvert ? dateFns.differenceInHours(originalContextDate, reverseConversion) : 0;
3179
+ const date = daylightSavingsOffset ? dateFns.addHours(reverseConversion, daylightSavingsOffset) : reverseConversion;
3180
+ return {
3181
+ date,
3182
+ daylightSavingsOffset
3183
+ };
3184
+ }
3185
+ }
3051
3186
  // MARK: DateTimezoneBaseDateConverter
3187
+ get setOnDate() {
3188
+ return this._setOnDate();
3189
+ }
3052
3190
  getCurrentOffset(date, from, to) {
3053
3191
  return this._getOffset(date, from, to);
3054
3192
  }
@@ -3062,22 +3200,22 @@ class DateTimezoneUtcNormalInstance {
3062
3200
  return transformDateRangeToTimezoneFunction(this, transformType);
3063
3201
  }
3064
3202
  targetDateToBaseDate(date) {
3065
- return this._computeOffsetDate(date, 'target', 'base');
3203
+ return this.convertDate(date, 'target', 'base');
3066
3204
  }
3067
3205
  baseDateToTargetDate(date) {
3068
- return this._computeOffsetDate(date, 'base', 'target');
3206
+ return this.convertDate(date, 'base', 'target');
3069
3207
  }
3070
3208
  baseDateToSystemDate(date) {
3071
- return this._computeOffsetDate(date, 'base', 'system');
3209
+ return this.convertDate(date, 'base', 'system');
3072
3210
  }
3073
3211
  systemDateToBaseDate(date) {
3074
- return this._computeOffsetDate(date, 'system', 'base');
3212
+ return this.convertDate(date, 'system', 'base');
3075
3213
  }
3076
3214
  targetDateToSystemDate(date) {
3077
- return this._computeOffsetDate(date, 'target', 'system');
3215
+ return this.convertDate(date, 'target', 'system');
3078
3216
  }
3079
3217
  systemDateToTargetDate(date) {
3080
- return this._computeOffsetDate(date, 'system', 'target');
3218
+ return this.convertDate(date, 'system', 'target');
3081
3219
  }
3082
3220
  getOffset(date, transform) {
3083
3221
  return this[`${transform}Offset`](date);
@@ -3106,6 +3244,9 @@ class DateTimezoneUtcNormalInstance {
3106
3244
  systemDateToTargetDateOffset(date) {
3107
3245
  return this._getOffset(date, 'system', 'target');
3108
3246
  }
3247
+ conversionOffset(date, from, to) {
3248
+ return this._getOffset(date, from, to);
3249
+ }
3109
3250
  calculateAllOffsets(date, map) {
3110
3251
  return calculateAllConversions(date, this, map);
3111
3252
  }
@@ -3317,6 +3458,77 @@ function startOfDayInTimezoneDayStringFactory(timezone) {
3317
3458
  function startOfDayInTimezoneFromISO8601DayString(day, timezone) {
3318
3459
  return startOfDayInTimezoneDayStringFactory(timezone)(day);
3319
3460
  }
3461
+ /**
3462
+ * Creates a new SetONDateFunction using the input
3463
+ */
3464
+ function setOnDateWithTimezoneNormalFunction(timezone) {
3465
+ const timezoneInstance = dateTimezoneUtcNormal(timezone);
3466
+ const fn = input => {
3467
+ const {
3468
+ date: inputDate,
3469
+ copyFrom: copyFromInput,
3470
+ copyHours,
3471
+ copyMinutes,
3472
+ inputType: inputInputType,
3473
+ outputType,
3474
+ hours: inputHours,
3475
+ minutes: inputMinutes,
3476
+ roundDownToMinute
3477
+ } = input;
3478
+ const DEFAULT_TYPE = 'target';
3479
+ const inputType = inputInputType !== null && inputInputType !== void 0 ? inputInputType : DEFAULT_TYPE;
3480
+ let baseDate;
3481
+ let copyFrom;
3482
+ // set copyFrom
3483
+ if (copyFromInput != null) {
3484
+ copyFrom = dateFromLogicalDate(copyFromInput); // read the logical date and set initial value
3485
+ // if the input matches the copyFrom values, then skip conversion
3486
+ // this step is also crucial for returning the correct value for daylight savings ending changes
3487
+ if (inputDate != null && isSameDate(copyFrom, inputDate) && copyHours !== false && copyMinutes !== false) {
3488
+ return roundDownToMinute ? roundDateDownTo(inputDate, 'minute') : inputDate;
3489
+ }
3490
+ if (inputType !== 'base') {
3491
+ copyFrom = copyFrom != null ? timezoneInstance.convertDate(copyFrom, 'base', inputType) : undefined;
3492
+ }
3493
+ }
3494
+ // set baseDate
3495
+ if (inputDate != null) {
3496
+ if (inputType === 'base') {
3497
+ // use dates directly as UTC
3498
+ baseDate = inputDate;
3499
+ } else {
3500
+ baseDate = timezoneInstance.convertDate(inputDate, 'base', inputType);
3501
+ }
3502
+ } else {
3503
+ baseDate = new Date();
3504
+ }
3505
+ const hours = inputHours !== null && inputHours !== void 0 ? inputHours : copyHours !== false ? copyFrom === null || copyFrom === void 0 ? void 0 : copyFrom.getUTCHours() : undefined;
3506
+ const minutes = inputMinutes !== null && inputMinutes !== void 0 ? inputMinutes : copyMinutes !== false ? copyFrom === null || copyFrom === void 0 ? void 0 : copyFrom.getUTCMinutes() : undefined;
3507
+ // NOTE: We do the math this way to avoid issues surrounding daylight savings
3508
+ const time = baseDate.getTime();
3509
+ const currentDayMillseconds = time % util.MS_IN_DAY;
3510
+ const minutesSecondsAndMillseconds = currentDayMillseconds % util.MS_IN_HOUR;
3511
+ const hoursInTimeInMs = currentDayMillseconds - minutesSecondsAndMillseconds;
3512
+ const secondsAndMilliseconds = minutesSecondsAndMillseconds % util.MS_IN_MINUTE;
3513
+ const minutesInTime = minutesSecondsAndMillseconds - secondsAndMilliseconds;
3514
+ const nextDay = time - currentDayMillseconds;
3515
+ const nextMinutes = minutes != null ? minutes * util.MS_IN_MINUTE : minutesInTime;
3516
+ const nextHours = hours != null ? hours * util.MS_IN_HOUR : hoursInTimeInMs;
3517
+ const nextTime = nextDay + nextHours + nextMinutes + (roundDownToMinute ? 0 : secondsAndMilliseconds);
3518
+ const nextBaseDate = new Date(nextTime);
3519
+ let result = timezoneInstance.convertDate(nextBaseDate, outputType !== null && outputType !== void 0 ? outputType : inputType, 'base');
3520
+ // one more test to limit the "range" of the change
3521
+ // if it is over 1 day, then we can infer there is a timezone mismatch issue. It only occurs in one direction here, so we can safely
3522
+ // infer that the real valid result can be derived by subtracting one day
3523
+ const inputToResultDifferenceInHours = inputDate != null ? dateFns.differenceInHours(result, inputDate) : 0;
3524
+ if (inputToResultDifferenceInHours >= 24) {
3525
+ result = dateFns.addHours(result, -24);
3526
+ }
3527
+ return result;
3528
+ };
3529
+ fn._timezoneInstance = timezoneInstance;
3530
+ return fn;
3531
+ }
3320
3532
  // MARK: Timezone Utilities
3321
3533
  /**
3322
3534
  * Convenience function for calling copyHoursAndMinutesFromDatesWithTimezoneNormal() with now.
@@ -3326,10 +3538,10 @@ function startOfDayInTimezoneFromISO8601DayString(day, timezone) {
3326
3538
  * @returns
3327
3539
  */
3328
3540
  function copyHoursAndMinutesFromNowWithTimezoneNormal(input, timezone) {
3329
- return copyHoursAndMinutesFromDateWithTimezoneNormal(input, new Date(), timezone);
3541
+ return copyHoursAndMinutesFromDateWithTimezoneNormal(input, 'now', timezone);
3330
3542
  }
3331
3543
  /**
3332
- * Converts the two input dates, which are dates in the same timezone/normal but than the current system, using the input DateTimezoneUtcNormalFunctionInput.
3544
+ * Converts the two input dates, which are dates in the same timezone/normal instead of the current system, using the input DateTimezoneUtcNormalFunctionInput.
3333
3545
  *
3334
3546
  * This converts the dates to the system timezone normal, copies the values, then back to the original timezone normal.
3335
3547
  *
@@ -3339,15 +3551,13 @@ function copyHoursAndMinutesFromNowWithTimezoneNormal(input, timezone) {
3339
3551
  */
3340
3552
  function copyHoursAndMinutesFromDateWithTimezoneNormal(input, copyFrom, timezone) {
3341
3553
  const timezoneInstance = dateTimezoneUtcNormal(timezone);
3342
- const inputInSystemTimezone = timezoneInstance.systemDateToTargetDate(input);
3343
- const copyFromInSystemTimezone = timezoneInstance.systemDateToTargetDate(copyFrom);
3344
- // handle the potential system date time offset when the system's timezone offset changes between dates...
3345
- const inputInSystemTimezoneOffset = inputInSystemTimezone.getTimezoneOffset();
3346
- const copyFromInSystemTimezoneOffset = copyFromInSystemTimezone.getTimezoneOffset();
3347
- const offsetDifference = inputInSystemTimezoneOffset - copyFromInSystemTimezoneOffset;
3348
- // copy the minutes then add the offset difference to get the appropriate time.
3349
- const copiedInSystemTimezone = dateFns.addMinutes(copyHoursAndMinutesFromDate(inputInSystemTimezone, copyFromInSystemTimezone), offsetDifference);
3350
- const result = timezoneInstance.targetDateToSystemDate(copiedInSystemTimezone);
3554
+ const result = timezoneInstance.setOnDate({
3555
+ date: input,
3556
+ copyFrom,
3557
+ inputType: 'target',
3558
+ copyHours: true,
3559
+ copyMinutes: true
3560
+ });
3351
3561
  return result;
3352
3562
  }
3353
3563
 
@@ -3878,9 +4088,7 @@ function IsKnownTimezone(validationOptions) {
3878
4088
  options: validationOptions,
3879
4089
  validator: {
3880
4090
  validate: isKnownTimezone,
3881
- defaultMessage(args) {
3882
- return `"${args.value}" is not a known timezone.`;
3883
- }
4091
+ defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${args === null || args === void 0 ? void 0 : args.value}" is not a known timezone.`, validationOptions)
3884
4092
  }
3885
4093
  });
3886
4094
  };
@@ -4416,7 +4624,8 @@ function dateCellTiming(durationInput, rangeInput, timezoneInput) {
4416
4624
  startsAt: inputStartsAt
4417
4625
  } = durationInput;
4418
4626
  // it is important that startsAt is evaluated the base time normal so we can avoid daylight savings issues
4419
- let startsAtInUtc = normalInstance.baseDateToTargetDate(inputStartsAt);
4627
+ const startsAtInUtcInitial = normalInstance.baseDateToTargetDate(inputStartsAt);
4628
+ let startsAtInUtc = startsAtInUtcInitial;
4420
4629
  let numberOfDayBlocks;
4421
4630
  let hasRangeFromInput = false;
4422
4631
  let rangeInUtc;
@@ -4458,12 +4667,18 @@ function dateCellTiming(durationInput, rangeInput, timezoneInput) {
4458
4667
  startsAtInUtc = roundDownToMinute(startsAtInUtc); // clear seconds and milliseconds from startsAt
4459
4668
  }
4460
4669
 
4461
- const lastStartsAtInBaseTimezone = dateFns.addHours(startsAtInUtc, numberOfDayBlocks * 24); // use addDays so the system (if it experiences daylight savings) can account for change for daylight savings
4462
- // calculate end to be the ending date/time of the final duration span
4463
- const end = dateFns.addMinutes(normalInstance.targetDateToBaseDate(lastStartsAtInBaseTimezone), duration);
4464
4670
  const utcDay = formatToISO8601DayStringForUTC(startsAtInUtc);
4465
4671
  const start = normalInstance.startOfDayInTargetTimezone(utcDay);
4466
- const startsAt = normalInstance.targetDateToBaseDate(startsAtInUtc);
4672
+ const safeMirror = util.isEqualDate(startsAtInUtc, startsAtInUtcInitial);
4673
+ const {
4674
+ date: startsAt,
4675
+ daylightSavingsOffset
4676
+ } = normalInstance.safeMirroredConvertDate(startsAtInUtc, inputStartsAt, 'target', safeMirror);
4677
+ // calculate end to be the ending date/time of the final duration span
4678
+ const lastStartsAtInBaseTimezone = dateFns.addHours(startsAtInUtc, numberOfDayBlocks * 24 + daylightSavingsOffset); // use addHours instead of addDays, since addDays will take into account a daylight savings change if the system time changes
4679
+ const lastStartInTarget = normalInstance.targetDateToBaseDate(lastStartsAtInBaseTimezone);
4680
+ const end = dateFns.addMinutes(lastStartInTarget, duration);
4681
+ // console.log({ lastStartsAtInBaseTimezone, inputStartsAt, startsAtInUtcInitial, startsAtInUtc, startsAt, daylightSavingsOffset, start, lastStartInTarget, end });
4467
4682
  return {
4468
4683
  start,
4469
4684
  end,
@@ -4725,11 +4940,24 @@ function calculateExpectedDateCellTimingDurationPair(timing) {
4725
4940
  timezone
4726
4941
  } = timing;
4727
4942
  const normalInstance = dateTimezoneUtcNormal(timezone);
4728
- const startsAtInUtcNormal = normalInstance.baseDateToTargetDate(startsAt); // convert to UTC normal
4729
- const endInUtcNormal = normalInstance.baseDateToTargetDate(end);
4943
+ let startsAtInUtcNormal = normalInstance.baseDateToTargetDate(startsAt); // convert to UTC normal
4944
+ let endInUtcNormal = normalInstance.baseDateToTargetDate(end);
4945
+ const {
4946
+ daylightSavingsOffset: startDaylightSavingsOffset
4947
+ } = normalInstance.safeMirroredConvertDate(startsAtInUtcNormal, startsAt, 'target');
4948
+ const {
4949
+ daylightSavingsOffset: endDaylightSavingsOffset
4950
+ } = normalInstance.safeMirroredConvertDate(endInUtcNormal, end, 'target');
4951
+ if (startDaylightSavingsOffset) {
4952
+ startsAtInUtcNormal = dateFns.addHours(startsAtInUtcNormal, startDaylightSavingsOffset);
4953
+ }
4954
+ if (endDaylightSavingsOffset) {
4955
+ endInUtcNormal = dateFns.addHours(endInUtcNormal, endDaylightSavingsOffset);
4956
+ }
4730
4957
  const finalMsDifferenceBetweenStartAndEnd = dateFns.differenceInMilliseconds(endInUtcNormal, startsAtInUtcNormal);
4731
4958
  const duration = finalMsDifferenceBetweenStartAndEnd / util.MS_IN_MINUTE % util.MINUTES_IN_DAY || util.MINUTES_IN_DAY;
4732
- const expectedFinalStartsAt = normalInstance.targetDateToBaseDate(dateFns.addMinutes(endInUtcNormal, -duration));
4959
+ const expectedFinalStartsAtUtc = dateFns.addMinutes(endInUtcNormal, -duration);
4960
+ const expectedFinalStartsAt = normalInstance.targetDateToBaseDate(expectedFinalStartsAtUtc); // 2024-11-03T03:00:00.000Z
4733
4961
  return {
4734
4962
  duration,
4735
4963
  expectedFinalStartsAt
@@ -7371,9 +7599,7 @@ function IsValidDateCellTiming(validationOptions) {
7371
7599
  options: validationOptions,
7372
7600
  validator: {
7373
7601
  validate: isValidDateCellTiming,
7374
- defaultMessage(args) {
7375
- return `"${JSON.stringify(args.value)}" is not a valid DateCellTiming.`;
7376
- }
7602
+ defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${JSON.stringify(args === null || args === void 0 ? void 0 : args.value)}" is not a valid DateCellTiming.`, validationOptions)
7377
7603
  }
7378
7604
  });
7379
7605
  };
@@ -7390,9 +7616,7 @@ function IsValidDateCellRange(validationOptions) {
7390
7616
  options: validationOptions,
7391
7617
  validator: {
7392
7618
  validate: isValidDateCellRange,
7393
- defaultMessage(args) {
7394
- return `"${JSON.stringify(args.value)}" is not a valid DateCellRange.`;
7395
- }
7619
+ defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${JSON.stringify(args === null || args === void 0 ? void 0 : args.value)}" is not a valid DateCellRange.`, validationOptions)
7396
7620
  }
7397
7621
  });
7398
7622
  };
@@ -7409,9 +7633,7 @@ function IsValidDateCellRangeSeries(validationOptions) {
7409
7633
  options: validationOptions,
7410
7634
  validator: {
7411
7635
  validate: isValidDateCellRangeSeries,
7412
- defaultMessage(args) {
7413
- return `"${JSON.stringify(args.value)}" is not a valid DateCellRange series. Items must be sorted in ascending order and have no repeat indexes.`;
7414
- }
7636
+ defaultMessage: classValidator.buildMessage((eachPrefix, args) => eachPrefix + `$property value of "${JSON.stringify(args === null || args === void 0 ? void 0 : args.value)}" is not a valid DateCellRange series. Items must be sorted in ascending order and have no repeat indexes.`, validationOptions)
7415
7637
  }
7416
7638
  });
7417
7639
  };
@@ -7480,58 +7702,6 @@ class DateSet extends util.HashSet {
7480
7702
  }
7481
7703
  }
7482
7704
 
7483
- const DATE_TODAY_START_VALUE = 'today_start';
7484
- const DATE_TODAY_END_VALUE = 'today_end';
7485
- const DATE_WEEK_START_VALUE = 'this_week_start';
7486
- const DATE_WEEK_END_VALUE = 'this_week_end';
7487
- function logicalDateStringCodeDateFactory(logicalDateStringCode) {
7488
- let mapFn;
7489
- switch (logicalDateStringCode.toLocaleLowerCase()) {
7490
- case util.DATE_NOW_VALUE:
7491
- mapFn = util.mapIdentityFunction();
7492
- break;
7493
- case DATE_TODAY_START_VALUE:
7494
- mapFn = dateFns.startOfDay;
7495
- break;
7496
- case DATE_TODAY_END_VALUE:
7497
- mapFn = dateFns.endOfDay;
7498
- break;
7499
- case DATE_WEEK_START_VALUE:
7500
- mapFn = dateFns.startOfWeek;
7501
- break;
7502
- case DATE_WEEK_END_VALUE:
7503
- mapFn = dateFns.endOfWeek;
7504
- break;
7505
- default:
7506
- throw new Error(`Unknown logical date string "${logicalDateStringCode}"`);
7507
- }
7508
- return (now = new Date()) => mapFn(now);
7509
- }
7510
- function dateFromLogicalDate(logicalDate, now = new Date()) {
7511
- let result;
7512
- if (typeof logicalDate === 'string') {
7513
- result = logicalDateStringCodeDateFactory(logicalDate)(now);
7514
- } else {
7515
- result = logicalDate;
7516
- }
7517
- return result;
7518
- }
7519
- function isLogicalDateStringCode(logicalDate) {
7520
- let isLogicalDateStringCode = false;
7521
- if (typeof logicalDate === 'string') {
7522
- switch (logicalDate.toLocaleLowerCase()) {
7523
- case util.DATE_NOW_VALUE:
7524
- case DATE_TODAY_START_VALUE:
7525
- case DATE_TODAY_END_VALUE:
7526
- case DATE_WEEK_START_VALUE:
7527
- case DATE_WEEK_END_VALUE:
7528
- isLogicalDateStringCode = true;
7529
- break;
7530
- }
7531
- }
7532
- return isLogicalDateStringCode;
7533
- }
7534
-
7535
7705
  function dateOrDayStringRangeToDateRange(range) {
7536
7706
  return {
7537
7707
  start: toJsDayDate(range.start),
@@ -9717,14 +9887,19 @@ exports.readDaysOfWeekNames = readDaysOfWeekNames;
9717
9887
  exports.readableTimeStringToDate = readableTimeStringToDate;
9718
9888
  exports.reduceDatesFunction = reduceDatesFunction;
9719
9889
  exports.requireCurrentTimezone = requireCurrentTimezone;
9890
+ exports.roundDateDownTo = roundDateDownTo;
9720
9891
  exports.roundDateTimeDown = roundDateTimeDown;
9721
9892
  exports.roundDateTimeDownToSteps = roundDateTimeDownToSteps;
9893
+ exports.roundDateTo = roundDateTo;
9894
+ exports.roundDateToDate = roundDateToDate;
9895
+ exports.roundDateToUnixDateTimeNumber = roundDateToUnixDateTimeNumber;
9722
9896
  exports.roundDownToHour = roundDownToHour;
9723
9897
  exports.roundDownToMinute = roundDownToMinute;
9724
9898
  exports.roundToMinuteSteps = roundToMinuteSteps;
9725
9899
  exports.safeFormatToISO8601DateString = safeFormatToISO8601DateString;
9726
9900
  exports.safeToJsDate = safeToJsDate;
9727
9901
  exports.searchTimezoneInfos = searchTimezoneInfos;
9902
+ exports.setOnDateWithTimezoneNormalFunction = setOnDateWithTimezoneNormalFunction;
9728
9903
  exports.shiftDateCellTimingToSystemTimezone = shiftDateCellTimingToSystemTimezone;
9729
9904
  exports.shiftDateCellTimingToTimezone = shiftDateCellTimingToTimezone;
9730
9905
  exports.shiftDateCellTimingToTimezoneFunction = shiftDateCellTimingToTimezoneFunction;