nhb-toolbox 4.26.21 → 4.26.30

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/CHANGELOG.md CHANGED
@@ -6,11 +6,25 @@ All notable changes to the package will be documented here.
6
6
 
7
7
  ---
8
8
 
9
+ ## [4.26.30] - 2025-11-11
10
+
11
+ ### 🕧 Updates in Chronos
12
+
13
+ - **Fixed** issues in `getTimeZoneName()` and `getTimeZoneNameShort()` where expected outputs were *missing or incorrect*.
14
+ - **Updated** *timezone constants*vby removing *redundant hints* and *improving internal consistency*.
15
+ - **Introduced** a new *method alias* `getTimeZoneNameAbbr()` for `getTimeZoneNameShort()`.
16
+ - **Fixed** an issue where the `clone()` method *did not correctly duplicate the instance state*.
17
+
9
18
  ## [4.26.21] - 2025-11-10
10
19
 
11
- - **Fixed** issues with `Chronos` timezone methods: `getTimeZoneName()` and `getTimeZoneNameShort()` *not providing name/short* name for *optional UTC*.
20
+ ### 🕧 Updates in Chronos
21
+
22
+ - **Fixed** issues with `Chronos` timezone methods: `getTimeZoneName()` and `getTimeZoneNameShort()` *not providing name/short name* for *optional UTC*.
12
23
  - **Fixed** issues with `Chronos` `diff()` method: now *calculates exact differences for month and year* too.
13
24
  - **Changed** the signature of `Chronos` `set()` method to `set<Unit extends TimeUnit>(unit: Unit, value: TimeUnitValue<Unit>): Chronos`. **Created** new type helper `TimeUnitValue<Unit>`.
25
+
26
+ ### 🛠️ Other Updates
27
+
14
28
  - **Renamed** `isValidUTCOffSet` *guard* as `isValidUTCOffset` and also kept `isValidUTCOffSet` as alias.
15
29
 
16
30
  ## [4.26.20] - 2025-11-10
@@ -284,7 +284,7 @@ class Chronos {
284
284
  return this.getTimeStamp();
285
285
  }
