UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

151 lines (150 loc) 5.67 kB
import { INTERNALS } from '../constants.js'; import { isValidUTCOffset } from '../guards.js'; import { _gmtToUtcOffset, _resolveNativeTzName } from '../helpers.js'; import { NATIVE_TZ_IDS, TIME_ZONE_LABELS, TIME_ZONES } from '../timezone.js'; import { extractMinutesFromUTC } from '../utils.js'; export const timeZonePlugin = ($Chronos) => { const { internalDate: $Date, withOrigin, offset } = $Chronos[INTERNALS]; const _isGMT = (factor) => { return factor === 'UTC+00:00' || factor === 'UTC-00:00'; }; const _isLabelKey = (offset) => { return offset ? offset in TIME_ZONE_LABELS : false; }; const _isValidTzAbbr = (tz) => { return tz in TIME_ZONES; }; const _resolveTzName = (offset) => { if (_isLabelKey(offset)) { return TIME_ZONE_LABELS[offset]; } return undefined; }; const _getTimeZoneDetails = (tzId, date) => { const TZ_NAME_TYPES = ['long', 'longOffset']; const obj = { tzId }; for (const type of TZ_NAME_TYPES) { const key = type === 'long' ? 'tzName' : 'offset'; try { const partValue = _resolveNativeTzName(tzId, type, date); const value = type === 'longOffset' ? _gmtToUtcOffset(partValue) : partValue; obj[key] = value; } catch { obj[key] = undefined; } } return obj; }; const TZ_ID_MAP = new Map(NATIVE_TZ_IDS.reduce((tzIds, id) => { const { offset } = _getTimeZoneDetails(id); if (offset) { const arr = tzIds.get(offset) ?? []; arr.push(id); tzIds.set(offset, arr); } return tzIds; }, new Map())); const _buildTzNameAbbrMap = () => { const result = []; for (const [tzAbbr, { offset, tzName }] of Object.entries(TIME_ZONES)) { result.unshift([offset, { tzAbbr, tzName }]); } return result; }; const TZ_NAME_ABBR_MAP = new Map(_buildTzNameAbbrMap()); const _getTimeZoneId = (utc) => { const tzIds = TZ_ID_MAP.get(utc); if (!tzIds || tzIds?.length === 0) return undefined; if (tzIds?.length === 1) return tzIds[0]; return tzIds; }; const _getTimeZoneName = (zone, date) => { if (_isGMT(zone)) return 'Greenwich Mean Time'; if (isValidUTCOffset(zone)) { const tzName = _resolveTzName(zone); if (!tzName && TZ_NAME_ABBR_MAP.has(zone)) { return TZ_NAME_ABBR_MAP.get(zone)?.tzName; } return tzName; } else if (_isValidTzAbbr(zone)) { return TIME_ZONES[zone].tzName; } else { const { offset, tzName } = _getTimeZoneDetails(zone, date); return tzName || _resolveTzName(offset); } }; $Chronos.prototype.timeZone = function (zone) { let offset; let tzId; if (isValidUTCOffset(zone)) { offset = zone; tzId = _getTimeZoneId(offset) || offset; } else if (_isValidTzAbbr(zone)) { offset = TIME_ZONES[zone].offset; tzId = _getTimeZoneId(offset) || offset; } else { offset = _getTimeZoneDetails(zone, $Date(this)).offset || TIME_ZONES['UTC'].offset; tzId = zone; } const $zone = zone || offset; const tzName = _getTimeZoneName($zone, $Date(this)) ?? offset; const relativeOffset = extractMinutesFromUTC(offset) - this.getTimeZoneOffsetMinutes(); const adjustedTime = new Date($Date(this).getTime() + relativeOffset * 60 * 1000); const instance = new $Chronos(adjustedTime); return withOrigin(instance, `timeZone`, offset, tzName, tzId, $zone); }; $Chronos.prototype.getTimeZoneName = function (utc) { const UTC = utc || offset(this); return _getTimeZoneName(utc || this?.$tzTracker || offset(this), $Date(this)) ?? UTC; }; const TZ_ABBR_CACHE = new Map(); const _abbreviate = (name) => { return name .split(/\s+/) .map((w) => w[0]) .join('') .replace(/\W/g, ''); }; $Chronos.prototype.getTimeZoneNameShort = function (utc) { const tracker = this?.$tzTracker; const UTC = utc || offset(this); const tzMapKey = utc || tracker || offset(this); if (_isGMT(tzMapKey)) return 'GMT'; if (!utc && tracker && _isValidTzAbbr(tracker)) return tracker; if (isValidUTCOffset(tzMapKey)) { if (TZ_ABBR_CACHE.has(tzMapKey)) { return TZ_ABBR_CACHE.get(tzMapKey); } const tzName = _resolveTzName(tzMapKey); if (!tzName && TZ_NAME_ABBR_MAP.has(tzMapKey)) { return TZ_NAME_ABBR_MAP.get(tzMapKey)?.tzAbbr; } if (tzName) { const tzAbbr = _abbreviate(tzName); TZ_ABBR_CACHE.set(tzMapKey, tzAbbr); return tzAbbr; } } const zone = _getTimeZoneName(tzMapKey, $Date(this)) ?? UTC; const fallbackKey = `name-${zone}`; if (TZ_ABBR_CACHE.has(fallbackKey)) { return TZ_ABBR_CACHE.get(fallbackKey); } const customAbbr = isValidUTCOffset(zone) ? zone : _abbreviate(zone); TZ_ABBR_CACHE.set(fallbackKey, customAbbr); return customAbbr; }; $Chronos.prototype.getTimeZoneNameAbbr = function (utc) { return this.getTimeZoneNameShort(utc); }; };