inventoresed
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
120 lines (107 loc) • 3.32 kB
text/typescript
import dayjs from "dayjs";
export interface DSTInfo {
startDate: Date;
endDate: Date;
standardOffset: number;
dstOffset: number;
}
/**
* Returns a fallback DSTInfo in case we cannot determine the correct one.
* This fallback has no additional DST shift.
* The dummy DST starts on March 31st and ends on October 31st, both times at 01:00 UTC.
* @param defaultOffset - The offset to use for both standardOffset and dstOffset
*/
export function getDefaultDSTInfo(defaultOffset?: number): DSTInfo {
const thisYear = new Date().getUTCFullYear();
if (defaultOffset == undefined)
defaultOffset = -new Date().getTimezoneOffset();
return {
startDate: new Date(Date.UTC(thisYear, 2, 31, 1)),
endDate: new Date(Date.UTC(thisYear, 9, 31, 1)),
standardOffset: defaultOffset,
dstOffset: defaultOffset,
};
}
/**
* Finds the first date on which the given timezone offset is in effect.
* date1 must be the smaller one of the two dates
*/
function findSwitchDate(
date1: Date,
date2: Date,
offset: number,
): Date | undefined {
const stepSize = 60000; // 1 minute
function middleDate(date1: Date, date2: Date): Date {
const middleTime =
Math.floor((date1.getTime() + date2.getTime()) / 2 / stepSize) *
stepSize;
return new Date(middleTime);
}
while (date1 < date2) {
const mid = middleDate(date1, date2);
if (mid.getTimezoneOffset() === offset) {
date2 = mid;
} else {
date1 = new Date(mid.getTime() + stepSize);
}
}
if (date1.getTimezoneOffset() !== offset) return undefined;
return date1;
}
/** Returns the current system's daylight savings time information if possible */
export function getDSTInfo(now: Date = new Date()): DSTInfo {
const halfAYearAgo = dayjs(now).subtract(6, "months").toDate();
const inAHalfYear = dayjs(now).add(6, "months").toDate();
if (
now.getTimezoneOffset() === halfAYearAgo.getTimezoneOffset() ||
now.getTimezoneOffset() === inAHalfYear.getTimezoneOffset()
) {
// There is no DST in this timezone
return getDefaultDSTInfo();
}
// Javascript has the offsets inverted, we use the normal interpretation
const offsets = [
-now.getTimezoneOffset(),
-inAHalfYear.getTimezoneOffset(),
];
const dates = [
findSwitchDate(halfAYearAgo, now, -offsets[0]),
findSwitchDate(now, inAHalfYear, -offsets[1]),
];
if (dates[0] == undefined || dates[1] == undefined) {
// This shouldn't happen, but better be sure
return getDefaultDSTInfo();
}
if (offsets[0] > offsets[1]) {
// DST is always the higher offset
return {
startDate: dates[0],
endDate: dates[1],
dstOffset: offsets[0],
standardOffset: offsets[1],
};
} else {
return {
// We don't have DST now, this is the next start date
startDate: dates[1],
endDate: dates[0],
dstOffset: offsets[1],
standardOffset: offsets[0],
};
}
}
/** Returns a timestamp with nano-second precision */
export function highResTimestamp(): number {
const [s, ns] = process.hrtime();
return s * 1e9 + ns;
}
export const timespan = Object.freeze({
seconds: (num: number) => num * 1000,
minutes: (num: number) => timespan.seconds(num * 60),
hours: (num: number) => timespan.minutes(num * 60),
days: (num: number) => timespan.hours(num * 24),
});
export function formatDate(date: Date, format: string): string {
return dayjs(date).format(format);
}