wix-style-react
Version:
319 lines (268 loc) • 8.28 kB
JavaScript
/**
* This file is replacing the `@date-fns/upgrade/v2/ legacyParse` method with some local code that doesn’t require the whole date-fns library
* See this open Github issue in the date-fns/upgrade/v2 library: https://github.com/date-fns/date-fns-upgrade/issues/14
*
* A PR with the fix for the date-fns/upgrade/v2 library: https://github.com/date-fns/date-fns-upgrade/pull/20
* Once this PR will be merged we could use the original legacyParse method
*/
import isDate from 'date-fns/isDate';
const MILLISECONDS_IN_HOUR = 3600000;
const MILLISECONDS_IN_MINUTE = 60000;
const DEFAULT_ADDITIONAL_DIGITS = 2;
const parseTokenDateTimeDelimeter = /[T ]/;
const parseTokenPlainTime = /:/;
// year tokens
const parseTokenYY = /^(\d{2})$/;
const parseTokensYYY = [
/^([+-]\d{2})$/, // 0 additional digits
/^([+-]\d{3})$/, // 1 additional digit
/^([+-]\d{4})$/, // 2 additional digits
];
const parseTokenYYYY = /^(\d{4})/;
const parseTokensYYYYY = [
/^([+-]\d{4})/, // 0 additional digits
/^([+-]\d{5})/, // 1 additional digit
/^([+-]\d{6})/, // 2 additional digits
];
// date tokens
const parseTokenMM = /^-(\d{2})$/;
const parseTokenDDD = /^-?(\d{3})$/;
const parseTokenMMDD = /^-?(\d{2})-?(\d{2})$/;
const parseTokenWww = /^-?W(\d{2})$/;
const parseTokenWwwD = /^-?W(\d{2})-?(\d{1})$/;
// time tokens
const parseTokenHH = /^(\d{2}([.,]\d*)?)$/;
const parseTokenHHMM = /^(\d{2}):?(\d{2}([.,]\d*)?)$/;
const parseTokenHHMMSS = /^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/;
// timezone tokens
const parseTokenTimezone = /([Z+-].*)$/;
const parseTokenTimezoneZ = /^(Z)$/;
const parseTokenTimezoneHH = /^([+-])(\d{2})$/;
const parseTokenTimezoneHHMM = /^([+-])(\d{2}):?(\d{2})$/;
export default function legacyParse(argument, options = {}) {
if (isDate(argument)) {
// Prevent the date to lose the milliseconds when passed to new Date() in IE10
return new Date(argument.getTime());
} else if (typeof argument !== 'string') {
return new Date(argument);
}
const additionalDigits =
options.additionalDigits == null
? DEFAULT_ADDITIONAL_DIGITS
: Number(options.additionalDigits);
const dateStrings = splitDateString(argument);
const parseYearResult = parseYear(dateStrings.date || '', additionalDigits);
const year = parseYearResult.year;
const restDateString = parseYearResult.restDateString;
const date = parseDate(restDateString || '', year);
if (date) {
const timestamp = date.getTime();
let time = 0;
let offset;
if (dateStrings.time) {
time = parseTime(dateStrings.time) || 0;
}
if (dateStrings.timezone) {
offset = parseTimezone(dateStrings.timezone) * MILLISECONDS_IN_MINUTE;
} else {
const fullTime = timestamp + time;
const fullTimeDate = new Date(fullTime);
offset = getTimezoneOffsetInMilliseconds(fullTimeDate);
// Adjust time when it's coming from DST
const fullTimeDateNextDay = new Date(fullTime);
fullTimeDateNextDay.setDate(fullTimeDate.getDate() + 1);
const offsetDiff =
getTimezoneOffsetInMilliseconds(fullTimeDateNextDay) -
getTimezoneOffsetInMilliseconds(fullTimeDate);
if (offsetDiff > 0) {
offset += offsetDiff;
}
}
return new Date(timestamp + time + offset);
} else {
return new Date(argument);
}
}
function splitDateString(dateString) {
const array = dateString.split(parseTokenDateTimeDelimeter);
let timeString, date, time, timezone;
if (parseTokenPlainTime.test(array[0])) {
date = undefined;
timeString = array[0];
} else {
date = array[0];
timeString = array[1];
}
if (timeString) {
const token = parseTokenTimezone.exec(timeString);
if (token) {
time = timeString.replace(token[1], '');
timezone = token[1];
} else {
time = timeString;
}
}
return {
date,
time,
timezone,
};
}
function parseYear(dateString, additionalDigits) {
const parseTokenYYY = parseTokensYYY[additionalDigits];
const parseTokenYYYYY = parseTokensYYYYY[additionalDigits];
let token;
// YYYY or ±YYYYY
token = parseTokenYYYY.exec(dateString) || parseTokenYYYYY.exec(dateString);
if (token) {
const yearString = token[1];
return {
year: parseInt(yearString, 10),
restDateString: dateString.slice(yearString.length),
};
}
// YY or ±YYY
token = parseTokenYY.exec(dateString) || parseTokenYYY.exec(dateString);
if (token) {
const centuryString = token[1];
return {
year: parseInt(centuryString, 10) * 100,
restDateString: dateString.slice(centuryString.length),
};
}
// Invalid ISO-formatted year
return {
year: null,
};
}
function parseDate(dateString, year) {
// Invalid ISO-formatted year
if (year === null) {
return null;
}
let token;
let date;
let month;
let week;
// YYYY
if (dateString.length === 0) {
date = new Date(0);
date.setUTCFullYear(year);
return date;
}
// YYYY-MM
token = parseTokenMM.exec(dateString);
if (token) {
date = new Date(0);
month = parseInt(token[1], 10) - 1;
date.setUTCFullYear(year, month);
return date;
}
// YYYY-DDD or YYYYDDD
token = parseTokenDDD.exec(dateString);
if (token) {
date = new Date(0);
const dayOfYear = parseInt(token[1], 10);
date.setUTCFullYear(year, 0, dayOfYear);
return date;
}
// YYYY-MM-DD or YYYYMMDD
token = parseTokenMMDD.exec(dateString);
if (token) {
date = new Date(0);
month = parseInt(token[1], 10) - 1;
const day = parseInt(token[2], 10);
date.setUTCFullYear(year, month, day);
return date;
}
// YYYY-Www or YYYYWww
token = parseTokenWww.exec(dateString);
if (token) {
week = parseInt(token[1], 10) - 1;
return dayOfISOYear(year, week);
}
// YYYY-Www-D or YYYYWwwD
token = parseTokenWwwD.exec(dateString);
if (token) {
week = parseInt(token[1], 10) - 1;
const dayOfWeek = parseInt(token[2], 10) - 1;
return dayOfISOYear(year, week, dayOfWeek);
}
// Invalid ISO-formatted date
return null;
}
function parseTime(timeString) {
let token;
let hours;
let minutes;
// hh
token = parseTokenHH.exec(timeString);
if (token) {
hours = parseFloat(token[1].replace(',', '.'));
return (hours % 24) * MILLISECONDS_IN_HOUR;
}
// hh:mm or hhmm
token = parseTokenHHMM.exec(timeString);
if (token) {
hours = parseInt(token[1], 10);
minutes = parseFloat(token[2].replace(',', '.'));
return (
(hours % 24) * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE
);
}
// hh:mm:ss or hhmmss
token = parseTokenHHMMSS.exec(timeString);
if (token) {
hours = parseInt(token[1], 10);
minutes = parseInt(token[2], 10);
const seconds = parseFloat(token[3].replace(',', '.'));
return (
(hours % 24) * MILLISECONDS_IN_HOUR +
minutes * MILLISECONDS_IN_MINUTE +
seconds * 1000
);
}
// Invalid ISO-formatted time
return null;
}
function parseTimezone(timezoneString) {
let token;
let absoluteOffset;
// Z
token = parseTokenTimezoneZ.exec(timezoneString);
if (token) {
return 0;
}
// ±hh
token = parseTokenTimezoneHH.exec(timezoneString);
if (token) {
absoluteOffset = parseInt(token[2], 10) * 60;
return token[1] === '+' ? -absoluteOffset : absoluteOffset;
}
// ±hh:mm or ±hhmm
token = parseTokenTimezoneHHMM.exec(timezoneString);
if (token) {
absoluteOffset = parseInt(token[2], 10) * 60 + parseInt(token[3], 10);
return token[1] === '+' ? -absoluteOffset : absoluteOffset;
}
return 0;
}
function dayOfISOYear(isoYear, week = 0, day = 0) {
const date = new Date(0);
date.setUTCFullYear(isoYear, 0, 4);
const fourthOfJanuaryDay = date.getUTCDay() || 7;
const diff = week * 7 + day + 1 - fourthOfJanuaryDay;
date.setUTCDate(date.getUTCDate() + diff);
return date;
}
function getTimezoneOffsetInMilliseconds(dirtyDate) {
const date = new Date(dirtyDate.getTime());
const baseTimezoneOffset = date.getTimezoneOffset();
date.setSeconds(0, 0);
const millisecondsPartOfTimezoneOffset =
date.getTime() % MILLISECONDS_IN_MINUTE;
return (
baseTimezoneOffset * MILLISECONDS_IN_MINUTE +
millisecondsPartOfTimezoneOffset
);
}