This commit is contained in:
2026-04-19 13:23:00 +03:00
parent e4c74025e5
commit 1346924387
25 changed files with 1639 additions and 1076 deletions
+82
View File
@@ -0,0 +1,82 @@
function extractDateParts(date: Date, timeZone: string) {
const formatter = new Intl.DateTimeFormat("en-CA", {
timeZone,
year: "numeric",
month: "2-digit",
day: "2-digit",
});
const parts = formatter.formatToParts(date);
const year = Number(parts.find((part) => part.type === "year")?.value);
const month = Number(parts.find((part) => part.type === "month")?.value);
const day = Number(parts.find((part) => part.type === "day")?.value);
return { year, month, day };
}
export function getDateStringInTimeZone(
date: Date,
timeZone: string,
): string {
const { year, month, day } = extractDateParts(date, timeZone);
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}
export function getShiftedDateStringInTimeZone(
daysOffset: number,
timeZone: string,
baseDate: Date = new Date(),
): string {
const { year, month, day } = extractDateParts(baseDate, timeZone);
const shifted = new Date(Date.UTC(year, month - 1, day));
shifted.setUTCDate(shifted.getUTCDate() + daysOffset);
return shifted.toISOString().split("T")[0];
}
function getTimeZoneOffsetMs(date: Date, timeZone: string): number {
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone,
timeZoneName: "shortOffset",
});
const offsetLabel =
formatter.formatToParts(date).find((part) => part.type === "timeZoneName")
?.value || "GMT+0";
const match = offsetLabel.match(/GMT([+-])(\d{1,2})(?::?(\d{2}))?/);
if (!match) return 0;
const sign = match[1] === "-" ? -1 : 1;
const hours = Number(match[2] || "0");
const minutes = Number(match[3] || "0");
return sign * (hours * 60 + minutes) * 60 * 1000;
}
export function getDayBoundsForTimeZone(
dateString: string,
timeZone: string,
): { startMs: number; endMs: number } {
const [year, month, day] = dateString.split("-").map(Number);
const startGuess = new Date(Date.UTC(year, month - 1, day, 0, 0, 0));
const nextDayGuess = new Date(Date.UTC(year, month - 1, day + 1, 0, 0, 0));
const startOffsetMs = getTimeZoneOffsetMs(startGuess, timeZone);
const nextDayOffsetMs = getTimeZoneOffsetMs(nextDayGuess, timeZone);
const startMs = Date.UTC(year, month - 1, day, 0, 0, 0) - startOffsetMs;
const nextDayStartMs =
Date.UTC(year, month - 1, day + 1, 0, 0, 0) - nextDayOffsetMs;
return {
startMs,
endMs: nextDayStartMs - 1,
};
}
export function getDateOnlyValueForTimeZone(
timeZone: string,
date: Date = new Date(),
): Date {
return new Date(`${getDateStringInTimeZone(date, timeZone)}T00:00:00.000Z`);
}