286
286
  clone() {
287
- return new _a(this.#date).#withOrigin(this.#ORIGIN);
287
+ return new _a(this.#date).#withOrigin(this.#ORIGIN, this.#offset, this.timeZoneName, this.timeZoneId, this.$tzTracker);
288
288
  }
289
289
  toDate() {
290
290
  switch (this.#ORIGIN) {
@@ -7,6 +7,13 @@ const timezone_1 = require("../timezone");
7
7
  const utils_1 = require("../utils");
8
8
  const timeZonePlugin = (ChronosClass) => {
9
9
  const { internalDate: $Date, withOrigin } = ChronosClass[constants_1.INTERNALS];
10
+ const _isGMT = (factor) => {
11
+ return factor === 'UTC+00:00' || factor === 'UTC-00:00';
12
+ };
13
+ const TZ_NAME_ABBR_MAP = new Map(Object.entries(timezone_1.TIME_ZONES).map(([tzAbbr, { offset, tzName }]) => [
14
+ offset,
15
+ { tzAbbr, tzName },
16
+ ]));
10
17
  const _isLabelKey = (offset) => {
11
18
  return offset in timezone_1.TIME_ZONE_LABELS;
12
19
  };
@@ -17,8 +24,14 @@ const timeZonePlugin = (ChronosClass) => {
17
24
  return undefined;
18
25
  };
19
26
  const _getTimeZoneName = (zone) => {
27
+ if (_isGMT(zone))
28
+ return 'Greenwich Mean Time';
20
29
  if ((0, guards_1.isValidUTCOffset)(zone)) {
21
- return _resolveTzName(zone);
30
+ const tzName = _resolveTzName(zone);
31
+ if (!tzName && TZ_NAME_ABBR_MAP.has(zone)) {
32
+ return TZ_NAME_ABBR_MAP.get(zone)?.tzName;
33
+ }
34
+ return tzName;
22
35
  }
23
36
  else if ((0, guards_1.isValidTimeZoneId)(zone)) {
24
37
  const record = timezone_1.TIME_ZONE_IDS[zone];
@@ -27,17 +40,17 @@ const timeZonePlugin = (ChronosClass) => {
27
40
  else {
28
41
  return zone in timezone_1.TIME_ZONES ?
29
42
  timezone_1.TIME_ZONES[zone].tzName
30
- : _resolveTzName(timezone_1.TIME_ZONES[zone].offset);
43
+ : _resolveTzName(timezone_1.TIME_ZONES[zone]?.offset);
31
44
  }
32
45
  };
33
- const TZ_ID_CACHE = new Map(Object.entries(timezone_1.TIME_ZONE_IDS).reduce((acc, [id, { offset }]) => {
46
+ const TZ_ID_MAP = new Map(Object.entries(timezone_1.TIME_ZONE_IDS).reduce((acc, [id, { offset }]) => {
34
47
  const arr = acc.get(offset) ?? [];
35
48
  arr.push(id);
36
49
  acc.set(offset, arr);
37
50
  return acc;
38
51
  }, new Map()));
39
52
  const _getTimeZoneId = (utc) => {
40
- const tzIds = TZ_ID_CACHE.get(utc);
53
+ const tzIds = TZ_ID_MAP.get(utc);
41
54
  if (!tzIds || tzIds?.length === 0)
42
55
  return undefined;
43
56
  if (tzIds?.length === 1)
@@ -59,51 +72,72 @@ const timeZonePlugin = (ChronosClass) => {
59
72
  offset = zone in timezone_1.TIME_ZONES ? timezone_1.TIME_ZONES[zone].offset : timezone_1.TIME_ZONES['UTC'].offset;
60
73
  tzId = _getTimeZoneId(offset) || offset;
61
74
  }
62
- const tzName = _getTimeZoneName(zone) ?? timezone_1.TIME_ZONES['UTC'].offset;
75
+ const $zone = zone || offset;
76
+ const tzName = _getTimeZoneName($zone) ?? offset;
63
77
  const targetOffset = (0, utils_1.extractMinutesFromUTC)(offset);
64
78
  const previousOffset = this.getTimeZoneOffsetMinutes();
65
79
  const relativeOffset = targetOffset - previousOffset;
66
80
  const adjustedTime = new Date($Date(this).getTime() + relativeOffset * 60 * 1000);
67
81
  const instance = new ChronosClass(adjustedTime);
68
- return withOrigin(instance, `timeZone`, offset, tzName, tzId, zone);
82
+ return withOrigin(instance, `timeZone`, offset, tzName, tzId, $zone);
69
83
  };
70
84
  ChronosClass.prototype.getTimeZoneName = function (utc) {
71
85
  const UTC = utc || this.utcOffset;
72
86
  return _getTimeZoneName(utc || this?.$tzTracker || this.utcOffset) ?? UTC;
73
87
  };
74
- const TZ_SHORT_CACHE = new Map();
88
+ const TZ_ABBR_CACHE = new Map();
89
+ const _abbreviate = (name) => {
90
+ return name
91
+ .split(/\s+/)
92
+ .map((w) => w[0])
93
+ .join('')
94
+ .replace(/\W/g, '');
95
+ };
75
96
  ChronosClass.prototype.getTimeZoneNameShort = function (utc) {
76
97
  const tracker = this?.$tzTracker;
98
+ const UTC = utc || this.utcOffset;
99
+ const tzMapKey = utc || tracker || this.utcOffset;
100
+ if (_isGMT(tzMapKey))
101
+ return 'GMT';
77
102
  if (!utc && tracker && tracker in timezone_1.TIME_ZONES)
78
103
  return tracker;
79
- const zone = this.getTimeZoneName(utc);
80
- if (TZ_SHORT_CACHE.has(zone))
81
- return TZ_SHORT_CACHE.get(zone);
82
- const customAbbr = (0, guards_1.isValidUTCOffset)(zone) || (0, guards_1.isValidTimeZoneId)(zone) ?
83
- zone
84
- : zone
85
- .split(/\s+/)
86
- .map((w) => w?.[0])
87
- .join('')
88
- .replace(/\W/g, '');
89
- TZ_SHORT_CACHE.set(zone, customAbbr);
104
+ if ((0, guards_1.isValidUTCOffset)(tzMapKey)) {
105
+ if (TZ_ABBR_CACHE.has(tzMapKey))
106
+ return TZ_ABBR_CACHE.get(tzMapKey);
107
+ if (TZ_NAME_ABBR_MAP.has(tzMapKey)) {
108
+ return TZ_NAME_ABBR_MAP.get(tzMapKey)?.tzAbbr;
109
+ }
110
+ const tzName = _resolveTzName(tzMapKey);
111
+ if (tzName) {
112
+ const tzAbbr = _abbreviate(tzName);
113
+ TZ_ABBR_CACHE.set(tzMapKey, tzAbbr);
114
+ return tzAbbr;
115
+ }
116
+ }
117
+ const zone = _getTimeZoneName(tzMapKey) ?? UTC;
118
+ if (TZ_ABBR_CACHE.has(`name-${zone}`))
119
+ return TZ_ABBR_CACHE.get(zone);
120
+ const customAbbr = (0, guards_1.isValidUTCOffset)(zone) || (0, guards_1.isValidTimeZoneId)(zone) ? zone : _abbreviate(zone);
121
+ TZ_ABBR_CACHE.set(`name-${zone}`, customAbbr);
90
122
  return customAbbr;
91
123
  };
124
+ ChronosClass.prototype.getTimeZoneNameAbbr = function (utc) {
125
+ return this.getTimeZoneNameShort(utc);
126
+ };
92
127
  ChronosClass.prototype.toString = function () {
93
128
  const offset = this.utcOffset;
129
+ const search = /GMT[+-]\d{4}\s+\([^)]+\)/;
94
130
  switch (this.origin) {
95
131
  case 'timeZone': {
96
132
  const gmt = offset.replace('UTC', 'GMT').replace(':', '');
97
133
  const label = this.getTimeZoneName();
98
- return $Date(this)
99
- .toString()
100
- .replace(/GMT[+-]\d{4}\s+\([^)]+\)/, `${gmt} (${label})`);
134
+ return $Date(this).toString().replace(search, `${gmt} (${label})`);
101
135
  }
102
136
  case 'toUTC':
103
137
  case 'utc': {
104
138
  return $Date(this)
105
139
  .toString()
106
- .replace(/GMT[+-]\d{4}\s+\([^)]+\)/, `GMT+0000 (Coordinated Universal Time)`);
140
+ .replace(search, `GMT+0000 (Coordinated Universal Time)`);
107
141
  }
108
142
  default:
109
143
  return $Date(this).toString();
@@ -251,7 +251,7 @@ exports.TIME_ZONES = Object.freeze({
251
251
  offset: 'UTC+07:00',
252
252
  },
253
253
  DDUT: {
254
- tzName: "Dumont d'Urville Time (in French Antarctic Station)",
254
+ tzName: "Dumont d'Urville Time (Antarctic Station in French)",
255
255
  offset: 'UTC+10:00',
256
256
  },
257
257
  DFT: {
@@ -399,7 +399,7 @@ exports.TIME_ZONES = Object.freeze({
399
399
  offset: 'UTC+07:00',
400
400
  },
401
401
  IDLW: {
402
- tzName: 'International Date Line West time zone',
402
+ tzName: 'International Date Line West',
403
403
  offset: 'UTC-12:00',
404
404
  },
405
405
  IDT: {
@@ -892,7 +892,7 @@ exports.TIME_ZONE_LABELS = Object.freeze({
892
892
  'UTC+10:30': 'Lord Howe Standard Time',
893
893
  'UTC+11:00': 'Central Pacific Standard Time',
894
894
  'UTC+12:00': 'New Zealand Standard Time',
895
- 'UTC+12:45': 'Chatham Islands Time',
895
+ 'UTC+12:45': 'Chatham Standard Time',
896
896
  'UTC+13:00': 'Phoenix Island Time',
897
897
  'UTC+14:00': 'Line Islands Time',
898
898
  });
@@ -17,7 +17,7 @@ import type { $UTCOffset, ChronosInput, ChronosInternals, ChronosMethods, Chrono
17
17
  * **It also accepts number values as following:**
18
18
  * - **`year, month, date, hours, minutes, seconds, milliseconds`**: Individual components of a date-time to construct a `Chronos` instance.
19
19
  * - **`year`**: A number representing the year. If the year is between 0 and 99, it will be assumed to be the year 1900 + the provided year.
20
- * - **`month`**: A number between 1 and 12 representing the month (1 for January, 12 for December). It is adjusted internally to a 0-based index (0 for January, 11 for December).
20
+ * - **`month`**: A number between 1 and 12 representing the month (1 for January, 12 for December).
21
21
  * - **`date`**: A number between 1 and 31 representing the day of the month.
22
22
  * - **`hours`**: A number between 0 and 23 representing the hour of the day.
23
23
  * - **`minutes`**: A number between 0 and 59 representing the minutes past the hour.
@@ -198,13 +198,13 @@ export declare class Chronos {
198
198
  get lastDateOfMonth(): NumberRange<28, 31>;
199
199
  /** @instance Returns a debug-friendly string for `console.log` or `util.inspect`. */
200
200
  inspect(): string;
201
- /** @instance Enables JSON.stringify and logging in the console (in Browser environment) to show readable output. */
201
+ /** @instance Enables `JSON.stringify` to show readable output. Calls {@link toLocalISOString} method. */
202
202
  toJSON(): string;
203
- /** @instance Enables arithmetic and comparison operations (e.g., +new Chronos()). */
203
+ /** @instance Enables arithmetic and comparison operations (e.g., `+new Chronos()`). Calls {@link getTimeStamp} method. */
204
204
  valueOf(): number;
205
- /** @instance Clones and returns a new `Chronos` instance with the same date. */
205
+ /** @instance Clones and returns exactly same `Chronos` instance. */
206
206
  clone(): Chronos;
207
- /** @instance Gets the native `Date` instance (read-only). */
207
+ /** @instance Gets the native `Date` instance of the current `Chronos`. */
208
208
  toDate(): Date;
209
209
  /** @instance Returns a string representation of a date. */
210
210
  toString(): string;
@@ -320,11 +320,11 @@ export declare class Chronos {
320
320
  * @returns `true` if the year is a leap year, `false` otherwise.
321
321
  */
322
322
  isLeapYear(year?: number): boolean;
323
- /** @instance Checks if another date is exactly equal to this one */
323
+ /** @instance Checks if another date is exactly equal to this one. */
324
324
  isEqual(other: ChronosInput): boolean;
325
- /** @instance Checks if another date is exactly equal to or before this one */
325
+ /** @instance Checks if another date is exactly equal to or before this one. */
326
326
  isEqualOrBefore(other: ChronosInput): boolean;
327
- /** @instance Checks if another date is exactly equal to or after this one */
327
+ /** @instance Checks if another date is exactly equal to or after this one. */
328
328
  isEqualOrAfter(other: ChronosInput): boolean;
329
329
  /**
330
330
  * @instance Checks if another date is the same as this one in a specific unit.
@@ -489,7 +489,7 @@ export declare class Chronos {
489
489
  * - `Q3`: July to September
490
490
  * - `Q4`: October to December
491
491
  *
492
- * This method strictly uses the **calendar year**. For fiscal quarters, use `toFiscalQuarter()` instead.
492
+ * This method strictly uses the **calendar year**. For fiscal quarters, use {@link toFiscalQuarter} instead.
493
493
  *
494
494
  * @example
495
495
  * new Chronos('2025-02-14').toQuarter(); // 1
@@ -752,12 +752,12 @@ export declare class Chronos {
752
752
  */
753
753
  static getDatesForDay(day: WeekDay, options?: DateRangeOptions): string[];
754
754
  /**
755
- * @static Returns earliest Chronos
755
+ * @static Returns earliest Chronos.
756
756
  * @param dates Date inputs.
757
757
  */
758
758
  static min(...dates: ChronosInput[]): Chronos;
759
759
  /**
760
- * @static Returns latest Chronos
760
+ * @static Returns latest Chronos.
761
761
  * @param dates Date inputs.
762
762
  */
763
763
  static max(...dates: ChronosInput[]): Chronos;
@@ -107,13 +107,13 @@ declare module '../Chronos' {
107
107
  */
108
108
  isBusinessHour(options?: BusinessOptionsWeekends): boolean;
109
109
  /**
110
- * @instance Returns the academic year based on a typical start in July and end in June.
110
+ * @instance Returns the academic year based on a typical start in `July` and end in `June`.
111
111
  * @returns The academic year in format `YYYY-YYYY`.
112
112
  */
113
113
  toAcademicYear(): AcademicYear;
114
114
  /**
115
115
  * @instance Returns the fiscal quarter based on custom fiscal year start (defaults to July).
116
- * @param startMonth - The fiscal year start month (1-12), default is July (7).
116
+ * @param startMonth - The fiscal year start month (1-12), default is July (`7`).
117
117
  * @returns The fiscal quarter (1-4).
118
118
  */
119
119
  toFiscalQuarter(startMonth?: NumberRange<1, 12>): Quarter;
@@ -4,6 +4,9 @@ declare module '../Chronos' {
4
4
  interface Chronos {
5
5
  /**
6
6
  * @instance Returns the full time duration breakdown between current input (start) and another time (to) as {@link TimeDuration} object.
7
+ *
8
+ * @remarks This method calculates the elapsed time difference (excludes the end day), if you need an inclusive calendar-style difference (counting both start and end days), adjust one day manually before calling `duration()`.
9
+ *
7
10
  * @param toTime The time to compare with. Defaults to `now`.
8
11
  * @param absolute If true, returns all values as positive numbers. Defaults to `true`.
9
12
  * @returns An object of time units: `years`, `months`, `days`, `hours`, `minutes`, `seconds`, `milliseconds` ({@link TimeDuration}).
@@ -11,6 +14,9 @@ declare module '../Chronos' {
11
14
  duration(toTime?: ChronosInput, absolute?: boolean): TimeDuration;
12
15
  /**
13
16
  * @instance Returns a human-readable formatted duration string between the current instance (start) and another time (to).
17
+ *
18
+ * @remarks This method calculates the elapsed time difference (excludes the end day), if you need an inclusive calendar-style difference (counting both start and end days), adjust one day manually before calling `durationString()`.
19
+ *
14
20
  * @param options {@link DurationOptions} to format duration string, including the time to compare with.
15
21
  * @returns A formatted duration string, e.g. `"2 hours, 5 minutes"` or `"2h 5m"`.
16
22
  */
@@ -34,23 +34,20 @@ declare module '../Chronos' {
34
34
  /**
35
35
  * @instance Returns the current time zone name as a full descriptive string (e.g. `"Bangladesh Standard Time"`).
36
36
  *
37
- * @param utc Optional UTC offset in `"UTC+06:00"` format. When passed, it bypasses the current time zone offset.
38
- * @returns Time zone name in full descriptive string or UTC offset if it is not a valid time zone.
39
- *
40
37
  * @remarks
41
38
  * - This method uses a predefined mapping of UTC offsets to time zone names.
42
39
  * - If multiple time zones share the same UTC offset, it returns the **first match** from the predefined list.
43
40
  * - If no match is found (which is rare), it falls back to returning the UTC offset (e.g. `"UTC+06:00"`).
44
41
  * - To retrieve the local system's native time zone name (or its identifier if the name is unavailable), use the {@link $getNativeTimeZone} instance method.
45
42
  * - To retrieve the local system's native time zone identifier, use the {@link $getNativeTimeZoneId} instance method.
43
+ *
44
+ * @param utc Optional UTC offset in `"UTC+06:00"` format. When passed, it bypasses the current time zone offset.
45
+ * @returns Time zone name in full descriptive string or UTC offset if it is not a valid time zone.
46
46
  */
47
47
  getTimeZoneName(utc?: UTCOffset): LooseLiteral<TimeZoneName | UTCOffset>;
48
48
  /**
49
49
  * @instance Returns the current time zone abbreviation (e.g. `"BST"` for `Bangladesh Standard Time`).
50
50
  *
51
- * @param utc Optional UTC offset in `"UTC+06:00"` format. When passed, it bypasses the current time zone offset.
52
- * @returns Time zone name in full descriptive string or UTC offset if it is not a valid time zone.
53
- *
54
51
  * @remarks
55
52
  * - This method uses a predefined mapping of UTC offsets to abbreviated time zone codes.
56
53
  * - If multiple time zones share the same UTC offset, it returns the **first abbreviation** from the list.
@@ -58,8 +55,20 @@ declare module '../Chronos' {
58
55
  * - If no match is found (for unlisted or fictional utc offset), it returns the UTC offset (e.g. `"UTC+06:00"`).
59
56
  * - To retrieve the local system's native time zone name (or its identifier if the name is unavailable), use the {@link $getNativeTimeZone} instance method.
60
57
  * - To retrieve the local system's native time zone identifier, use the {@link $getNativeTimeZoneId} instance method.
58
+ *
59
+ * @param utc Optional UTC offset in `"UTC+06:00"` format. When passed, it bypasses the current time zone offset.
60
+ * @returns Time zone name in full descriptive string or UTC offset if it is not a valid time zone.
61
61
  */
62
62
  getTimeZoneNameShort(utc?: UTCOffset): LooseLiteral<TimeZone | UTCOffset>;
63
+ /**
64
+ * @instance Returns the current time zone abbreviation (e.g. `"BST"` for `Bangladesh Standard Time`).
65
+ *
66
+ * @remarks This method is an alias for {@link getTimeZoneNameShort}.
67
+ *
68
+ * @param utc Optional UTC offset in `"UTC+06:00"` format. When passed, it bypasses the current time zone offset.
69
+ * @returns Time zone name in full descriptive string or UTC offset if it is not a valid time zone.
70
+ */
71
+ getTimeZoneNameAbbr(utc?: UTCOffset): LooseLiteral<TimeZone | UTCOffset>;
63
72
  }
64
73
  }
65
74
  /** * Plugin to inject `timeZone` related methods */
@@ -249,7 +249,7 @@ export declare const TIME_ZONES: Readonly<{
249
249
  readonly offset: "UTC+07:00";
250
250
  };
251
251
  readonly DDUT: {
252
- readonly tzName: "Dumont d'Urville Time (in French Antarctic Station)";
252
+ readonly tzName: "Dumont d'Urville Time (Antarctic Station in French)";
253
253
  readonly offset: "UTC+10:00";
254
254
  };
255
255
  readonly DFT: {
@@ -397,7 +397,7 @@ export declare const TIME_ZONES: Readonly<{
397
397
  readonly offset: "UTC+07:00";
398
398
  };
399
399
  readonly IDLW: {
400
- readonly tzName: "International Date Line West time zone";
400
+ readonly tzName: "International Date Line West";
401
401
  readonly offset: "UTC-12:00";
402
402
  };
403
403
  readonly IDT: {
@@ -891,7 +891,7 @@ export declare const TIME_ZONE_LABELS: Readonly<{
891
891
  readonly 'UTC+10:30': "Lord Howe Standard Time";
892
892
  readonly 'UTC+11:00': "Central Pacific Standard Time";
893
893
  readonly 'UTC+12:00': "New Zealand Standard Time";
894
- readonly 'UTC+12:45': "Chatham Islands Time";
894
+ readonly 'UTC+12:45': "Chatham Standard Time";
895
895
  readonly 'UTC+13:00': "Phoenix Island Time";
896
896
  readonly 'UTC+14:00': "Line Islands Time";
897
897
  }>;
@@ -214,6 +214,7 @@ export type ChronosMethods = $InstanceMethods | $StaticMethods | $PluginMethods;
214
214
  export type ChronosInput = number | string | Date | Chronos;
215
215
  /** Represents key of `ChronosStatics` (each static method and property) */
216
216
  export type ChronosStaticKey = keyof ChronosStatics;
217
+ /** Key of {@link TIME_ZONE_LABELS} ({@link UTCOffset}) */
217
218
  export type $TZLabelKey = keyof typeof TIME_ZONE_LABELS;
218
219
  /** Abbreviated time-zone names (from {@link https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations time zone abbreviations on Wikipedia}) */
219
220
  export type TimeZone = keyof typeof TIME_ZONES;
@@ -281,7 +281,7 @@ export class Chronos {
281
281
  return this.getTimeStamp();
282
282
  }
283
283
  clone() {
284
- return new _a(this.#date).#withOrigin(this.#ORIGIN);
284
+ return new _a(this.#date).#withOrigin(this.#ORIGIN, this.#offset, this.timeZoneName, this.timeZoneId, this.$tzTracker);
285
285
  }
286
286
  toDate() {
287
287
  switch (this.#ORIGIN) {
@@ -4,6 +4,13 @@ import { TIME_ZONES, TIME_ZONE_IDS, TIME_ZONE_LABELS } from '../timezone.js';
4
4
  import { extractMinutesFromUTC } from '../utils.js';
5
5
  export const timeZonePlugin = (ChronosClass) => {
6
6
  const { internalDate: $Date, withOrigin } = ChronosClass[INTERNALS];
7
+ const _isGMT = (factor) => {
8
+ return factor === 'UTC+00:00' || factor === 'UTC-00:00';
9
+ };
10
+ const TZ_NAME_ABBR_MAP = new Map(Object.entries(TIME_ZONES).map(([tzAbbr, { offset, tzName }]) => [
11
+ offset,
12
+ { tzAbbr, tzName },
13
+ ]));
7
14
  const _isLabelKey = (offset) => {
8
15
  return offset in TIME_ZONE_LABELS;
9
16
  };
@@ -14,8 +21,14 @@ export const timeZonePlugin = (ChronosClass) => {
14
21
  return undefined;
15
22
  };
16
23
  const _getTimeZoneName = (zone) => {
24
+ if (_isGMT(zone))
25
+ return 'Greenwich Mean Time';
17
26
  if (isValidUTCOffset(zone)) {
18
- return _resolveTzName(zone);
27
+ const tzName = _resolveTzName(zone);
28
+ if (!tzName && TZ_NAME_ABBR_MAP.has(zone)) {
29
+ return TZ_NAME_ABBR_MAP.get(zone)?.tzName;
30
+ }
31
+ return tzName;
19
32
  }
20
33
  else if (isValidTimeZoneId(zone)) {
21
34
  const record = TIME_ZONE_IDS[zone];
@@ -24,17 +37,17 @@ export const timeZonePlugin = (ChronosClass) => {
24
37
  else {
25
38
  return zone in TIME_ZONES ?
26
39
  TIME_ZONES[zone].tzName
27
- : _resolveTzName(TIME_ZONES[zone].offset);
40
+ : _resolveTzName(TIME_ZONES[zone]?.offset);
28
41
  }
29
42
  };
30
- const TZ_ID_CACHE = new Map(Object.entries(TIME_ZONE_IDS).reduce((acc, [id, { offset }]) => {
43
+ const TZ_ID_MAP = new Map(Object.entries(TIME_ZONE_IDS).reduce((acc, [id, { offset }]) => {
31
44
  const arr = acc.get(offset) ?? [];
32
45
  arr.push(id);
33
46
  acc.set(offset, arr);
34
47
  return acc;
35
48
  }, new Map()));
36
49
  const _getTimeZoneId = (utc) => {
37
- const tzIds = TZ_ID_CACHE.get(utc);
50
+ const tzIds = TZ_ID_MAP.get(utc);
38
51
  if (!tzIds || tzIds?.length === 0)
39
52
  return undefined;
40
53
  if (tzIds?.length === 1)
@@ -56,51 +69,72 @@ export const timeZonePlugin = (ChronosClass) => {
56
69
  offset = zone in TIME_ZONES ? TIME_ZONES[zone].offset : TIME_ZONES['UTC'].offset;
57
70
  tzId = _getTimeZoneId(offset) || offset;
58
71
  }
59
- const tzName = _getTimeZoneName(zone) ?? TIME_ZONES['UTC'].offset;
72
+ const $zone = zone || offset;
73
+ const tzName = _getTimeZoneName($zone) ?? offset;
60
74
  const targetOffset = extractMinutesFromUTC(offset);
61
75
  const previousOffset = this.getTimeZoneOffsetMinutes();
62
76
  const relativeOffset = targetOffset - previousOffset;
63
77
  const adjustedTime = new Date($Date(this).getTime() + relativeOffset * 60 * 1000);
64
78
  const instance = new ChronosClass(adjustedTime);
65
- return withOrigin(instance, `timeZone`, offset, tzName, tzId, zone);
79
+ return withOrigin(instance, `timeZone`, offset, tzName, tzId, $zone);
66
80
  };
67
81
  ChronosClass.prototype.getTimeZoneName = function (utc) {
68
82
  const UTC = utc || this.utcOffset;
69
83
  return _getTimeZoneName(utc || this?.$tzTracker || this.utcOffset) ?? UTC;
70
84
  };
71
- const TZ_SHORT_CACHE = new Map();
85
+ const TZ_ABBR_CACHE = new Map();
86
+ const _abbreviate = (name) => {
87
+ return name
88
+ .split(/\s+/)
89
+ .map((w) => w[0])
90
+ .join('')
91
+ .replace(/\W/g, '');
92
+ };
72
93
  ChronosClass.prototype.getTimeZoneNameShort = function (utc) {
73
94
  const tracker = this?.$tzTracker;
95
+ const UTC = utc || this.utcOffset;
96
+ const tzMapKey = utc || tracker || this.utcOffset;
97
+ if (_isGMT(tzMapKey))
98
+ return 'GMT';
74
99
  if (!utc && tracker && tracker in TIME_ZONES)
75
100
  return tracker;
76
- const zone = this.getTimeZoneName(utc);
77
- if (TZ_SHORT_CACHE.has(zone))
78
- return TZ_SHORT_CACHE.get(zone);
79
- const customAbbr = isValidUTCOffset(zone) || isValidTimeZoneId(zone) ?
80
- zone
81
- : zone
82
- .split(/\s+/)
83
- .map((w) => w?.[0])
84
- .join('')
85
- .replace(/\W/g, '');
86
- TZ_SHORT_CACHE.set(zone, customAbbr);
101
+ if (isValidUTCOffset(tzMapKey)) {
102
+ if (TZ_ABBR_CACHE.has(tzMapKey))
103
+ return TZ_ABBR_CACHE.get(tzMapKey);
104
+ if (TZ_NAME_ABBR_MAP.has(tzMapKey)) {
105
+ return TZ_NAME_ABBR_MAP.get(tzMapKey)?.tzAbbr;
106
+ }
107
+ const tzName = _resolveTzName(tzMapKey);
108
+ if (tzName) {
109
+ const tzAbbr = _abbreviate(tzName);
110
+ TZ_ABBR_CACHE.set(tzMapKey, tzAbbr);
111
+ return tzAbbr;
112
+ }
113
+ }
114
+ const zone = _getTimeZoneName(tzMapKey) ?? UTC;
115
+ if (TZ_ABBR_CACHE.has(`name-${zone}`))
116
+ return TZ_ABBR_CACHE.get(zone);
117
+ const customAbbr = isValidUTCOffset(zone) || isValidTimeZoneId(zone) ? zone : _abbreviate(zone);
118
+ TZ_ABBR_CACHE.set(`name-${zone}`, customAbbr);
87
119
  return customAbbr;
88
120
  };
121
+ ChronosClass.prototype.getTimeZoneNameAbbr = function (utc) {
122
+ return this.getTimeZoneNameShort(utc);
123
+ };
89
124
  ChronosClass.prototype.toString = function () {
90
125
  const offset = this.utcOffset;
126
+ const search = /GMT[+-]\d{4}\s+\([^)]+\)/;
91
127
  switch (this.origin) {
92
128
  case 'timeZone': {
93
129
  const gmt = offset.replace('UTC', 'GMT').replace(':', '');
94
130
  const label = this.getTimeZoneName();
95
- return $Date(this)
96
- .toString()
97
- .replace(/GMT[+-]\d{4}\s+\([^)]+\)/, `${gmt} (${label})`);
131
+ return $Date(this).toString().replace(search, `${gmt} (${label})`);
98
132
  }
99
133
  case 'toUTC':
100
134
  case 'utc': {
101
135
  return $Date(this)
102
136
  .toString()
103
- .replace(/GMT[+-]\d{4}\s+\([^)]+\)/, `GMT+0000 (Coordinated Universal Time)`);
137
+ .replace(search, `GMT+0000 (Coordinated Universal Time)`);
104
138
  }
105
139
  default:
106
140
  return $Date(this).toString();
@@ -248,7 +248,7 @@ export const TIME_ZONES = Object.freeze({
248
248
  offset: 'UTC+07:00',
249
249
  },
250
250
  DDUT: {
251
- tzName: "Dumont d'Urville Time (in French Antarctic Station)",
251
+ tzName: "Dumont d'Urville Time (Antarctic Station in French)",
252
252
  offset: 'UTC+10:00',
253
253
  },
254
254
  DFT: {
@@ -396,7 +396,7 @@ export const TIME_ZONES = Object.freeze({
396
396
  offset: 'UTC+07:00',
397
397
  },
398
398
  IDLW: {
399
- tzName: 'International Date Line West time zone',
399
+ tzName: 'International Date Line West',
400
400
  offset: 'UTC-12:00',
401
401
  },
402
402
  IDT: {
@@ -889,7 +889,7 @@ export const TIME_ZONE_LABELS = Object.freeze({
889
889
  'UTC+10:30': 'Lord Howe Standard Time',
890
890
  'UTC+11:00': 'Central Pacific Standard Time',
891
891
  'UTC+12:00': 'New Zealand Standard Time',
892
- 'UTC+12:45': 'Chatham Islands Time',
892
+ 'UTC+12:45': 'Chatham Standard Time',
893
893
  'UTC+13:00': 'Phoenix Island Time',
894
894
  'UTC+14:00': 'Line Islands Time',
895
895
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhb-toolbox",
3
- "version": "4.26.21",
3
+ "version": "4.26.30",
4
4
  "description": "A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -42,8 +42,8 @@
42
42
  "@eslint/js": "^9.39.1",
43
43
  "@types/jest": "^30.0.0",
44
44
  "@types/node": "^24.10.0",
45
- "@typescript-eslint/eslint-plugin": "^8.46.3",
46
- "@typescript-eslint/parser": "^8.46.3",
45
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
46
+ "@typescript-eslint/parser": "^8.46.4",
47
47
  "eslint": "^9.39.1",
48
48
  "eslint-config-prettier": "^10.1.8",
49
49
  "eslint-plugin-prettier": "^5.5.4",
@@ -56,7 +56,7 @@
56
56
  "ts-jest": "^29.4.5",
57
57
  "ts-node": "^10.9.2",
58
58
  "typescript": "^5.9.3",
59
- "typescript-eslint": "^8.46.3"
59
+ "typescript-eslint": "^8.46.4"
60
60
  },
61
61
  "keywords": [
62
62
  "toolbox",