From 8d175505b8e247840494653328db3ca605ea4bd8 Mon Sep 17 00:00:00 2001 From: badosz0 Date: Tue, 20 Aug 2024 12:25:07 +0200 Subject: [PATCH] feat: timezone format --- package.json | 2 +- src/constants.ts | 2 +- src/time.ts | 28 +++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f9435dd..88c8121 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "holy-time", - "version": "5.1.0", + "version": "5.2.0", "description": "Utility functions", "repository": "Yet another (type-safe) date time library", "main": "dist/index.js", diff --git a/src/constants.ts b/src/constants.ts index 2a1b6c8..1ea1e0a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,7 +9,7 @@ export const TimeUnits = { MILLISECOND: 1, } as const; -export const FORMAT_REGEX = /\[(?[^\]]+)]|Y{4}|Y{2}|M{1,4}|D{1,4}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|a|A/g; +export const FORMAT_REGEX = /\[(?[^\]]+)]|Y{4}|Y{2}|M{1,4}|D{1,4}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|a|A|O{1,2}|TZ/g; export const MONTH_NAMES = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; export const DAY_NAMES = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; diff --git a/src/time.ts b/src/time.ts index d8b3070..bb9156a 100644 --- a/src/time.ts +++ b/src/time.ts @@ -211,7 +211,10 @@ export class HolyTime { } public static format(time: TimeResolvable, format: string | ((time: HolyTime) => string), timeZone?: TimeZone): string { - const date = HolyTime.adjustToTimeZone(HolyTime.resolveDate(time), timeZone); + const resolvedDate = HolyTime.resolveDate(time); + const utcDate = HolyTime.adjustToTimeZone(resolvedDate, 'UTC'); + const date = HolyTime.adjustToTimeZone(resolvedDate, timeZone); + const timeZoneOffset = Math.round(this.between(date, utcDate).in('minutes')) * (this.isAfter(date, utcDate) ? -1 : +1); if (typeof format === 'function') { return format(new HolyTime(date)); @@ -237,11 +240,34 @@ export class HolyTime { mm: date.getMinutes().toString().padStart(2, '0'), s: date.getSeconds().toString(), ss: date.getSeconds().toString().padStart(2, '0'), + O: `GMT${this.formatTimeZoneOffset(timeZoneOffset, false)}`, + OO: `GMT${this.formatTimeZoneOffset(timeZoneOffset, true)}`, + TZ: TIMEZONE_MAP[timeZone as keyof typeof TIMEZONE_MAP] ?? timeZone ?? this.getTimeZone(), }; return format.replace(FORMAT_REGEX, (match, group) => group ?? values[match] ?? '?'); } + private static formatTimeZoneOffset(offset: number, long: boolean): string { + const sign = offset > 0 ? '-' : '+'; + + const hours = long + ? this.formatWithLeadingZeros(Math.trunc(Math.abs(offset) / 60), 2) + : Math.trunc(Math.abs(offset) / 60); + const minutes = Math.abs(offset) % 60; + + return minutes === 0 && !long + ? `${sign}${hours}` + : `${sign}${hours}:${this.formatWithLeadingZeros(minutes, 2)}`; + } + + private static formatWithLeadingZeros(number: number, length: number): string { + const sign = number < 0 ? '-' : ''; + const output = Math.abs(number).toString().padStart(length, '0'); + + return `${sign}${output}`; + } + public format(format: string, timeZone?: TimeZone): string { return HolyTime.format(this, format, timeZone); }