nhb-toolbox 4.26.0 → 4.26.10

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,7 +6,35 @@ All notable changes to the package will be documented here.
6
6
 
7
7
  ---
8
8
 
9
- ## [4.26.00] - 2025-11-08
9
+ ## [4.26.10] - 2025-11-09
10
+
11
+ ### 🕧 Updates in Chronos
12
+
13
+ - **Fixed** *timezone conversion issues* by updating `TIME_ZONE_IDS`, and `TIME_ZONES` constants:
14
+ - Now both the constants have similar *type definitions* and used with `as const` in the codebase.
15
+
16
+ ```ts
17
+ // Example structure after unification
18
+ const TIME_ZONE_IDS: Record<string, { tzName: string; offset: UTCOffSet }> = {
19
+ 'Asia/Dhaka': { tzName: 'Bangladesh Standard Time', offset: 'UTC+06:00' },
20
+ // ...
21
+ };
22
+
23
+ const TIME_ZONES: Record<string, { tzName: string; offset: UTCOffSet }> = {
24
+ BST: { tzName: 'Bangladesh Standard Time', offset: 'UTC+06:00' },
25
+ // ...
26
+ };
27
+ ```
28
+
29
+ - **Added** new *protected property* `$tzTracker`. **Updated** `withOrigin` and `#withOrigin` methods.
30
+ - **Added** *3 overload signatures* and *internal caching mechanism* for `timeZonePlugin` methods.
31
+ - **Updated** *tsdoc* for some `Chronos` methods. **Created** new type `TimeZoneName` and *more*.
32
+
33
+ ## [4.26.1] - 2025-11-08
34
+
35
+ - **Updated** *tsdoc* for some `Chronos` *methods* and *public properties*.
36
+
37
+ ## [4.26.0] - 2025-11-08
10
38
 
11
39
  ### 🕧 New in Chronos
12
40
 
package/README.md CHANGED
@@ -45,9 +45,9 @@
45
45
  </a>
46
46
  </p>
47
47
 
48
- ## TypeScript Utility Library
48
+ ## JavaScript/TypeScript Utility Library
49
49
 
50
- **NHB Toolbox** provides battle-tested utilities for professional TypeScript development. Carefully crafted to solve common challenges with elegant, production-ready solutions:
50
+ **NHB Toolbox** provides battle-tested utilities for professional JavaScript/TypeScript development. Carefully crafted to solve common challenges with elegant, production-ready solutions:
51
51
 
52
52
  - **Helper Functions & Classes**: Reusable solutions for everyday tasks
53
53
  - **Type Guards & Predicates**: Runtime safety with perfect type inference
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.Chronos = void 0;
4
+ exports.chronos = exports.Chronos = void 0;
5
5
  const non_primitives_1 = require("../guards/non-primitives");
6
6
  const primitives_1 = require("../guards/primitives");
7
7
  const utilities_1 = require("../number/utilities");
