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
JavaScript
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);
};
};