luxon
Version:
Immutable date wrapper
118 lines (101 loc) • 2.61 kB
JavaScript
import { parseZoneInfo, isUndefined } from '../impl/util';
import Zone from '../zone';
const dtfCache = {};
function makeDTF(zone) {
if (!dtfCache[zone]) {
dtfCache[zone] = new Intl.DateTimeFormat('en-US', {
hour12: false,
timeZone: zone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
return dtfCache[zone];
}
const typeToPos = {
year: 0,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5
};
function hackyOffset(dtf, date) {
const formatted = dtf.format(date).replace(/\u200E/g, ''),
parsed = /(\d+)\/(\d+)\/(\d+),? (\d+):(\d+):(\d+)/.exec(formatted),
[, fMonth, fDay, fYear, fHour, fMinute, fSecond] = parsed;
return [fYear, fMonth, fDay, fHour, fMinute, fSecond];
}
function partsOffset(dtf, date) {
const formatted = dtf.formatToParts(date),
filled = [];
for (let i = 0; i < formatted.length; i++) {
const { type, value } = formatted[i],
pos = typeToPos[type];
if (!isUndefined(pos)) {
filled[pos] = parseInt(value, 10);
}
}
return filled;
}
export default class IANAZone extends Zone {
static isValidSpecifier(s) {
return s && s.match(/^[a-z_+-]{1,256}\/[a-z_+-]{1,256}(\/[a-z_+-]{1,256})?$/i);
}
static isValidZone(zone) {
try {
new Intl.DateTimeFormat('en-US', { timeZone: zone }).format();
return true;
} catch (e) {
return false;
}
}
// Etc/GMT+8 -> 480
static parseGMTOffset(specifier) {
if (specifier) {
const match = specifier.match(/^Etc\/GMT([+-]\d{1,2})$/i);
if (match) {
return 60 * parseInt(match[1]);
}
}
return null;
}
constructor(name) {
super();
this.zoneName = name;
this.valid = IANAZone.isValidZone(name);
}
get type() {
return 'iana';
}
get name() {
return this.zoneName;
}
get universal() {
return false;
}
offsetName(ts, { format, locale }) {
return parseZoneInfo(ts, format, locale, this.zoneName);
}
offset(ts) {
const date = new Date(ts),
dtf = makeDTF(this.zoneName),
[fYear, fMonth, fDay, fHour, fMinute, fSecond] = dtf.formatToParts
? partsOffset(dtf, date)
: hackyOffset(dtf, date),
asUTC = Date.UTC(fYear, fMonth - 1, fDay, fHour, fMinute, fSecond);
let asTS = date.valueOf();
asTS -= asTS % 1000;
return (asUTC - asTS) / (60 * 1000);
}
equals(otherZone) {
return otherZone.type === 'iana' && otherZone.zoneName === this.zoneName;
}
get isValid() {
return this.valid;
}
}