@@ -20,8 +20,8 @@ class Chronos {
20
20
  offset(instance) {
21
21
  return instance.#offset;
22
22
  },
23
- withOrigin(instance, method, offset, tzName, tzId) {
24
- return instance.#withOrigin(method, offset, tzName, tzId);
23
+ withOrigin(instance, method, offset, tzName, tzId, tzTracker) {
24
+ return instance.#withOrigin(method, offset, tzName, tzId, tzTracker);
25
25
  },
26
26
  toNewDate(instance, value) {
27
27
  return instance.#toNewDate(value);
@@ -32,6 +32,7 @@ class Chronos {
32
32
  utcOffset;
33
33
  timeZoneName;
34
34
  timeZoneId;
35
+ $tzTracker;
35
36
  constructor(valueOrYear, month, date, hours, minutes, seconds, ms) {
36
37
  if (typeof valueOrYear === 'number' && typeof month === 'number') {
37
38
  this.#date = new Date(valueOrYear, month - 1, date ?? 1, hours ?? 0, minutes ?? 0, seconds ?? 0, ms ?? 0);
@@ -80,9 +81,9 @@ class Chronos {
80
81
  case 'timeZone':
81
82
  case 'toUTC':
82
83
  case 'utc':
83
- return string.replace(this.toISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''), replacement);
84
+ return string.replace(this.#isoTzRemoved(), replacement);
84
85
  default:
85
- return string.replace(this.toLocalISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''), replacement);
86
+ return string.replace(this.#isoTzRemoved(true), replacement);
86
87
  }
87
88
  }
88
89
  [Symbol.search](string) {
@@ -90,9 +91,9 @@ class Chronos {
90
91
  case 'timeZone':
91
92
  case 'toUTC':
92
93
  case 'utc':
93
- return string.indexOf(this.toISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''));
94
+ return string.indexOf(this.#isoTzRemoved());
94
95
  default:
95
- return string.indexOf(this.toLocalISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''));
96
+ return string.indexOf(this.#isoTzRemoved(true));
96
97
  }
97
98
  }
98
99
  [Symbol.split](string) {
@@ -100,9 +101,9 @@ class Chronos {
100
101
  case 'timeZone':
101
102
  case 'toUTC':
102
103
  case 'utc':
103
- return string.split(this.toISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''));
104
+ return string.split(this.#isoTzRemoved());
104
105
  default:
105
- return string.split(this.toLocalISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, ''));
106
+ return string.split(this.#isoTzRemoved(true));
106
107
  }
107
108
  }
108
109
  [Symbol.match](string) {
@@ -144,7 +145,7 @@ class Chronos {
144
145
  }
145
146
  return date;
146
147
  }
147
- #withOrigin(origin, offset, tzName, tzId) {
148
+ #withOrigin(origin, offset, tzName, tzId, tzTracker) {
148
149
  const instance = new _a(this.#date);
149
150
  instance.#ORIGIN = origin;
150
151
  instance.origin = origin;
@@ -152,12 +153,12 @@ class Chronos {
152
153
  instance.#offset = offset;
153
154
  instance.utcOffset = offset;
154
155
  }
155
- if (tzName) {
156
+ if (tzName)
156
157
  instance.timeZoneName = tzName;
157
- }
158
- if (tzId) {
158
+ if (tzId)
159
159
  instance.timeZoneId = tzId;
160
- }
160
+ if (tzTracker)
161
+ instance.$tzTracker = tzTracker;
161
162
  instance.native = instance.toDate();
162
163
  return instance;
163
164
  }
@@ -228,6 +229,11 @@ class Chronos {
228
229
  const pad = (n, p = 2) => String(n).padStart(p, '0');
229
230
  return `${this.year}-${pad(this.month + 1)}-${pad(this.date)}T${pad(this.hour)}:${pad(this.minute)}:${pad(this.second)}.${pad(this.millisecond, 3)}${this.getUTCOffset()}`;
230
231
  }
232
+ #isoTzRemoved(local = false) {
233
+ return local ?
234
+ this.toLocalISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, '')
235
+ : this.toISOString().replace(/\.\d+(Z|[+-]\d{2}:\d{2})?$/, '');
236
+ }
231
237
  #normalizeDuration(result, absolute, isFuture) {
232
238
  const entries = Object.entries(result);
233
239
  const updated = { ...result };
@@ -1050,3 +1056,5 @@ class Chronos {
1050
1056
  }
1051
1057
  exports.Chronos = Chronos;
1052
1058
  _a = Chronos;
1059
+ var chronos_fn_1 = require("./chronos-fn");
1060
+ Object.defineProperty(exports, "chronos", { enumerable: true, get: function () { return chronos_fn_1.chronos; } });
@@ -7,6 +7,7 @@ exports.isLeapYear = isLeapYear;
7
7
  exports.isDateLike = isDateLike;
8
8
  const primitives_1 = require("../guards/primitives");
9
9
  const specials_1 = require("../guards/specials");
10
+ const utilities_1 = require("../number/utilities");
10
11
  const timezone_1 = require("./timezone");
11
12
  function isValidTime(value) {
12
13
  if (!(0, primitives_1.isString)(value))
@@ -25,7 +26,8 @@ function isValidTimeZoneId(value) {
25
26
  return (0, primitives_1.isString)(value) ? value in timezone_1.TIME_ZONE_IDS : false;
26
27
  }
27
28
  function isLeapYear(year) {
28
- return (Number(year) % 4 === 0 && Number(year) % 100 !== 0) || Number(year) % 400 === 0;
29
+ const $year = (0, utilities_1.normalizeNumber)(year);
30
+ return $year ? ($year % 4 === 0 && $year % 100 !== 0) || $year % 400 === 0 : false;
29
31
  }
30
32
  function isDateLike(value) {
31
33
  if (value instanceof Date)
@@ -8,66 +8,94 @@ const utils_1 = require("../utils");
8
8
  const timeZonePlugin = (ChronosClass) => {
9
9
  const internal = ChronosClass[constants_1.INTERNALS];
10
10
  const $Date = internal.internalDate;
11
- const getTimeZoneId = (utc) => {
12
- const obj = { ...timezone_1.TIME_ZONE_IDS };
13
- const tzIds = Object.keys(obj).filter((key) => obj[key] === utc);
14
- if (!tzIds || tzIds.length === 0)
11
+ const _isLabelKey = (offset) => {
12
+ return offset in timezone_1.TIME_ZONE_LABELS;
13
+ };
14
+ const _resolveTzName = (offset) => {
15
+ if (_isLabelKey(offset)) {
16
+ return timezone_1.TIME_ZONE_LABELS[offset];
17
+ }
18
+ return undefined;
19
+ };
20
+ const _getTimeZoneName = (zone) => {
21
+ if ((0, guards_1.isValidUTCOffSet)(zone)) {
22
+ return _resolveTzName(zone);
23
+ }
24
+ else if ((0, guards_1.isValidTimeZoneId)(zone)) {
25
+ const record = timezone_1.TIME_ZONE_IDS[zone];
26
+ return record?.tzName ?? _resolveTzName(record?.offset);
27
+ }
28
+ else {
29
+ return zone in timezone_1.TIME_ZONES ?
30
+ timezone_1.TIME_ZONES[zone].tzName
31
+ : _resolveTzName(timezone_1.TIME_ZONES[zone].offset);
32
+ }
33
+ };
34
+ const TZ_ID_CACHE = new Map(Object.entries(timezone_1.TIME_ZONE_IDS).reduce((acc, [id, { offset }]) => {
35
+ const arr = acc.get(offset) ?? [];
36
+ arr.push(id);
37
+ acc.set(offset, arr);
38
+ return acc;
39
+ }, new Map()));
40
+ const _getTimeZoneId = (utc) => {
41
+ const tzIds = TZ_ID_CACHE.get(utc);
42
+ if (!tzIds || tzIds?.length === 0)
15
43
  return undefined;
16
- if (tzIds.length === 1)
44
+ if (tzIds?.length === 1)
17
45
  return tzIds[0];
18
46
  return tzIds;
19
47
  };
20
48
  ChronosClass.prototype.timeZone = function (zone) {
21
- let targetOffset;
22
- let stringOffset;
49
+ let offset;
23
50
  let tzId;
24
51
  if ((0, guards_1.isValidUTCOffSet)(zone)) {
25
- targetOffset = (0, utils_1.extractMinutesFromUTC)(zone);
26
- stringOffset = zone;
27
- tzId = getTimeZoneId(stringOffset) || stringOffset;
52
+ offset = zone;
53
+ tzId = _getTimeZoneId(offset) || offset;
28
54
  }
29
55
  else if ((0, guards_1.isValidTimeZoneId)(zone)) {
30
- stringOffset = timezone_1.TIME_ZONE_IDS[zone];
31
- targetOffset = (0, utils_1.extractMinutesFromUTC)(stringOffset);
56
+ offset = timezone_1.TIME_ZONE_IDS[zone].offset;
32
57
  tzId = zone;
33
58
  }
34
59
  else {
35
- targetOffset = timezone_1.TIME_ZONES?.[zone] ?? timezone_1.TIME_ZONES['UTC'];
36
- stringOffset = (0, utils_1.formatUTCOffset)(targetOffset);
37
- tzId = getTimeZoneId(stringOffset) || stringOffset;
60
+ offset = zone in timezone_1.TIME_ZONES ? timezone_1.TIME_ZONES[zone].offset : timezone_1.TIME_ZONES['UTC'].offset;
61
+ tzId = _getTimeZoneId(offset) || offset;
38
62
  }
63
+ const tzName = _getTimeZoneName(zone) ?? timezone_1.TIME_ZONES['UTC'].offset;
64
+ const targetOffset = (0, utils_1.extractMinutesFromUTC)(offset);
39
65
  const previousOffset = this.getTimeZoneOffsetMinutes();
40
66
  const relativeOffset = targetOffset - previousOffset;
41
67
  const adjustedTime = new Date($Date(this).getTime() + relativeOffset * 60 * 1000);
42
68
  const instance = new ChronosClass(adjustedTime);
43
- return internal.withOrigin(instance, 'timeZone', stringOffset, this.getTimeZoneName(stringOffset), tzId);
69
+ return internal.withOrigin(instance, `timeZone`, offset, tzName, tzId, zone);
44
70
  };
45
71
  ChronosClass.prototype.getTimeZoneName = function (utc) {
46
- const UTC = utc ?? `UTC${this.getTimeZoneOffset()}`;
47
- return timezone_1.TIME_ZONE_LABELS?.[UTC] ?? UTC;
72
+ const UTC = utc ?? this.utcOffset;
73
+ return _getTimeZoneName(this?.$tzTracker ?? UTC) ?? UTC;
48
74
  };
75
+ const TZ_SHORT_CACHE = new Map();
49
76
  ChronosClass.prototype.getTimeZoneNameShort = function (utc) {
50
- const mins = utc ? (0, utils_1.extractMinutesFromUTC)(utc) : this.getTimeZoneOffsetMinutes();
51
- const UTC = (0, utils_1.formatUTCOffset)(mins);
52
- const timeZone = timezone_1.TIME_ZONE_LABELS?.[UTC];
53
- let result = timeZone
54
- ?.split(/\s+/)
55
- ?.filter(Boolean)
56
- ?.map((part) => part?.[0])
57
- ?.join('')
58
- ?.replace(/\W/g, '');
59
- if (!result) {
60
- const zones = Object.entries(timezone_1.TIME_ZONES);
61
- result = zones.find((zone) => zone?.[1] === mins)?.[0];
62
- }
63
- return result ?? (0, utils_1.formatUTCOffset)(mins);
77
+ const tracker = this?.$tzTracker;
78
+ if (!utc && tracker && tracker in timezone_1.TIME_ZONES)
79
+ return tracker;
80
+ const zone = this.getTimeZoneName(utc);
81
+ if (TZ_SHORT_CACHE.has(zone))
82
+ return TZ_SHORT_CACHE.get(zone);
83
+ const customAbbr = (0, guards_1.isValidUTCOffSet)(zone) || (0, guards_1.isValidTimeZoneId)(zone) ?
84
+ zone
85
+ : zone
86
+ .split(/\s+/)
87
+ .map((w) => w?.[0])
88
+ .join('')
89
+ .replace(/\W/g, '');
90
+ TZ_SHORT_CACHE.set(zone, customAbbr);
91
+ return customAbbr;
64
92
  };
65
93
  ChronosClass.prototype.toString = function () {
66
- const offset = internal.offset(this);
94
+ const offset = this.utcOffset;
67
95
  switch (this.origin) {
68
96
  case 'timeZone': {
69
97
  const gmt = offset.replace('UTC', 'GMT').replace(':', '');
70
- const label = timezone_1.TIME_ZONE_LABELS[offset] ?? offset;
98
+ const label = this.getTimeZoneName();
71
99
  return $Date(this)
72
100
  .toString()
73
101
  .replace(/GMT[+-]\d{4}\s+\([^)]+\)/, `${gmt} (${label})`);