Metzploreur/node_modules/@smithy/smithy-client/dist-es/date-utils.js
clement callaert 244d45ceb8 Version 2
2023-11-01 17:33:25 +01:00

187 lines
8.5 KiB
JavaScript

import { strictParseByte, strictParseDouble, strictParseFloat32, strictParseShort } from "./parse-utils";
const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
export function dateToUtcString(date) {
const year = date.getUTCFullYear();
const month = date.getUTCMonth();
const dayOfWeek = date.getUTCDay();
const dayOfMonthInt = date.getUTCDate();
const hoursInt = date.getUTCHours();
const minutesInt = date.getUTCMinutes();
const secondsInt = date.getUTCSeconds();
const dayOfMonthString = dayOfMonthInt < 10 ? `0${dayOfMonthInt}` : `${dayOfMonthInt}`;
const hoursString = hoursInt < 10 ? `0${hoursInt}` : `${hoursInt}`;
const minutesString = minutesInt < 10 ? `0${minutesInt}` : `${minutesInt}`;
const secondsString = secondsInt < 10 ? `0${secondsInt}` : `${secondsInt}`;
return `${DAYS[dayOfWeek]}, ${dayOfMonthString} ${MONTHS[month]} ${year} ${hoursString}:${minutesString}:${secondsString} GMT`;
}
const RFC3339 = new RegExp(/^(\d{4})-(\d{2})-(\d{2})[tT](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?[zZ]$/);
export const parseRfc3339DateTime = (value) => {
if (value === null || value === undefined) {
return undefined;
}
if (typeof value !== "string") {
throw new TypeError("RFC-3339 date-times must be expressed as strings");
}
const match = RFC3339.exec(value);
if (!match) {
throw new TypeError("Invalid RFC-3339 date-time value");
}
const [_, yearStr, monthStr, dayStr, hours, minutes, seconds, fractionalMilliseconds] = match;
const year = strictParseShort(stripLeadingZeroes(yearStr));
const month = parseDateValue(monthStr, "month", 1, 12);
const day = parseDateValue(dayStr, "day", 1, 31);
return buildDate(year, month, day, { hours, minutes, seconds, fractionalMilliseconds });
};
const RFC3339_WITH_OFFSET = new RegExp(/^(\d{4})-(\d{2})-(\d{2})[tT](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(([-+]\d{2}\:\d{2})|[zZ])$/);
export const parseRfc3339DateTimeWithOffset = (value) => {
if (value === null || value === undefined) {
return undefined;
}
if (typeof value !== "string") {
throw new TypeError("RFC-3339 date-times must be expressed as strings");
}
const match = RFC3339_WITH_OFFSET.exec(value);
if (!match) {
throw new TypeError("Invalid RFC-3339 date-time value");
}
const [_, yearStr, monthStr, dayStr, hours, minutes, seconds, fractionalMilliseconds, offsetStr] = match;
const year = strictParseShort(stripLeadingZeroes(yearStr));
const month = parseDateValue(monthStr, "month", 1, 12);
const day = parseDateValue(dayStr, "day", 1, 31);
const date = buildDate(year, month, day, { hours, minutes, seconds, fractionalMilliseconds });
if (offsetStr.toUpperCase() != "Z") {
date.setTime(date.getTime() - parseOffsetToMilliseconds(offsetStr));
}
return date;
};
const IMF_FIXDATE = new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/);
const RFC_850_DATE = new RegExp(/^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/);
const ASC_TIME = new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( [1-9]|\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? (\d{4})$/);
export const parseRfc7231DateTime = (value) => {
if (value === null || value === undefined) {
return undefined;
}
if (typeof value !== "string") {
throw new TypeError("RFC-7231 date-times must be expressed as strings");
}
let match = IMF_FIXDATE.exec(value);
if (match) {
const [_, dayStr, monthStr, yearStr, hours, minutes, seconds, fractionalMilliseconds] = match;
return buildDate(strictParseShort(stripLeadingZeroes(yearStr)), parseMonthByShortName(monthStr), parseDateValue(dayStr, "day", 1, 31), { hours, minutes, seconds, fractionalMilliseconds });
}
match = RFC_850_DATE.exec(value);
if (match) {
const [_, dayStr, monthStr, yearStr, hours, minutes, seconds, fractionalMilliseconds] = match;
return adjustRfc850Year(buildDate(parseTwoDigitYear(yearStr), parseMonthByShortName(monthStr), parseDateValue(dayStr, "day", 1, 31), {
hours,
minutes,
seconds,
fractionalMilliseconds,
}));
}
match = ASC_TIME.exec(value);
if (match) {
const [_, monthStr, dayStr, hours, minutes, seconds, fractionalMilliseconds, yearStr] = match;
return buildDate(strictParseShort(stripLeadingZeroes(yearStr)), parseMonthByShortName(monthStr), parseDateValue(dayStr.trimLeft(), "day", 1, 31), { hours, minutes, seconds, fractionalMilliseconds });
}
throw new TypeError("Invalid RFC-7231 date-time value");
};
export const parseEpochTimestamp = (value) => {
if (value === null || value === undefined) {
return undefined;
}
let valueAsDouble;
if (typeof value === "number") {
valueAsDouble = value;
}
else if (typeof value === "string") {
valueAsDouble = strictParseDouble(value);
}
else {
throw new TypeError("Epoch timestamps must be expressed as floating point numbers or their string representation");
}
if (Number.isNaN(valueAsDouble) || valueAsDouble === Infinity || valueAsDouble === -Infinity) {
throw new TypeError("Epoch timestamps must be valid, non-Infinite, non-NaN numerics");
}
return new Date(Math.round(valueAsDouble * 1000));
};
const buildDate = (year, month, day, time) => {
const adjustedMonth = month - 1;
validateDayOfMonth(year, adjustedMonth, day);
return new Date(Date.UTC(year, adjustedMonth, day, parseDateValue(time.hours, "hour", 0, 23), parseDateValue(time.minutes, "minute", 0, 59), parseDateValue(time.seconds, "seconds", 0, 60), parseMilliseconds(time.fractionalMilliseconds)));
};
const parseTwoDigitYear = (value) => {
const thisYear = new Date().getUTCFullYear();
const valueInThisCentury = Math.floor(thisYear / 100) * 100 + strictParseShort(stripLeadingZeroes(value));
if (valueInThisCentury < thisYear) {
return valueInThisCentury + 100;
}
return valueInThisCentury;
};
const FIFTY_YEARS_IN_MILLIS = 50 * 365 * 24 * 60 * 60 * 1000;
const adjustRfc850Year = (input) => {
if (input.getTime() - new Date().getTime() > FIFTY_YEARS_IN_MILLIS) {
return new Date(Date.UTC(input.getUTCFullYear() - 100, input.getUTCMonth(), input.getUTCDate(), input.getUTCHours(), input.getUTCMinutes(), input.getUTCSeconds(), input.getUTCMilliseconds()));
}
return input;
};
const parseMonthByShortName = (value) => {
const monthIdx = MONTHS.indexOf(value);
if (monthIdx < 0) {
throw new TypeError(`Invalid month: ${value}`);
}
return monthIdx + 1;
};
const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const validateDayOfMonth = (year, month, day) => {
let maxDays = DAYS_IN_MONTH[month];
if (month === 1 && isLeapYear(year)) {
maxDays = 29;
}
if (day > maxDays) {
throw new TypeError(`Invalid day for ${MONTHS[month]} in ${year}: ${day}`);
}
};
const isLeapYear = (year) => {
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
};
const parseDateValue = (value, type, lower, upper) => {
const dateVal = strictParseByte(stripLeadingZeroes(value));
if (dateVal < lower || dateVal > upper) {
throw new TypeError(`${type} must be between ${lower} and ${upper}, inclusive`);
}
return dateVal;
};
const parseMilliseconds = (value) => {
if (value === null || value === undefined) {
return 0;
}
return strictParseFloat32("0." + value) * 1000;
};
const parseOffsetToMilliseconds = (value) => {
const directionStr = value[0];
let direction = 1;
if (directionStr == "+") {
direction = 1;
}
else if (directionStr == "-") {
direction = -1;
}
else {
throw new TypeError(`Offset direction, ${directionStr}, must be "+" or "-"`);
}
const hour = Number(value.substring(1, 3));
const minute = Number(value.substring(4, 6));
return direction * (hour * 60 + minute) * 60 * 1000;
};
const stripLeadingZeroes = (value) => {
let idx = 0;
while (idx < value.length - 1 && value.charAt(idx) === "0") {
idx++;
}
if (idx === 0) {
return value;
}
return value.slice(idx);
};