2025-11-13 13:13:34 -07:00

208 lines
6.9 KiB
JavaScript

"use strict";
exports.TZDateMini = void 0;
var _index = require("../tzOffset/index.cjs");
class TZDateMini extends Date {
//#region static
constructor(...args) {
super();
if (args.length > 1 && typeof args[args.length - 1] === "string") {
this.timeZone = args.pop();
}
this.internal = new Date();
if (isNaN((0, _index.tzOffset)(this.timeZone, this))) {
this.setTime(NaN);
} else {
if (!args.length) {
this.setTime(Date.now());
} else if (typeof args[0] === "number" && (args.length === 1 || args.length === 2 && typeof args[1] !== "number")) {
this.setTime(args[0]);
} else if (typeof args[0] === "string") {
this.setTime(+new Date(args[0]));
} else if (args[0] instanceof Date) {
this.setTime(+args[0]);
} else {
this.setTime(+new Date(...args));
adjustToSystemTZ(this, NaN);
syncToInternal(this);
}
}
}
static tz(tz, ...args) {
return args.length ? new TZDateMini(...args, tz) : new TZDateMini(Date.now(), tz);
}
//#endregion
//#region time zone
withTimeZone(timeZone) {
return new TZDateMini(+this, timeZone);
}
getTimezoneOffset() {
return -(0, _index.tzOffset)(this.timeZone, this);
}
//#endregion
//#region time
setTime(time) {
Date.prototype.setTime.apply(this, arguments);
syncToInternal(this);
return +this;
}
//#endregion
//#region date-fns integration
[Symbol.for("constructDateFrom")](date) {
return new TZDateMini(+new Date(date), this.timeZone);
}
//#endregion
}
// Assign getters and setters
exports.TZDateMini = TZDateMini;
const re = /^(get|set)(?!UTC)/;
Object.getOwnPropertyNames(Date.prototype).forEach(method => {
if (!re.test(method)) return;
const utcMethod = method.replace(re, "$1UTC");
// Filter out methods without UTC counterparts
if (!TZDateMini.prototype[utcMethod]) return;
if (method.startsWith("get")) {
// Delegate to internal date's UTC method
TZDateMini.prototype[method] = function () {
return this.internal[utcMethod]();
};
} else {
// Assign regular setter
TZDateMini.prototype[method] = function () {
Date.prototype[utcMethod].apply(this.internal, arguments);
syncFromInternal(this);
return +this;
};
// Assign UTC setter
TZDateMini.prototype[utcMethod] = function () {
Date.prototype[utcMethod].apply(this, arguments);
syncToInternal(this);
return +this;
};
}
});
/**
* Function syncs time to internal date, applying the time zone offset.
*
* @param {Date} date - Date to sync
*/
function syncToInternal(date) {
date.internal.setTime(+date);
date.internal.setUTCMinutes(date.internal.getUTCMinutes() - date.getTimezoneOffset());
}
/**
* Function syncs the internal date UTC values to the date. It allows to get
* accurate timestamp value.
*
* @param {Date} date - The date to sync
*/
function syncFromInternal(date) {
// First we transpose the internal values
Date.prototype.setFullYear.call(date, date.internal.getUTCFullYear(), date.internal.getUTCMonth(), date.internal.getUTCDate());
Date.prototype.setHours.call(date, date.internal.getUTCHours(), date.internal.getUTCMinutes(), date.internal.getUTCSeconds(), date.internal.getUTCMilliseconds());
// Now we have to adjust the date to the system time zone
adjustToSystemTZ(date);
}
/**
* Function adjusts the date to the system time zone. It uses the time zone
* differences to calculate the offset and adjust the date.
*
* @param {Date} date - Date to adjust
*/
function adjustToSystemTZ(date) {
// Save the time zone offset before all the adjustments
const offset = (0, _index.tzOffset)(date.timeZone, date);
//#region System DST adjustment
// The biggest problem with using the system time zone is that when we create
// a date from internal values stored in UTC, the system time zone might end
// up on the DST hour:
//
// $ TZ=America/New_York node
// > new Date(2020, 2, 8, 1).toString()
// 'Sun Mar 08 2020 01:00:00 GMT-0500 (Eastern Standard Time)'
// > new Date(2020, 2, 8, 2).toString()
// 'Sun Mar 08 2020 03:00:00 GMT-0400 (Eastern Daylight Time)'
// > new Date(2020, 2, 8, 3).toString()
// 'Sun Mar 08 2020 03:00:00 GMT-0400 (Eastern Daylight Time)'
// > new Date(2020, 2, 8, 4).toString()
// 'Sun Mar 08 2020 04:00:00 GMT-0400 (Eastern Daylight Time)'
//
// Here we get the same hour for both 2 and 3, because the system time zone
// has DST beginning at 8 March 2020, 2 a.m. and jumps to 3 a.m. So we have
// to adjust the internal date to reflect that.
//
// However we want to adjust only if that's the DST hour the change happenes,
// not the hour where DST moves to.
// We calculate the previous hour to see if the time zone offset has changed
// and we have landed on the DST hour.
const prevHour = new Date(+date);
// We use UTC methods here as we don't want to land on the same hour again
// in case of DST.
prevHour.setUTCHours(prevHour.getUTCHours() - 1);
// Calculate if we are on the system DST hour.
const systemOffset = -new Date(+date).getTimezoneOffset();
const prevHourSystemOffset = -new Date(+prevHour).getTimezoneOffset();
const systemDSTChange = systemOffset - prevHourSystemOffset;
// Detect the DST shift. System DST change will occur both on
const dstShift = Date.prototype.getHours.apply(date) !== date.internal.getUTCHours();
// Move the internal date when we are on the system DST hour.
if (systemDSTChange && dstShift) date.internal.setUTCMinutes(date.internal.getUTCMinutes() + systemDSTChange);
//#endregion
//#region System diff adjustment
// Now we need to adjust the date, since we just applied internal values.
// We need to calculate the difference between the system and date time zones
// and apply it to the date.
const offsetDiff = systemOffset - offset;
if (offsetDiff) Date.prototype.setUTCMinutes.call(date, Date.prototype.getUTCMinutes.call(date) + offsetDiff);
//#endregion
//#region Post-adjustment DST fix
const postOffset = (0, _index.tzOffset)(date.timeZone, date);
const postSystemOffset = -new Date(+date).getTimezoneOffset();
const postOffsetDiff = postSystemOffset - postOffset;
const offsetChanged = postOffset !== offset;
const postDiff = postOffsetDiff - offsetDiff;
if (offsetChanged && postDiff) {
Date.prototype.setUTCMinutes.call(date, Date.prototype.getUTCMinutes.call(date) + postDiff);
// Now we need to check if got offset change during the post-adjustment.
// If so, we also need both dates to reflect that.
const newOffset = (0, _index.tzOffset)(date.timeZone, date);
const offsetChange = postOffset - newOffset;
if (offsetChange) {
date.internal.setUTCMinutes(date.internal.getUTCMinutes() + offsetChange);
Date.prototype.setUTCMinutes.call(date, Date.prototype.getUTCMinutes.call(date) + offsetChange);
}
}
//#endregion
}