vremel
Version:
JavaScript date utility library for Temporal API
138 lines • 4.9 kB
JavaScript
import { isInstantConstructor, isPlainDateTimeConstructor, } from "../type-utils.js";
import { createRecord } from "./_createRecord.js";
import { formatExactTimeIso } from "./_formatExactTimeIso.js";
import { getDayOfWeekFromYmd } from "./_getDayOfWeekFromYmd.js";
import { getDayOfWeekNumberFromAbbreviation } from "./_getDayOfWeekNumberFromAbbreviation.js";
import { getMonthNumberFromAbbreviation } from "./_getMonthNumberFromAbbreviation.js";
// spec: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3 https://datatracker.ietf.org/doc/html/rfc2822#section-4.3
function removeComment(str) {
const r = /(?<!\\)[()]/g;
let res = "";
let commentNestLevel = 0;
let lastNonCommentStarted = 0;
for (const m of str.matchAll(r)) {
if (m[0] === "(") {
if (commentNestLevel === 0) {
// comment started
res += str.slice(lastNonCommentStarted, m.index);
}
commentNestLevel++;
}
else {
commentNestLevel--;
if (commentNestLevel === 0) {
// comment ended
lastNonCommentStarted = m.index + 1;
}
if (commentNestLevel < 0) {
throw new Error("Unbalanced nested comment");
}
}
}
if (commentNestLevel !== 0) {
throw new Error("Unbalanced nested comment");
}
return res;
}
const dateTimeFormatRegex = /^[ \t\r\n]*(?:([A-Za-z]{3}),)?[ \t\r\n]*(\d\d)[ \t\r\n]+([A-Za-z]{3})[ \t\r\n]+(\d\d|\d\d\d\d)[ \t\r\n]+(\d\d):(\d\d)(?::(\d\d))?[ \t\r\n]+([+-]\d{4}|[A-Za-z]+)[ \t\r\n]*$/;
function fullYear(year) {
const yearNum = parseInt(year, 10);
if (year.length === 4) {
return yearNum;
}
return yearNum >= 50 ? 1900 + yearNum : 2000 + yearNum;
}
function getOffset(timeZone) {
if (["UT", "GMT", "z", "Z"].includes(timeZone)) {
return "+00:00";
}
if (timeZone === "-0000" || /^[A-IK-Za-ik-z]$/.test(timeZone)) {
// according to the spec, military zone except 'Z' should be considered equivalent to "-0000",
// which means the date-time contains no information about the local time zone
throw new Error("No offset info");
}
if (/^[+-]\d{4}$/.test(timeZone)) {
return `${timeZone.slice(0, 3)}:${timeZone.slice(3)}`;
}
const table = createRecord({
EDT: "-04:00",
EST: "-05:00",
CDT: "-05:00",
CST: "-06:00",
MDT: "-06:00",
MST: "-07:00",
PDT: "-07:00",
PST: "-08:00",
});
if (table[timeZone] !== undefined) {
return table[timeZone];
}
throw new Error("Unknown time zone");
}
function parse(date) {
const result = dateTimeFormatRegex.exec(date);
if (result === null) {
throw new Error(`Invalid date and time format: ${date}`);
}
const [, dayOfWeek, day, monthName, year, hour, minute, second = "00", timeZone,] = result;
if (day === undefined ||
monthName === undefined ||
year === undefined ||
hour === undefined ||
minute === undefined ||
timeZone === undefined) {
throw new Error("something wrong");
}
return [
fullYear(year),
getMonthNumberFromAbbreviation(monthName),
parseInt(day),
parseInt(hour, 10),
parseInt(minute, 10),
parseInt(second),
dayOfWeek,
timeZone,
];
}
/**
* Creates Temporal object from datetime string in RFC 2822's format.
*
* @param date datetime string in RFC 2822's format
* @param TemporalClass Temporal class (such as `Temporal.PlainDateTime` or `Temporal.Instant`) which will be returned
* @returns an instance of Temporal class specified in `TemporalClass` argument
*/
export function fromRfc2822(date, TemporalClass) {
const dateWithoutComment = date.includes("(") ? removeComment(date) : date;
const [year, month, day, hour, minute, second, dayOfWeek, timeZone] = parse(dateWithoutComment);
if (dayOfWeek !== undefined &&
getDayOfWeekFromYmd(year, month, day) !==
getDayOfWeekNumberFromAbbreviation(dayOfWeek)) {
throw new Error(`Wrong day of week: ${dayOfWeek}`);
}
if (isPlainDateTimeConstructor(TemporalClass)) {
return TemporalClass.from({
year,
month,
day,
hour,
minute,
second,
calendarId: "iso8601",
});
}
const offsetIso = getOffset(timeZone);
if (isInstantConstructor(TemporalClass)) {
return TemporalClass.from(formatExactTimeIso(year, month, day, hour, minute, second, 0, offsetIso));
}
return TemporalClass.from({
year,
month,
day,
hour,
minute,
second,
calendarId: "iso8601",
timeZone: offsetIso,
});
}
//# sourceMappingURL=fromRfc2822.js.map