@woocommerce/date
Version:
WooCommerce date utilities.
783 lines (782 loc) • 31.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.containsLeapYear = exports.isLeapYear = exports.validateDateInputForRange = exports.dateValidationMessages = exports.loadLocaleData = exports.getDateFormatsForInterval = exports.getDateFormatsForIntervalPhp = exports.getDateFormatsForIntervalD3 = exports.defaultTableDateFormat = exports.weekTicksThreshold = exports.dayTicksThreshold = exports.getChartTypeForQuery = exports.getIntervalForQuery = exports.getAllowedIntervalsForQuery = exports.getPreviousDate = exports.getDateDifferenceInDays = exports.getCurrentDates = exports.getDateParamsFromQuery = exports.getCurrentPeriod = exports.getLastPeriod = exports.getStoreTimeZoneMoment = exports.getRangeLabel = exports.toMoment = exports.appendTimestamp = exports.periods = exports.presetValues = exports.defaultDateTimeFormat = exports.isoDateFormat = void 0;
/**
* External dependencies
*/
const moment_1 = __importDefault(require("moment"));
const lodash_1 = require("lodash");
const i18n_1 = require("@wordpress/i18n");
const qs_1 = require("qs");
exports.isoDateFormat = 'YYYY-MM-DD';
exports.defaultDateTimeFormat = 'YYYY-MM-DDTHH:mm:ss';
exports.presetValues = [
{ value: 'today', label: (0, i18n_1.__)('Today', 'woocommerce') },
{ value: 'yesterday', label: (0, i18n_1.__)('Yesterday', 'woocommerce') },
{ value: 'week', label: (0, i18n_1.__)('Week to date', 'woocommerce') },
{ value: 'last_week', label: (0, i18n_1.__)('Last week', 'woocommerce') },
{ value: 'month', label: (0, i18n_1.__)('Month to date', 'woocommerce') },
{ value: 'last_month', label: (0, i18n_1.__)('Last month', 'woocommerce') },
{ value: 'quarter', label: (0, i18n_1.__)('Quarter to date', 'woocommerce') },
{ value: 'last_quarter', label: (0, i18n_1.__)('Last quarter', 'woocommerce') },
{ value: 'year', label: (0, i18n_1.__)('Year to date', 'woocommerce') },
{ value: 'last_year', label: (0, i18n_1.__)('Last year', 'woocommerce') },
{ value: 'custom', label: (0, i18n_1.__)('Custom', 'woocommerce') },
];
exports.periods = [
{
value: 'previous_period',
label: (0, i18n_1.__)('Previous period', 'woocommerce'),
},
{
value: 'previous_year',
label: (0, i18n_1.__)('Previous year', 'woocommerce'),
},
];
const isValidMomentInput = (input) => (0, moment_1.default)(input).isValid();
/**
* Adds timestamp to a string date.
*
* @param {moment.Moment} date - Date as a moment object.
* @param {string} timeOfDay - Either `start`, `now` or `end` of the day.
* @return {string} - String date with timestamp attached.
*/
const appendTimestamp = (date, timeOfDay) => {
if (timeOfDay === 'start') {
return date.startOf('day').format(exports.defaultDateTimeFormat);
}
if (timeOfDay === 'now') {
// Set seconds to 00 to avoid consecutives calls happening before the previous
// one finished.
return date.format(exports.defaultDateTimeFormat);
}
if (timeOfDay === 'end') {
return date.endOf('day').format(exports.defaultDateTimeFormat);
}
throw new Error('appendTimestamp requires second parameter to be either `start`, `now` or `end`');
};
exports.appendTimestamp = appendTimestamp;
/**
* Convert a string to Moment object
*
* @param {string} format - localized date string format
* @param {unknown} str - date string or moment object
* @return {moment.Moment|null} - Moment object representing given string
*/
function toMoment(format, str) {
if (moment_1.default.isMoment(str)) {
return str.isValid() ? str : null;
}
if (typeof str === 'string') {
const date = (0, moment_1.default)(str, [exports.isoDateFormat, format], true);
return date.isValid() ? date : null;
}
throw new Error('toMoment requires a string to be passed as an argument');
}
exports.toMoment = toMoment;
/**
* Given two dates, derive a string representation
*
* @param {moment.Moment} after - start date
* @param {moment.Moment} before - end date
* @return {string} - text value for the supplied date range
*/
function getRangeLabel(after, before) {
const isSameYear = after.year() === before.year();
const isSameMonth = isSameYear && after.month() === before.month();
const isSameDay = isSameYear && isSameMonth && after.isSame(before, 'day');
const fullDateFormat = (0, i18n_1.__)('MMM D, YYYY', 'woocommerce');
if (isSameDay) {
return after.format(fullDateFormat);
}
else if (isSameMonth) {
const afterDate = after.date();
return after
.format(fullDateFormat)
.replace(String(afterDate), `${afterDate} - ${before.date()}`);
}
else if (isSameYear) {
const monthDayFormat = (0, i18n_1.__)('MMM D', 'woocommerce');
return `${after.format(monthDayFormat)} - ${before.format(fullDateFormat)}`;
}
return `${after.format(fullDateFormat)} - ${before.format(fullDateFormat)}`;
}
exports.getRangeLabel = getRangeLabel;
/**
* Gets the current time in the store time zone if set.
*
* @return {string} - Datetime string.
*/
function getStoreTimeZoneMoment() {
if (!window.wcSettings || !window.wcSettings.timeZone) {
return (0, moment_1.default)();
}
if (['+', '-'].includes(window.wcSettings.timeZone.charAt(0))) {
return (0, moment_1.default)().utcOffset(window.wcSettings.timeZone);
}
return (0, moment_1.default)().tz(window.wcSettings.timeZone);
}
exports.getStoreTimeZoneMoment = getStoreTimeZoneMoment;
/**
* Get a DateValue object for a period prior to the current period.
*
* @param {moment.DurationInputArg2} period - the chosen period
* @param {string} compare - `previous_period` or `previous_year`
* @return {DateValue} - DateValue data about the selected period
*/
function getLastPeriod(period, compare) {
const primaryStart = getStoreTimeZoneMoment()
.startOf(period)
.subtract(1, period);
const primaryEnd = primaryStart.clone().endOf(period);
let secondaryStart;
let secondaryEnd;
if (compare === 'previous_period') {
if (period === 'year') {
// Subtract two entire periods for years to take into account leap year
secondaryStart = (0, moment_1.default)().startOf(period).subtract(2, period);
secondaryEnd = secondaryStart.clone().endOf(period);
}
else {
// Otherwise, use days in primary period to figure out how far to go back
// This is necessary for calculating weeks instead of using `endOf`.
const daysDiff = primaryEnd.diff(primaryStart, 'days');
secondaryEnd = primaryStart.clone().subtract(1, 'days');
secondaryStart = secondaryEnd.clone().subtract(daysDiff, 'days');
}
}
else if (period === 'week') {
secondaryStart = primaryStart.clone().subtract(1, 'years');
secondaryEnd = primaryEnd.clone().subtract(1, 'years');
}
else {
secondaryStart = primaryStart.clone().subtract(1, 'years');
secondaryEnd = secondaryStart.clone().endOf(period);
}
// When the period is month, be sure to force end of month to take into account leap year
if (period === 'month') {
secondaryEnd = secondaryEnd.clone().endOf('month');
}
return {
primaryStart,
primaryEnd,
secondaryStart,
secondaryEnd,
};
}
exports.getLastPeriod = getLastPeriod;
/**
* Get a DateValue object for a curent period. The period begins on the first day of the period,
* and ends on the current day.
*
* @param {moment.DurationInputArg2} period - the chosen period
* @param {string} compare - `previous_period` or `previous_year`
* @return {DateValue} - DateValue data about the selected period
*/
function getCurrentPeriod(period, compare) {
const primaryStart = getStoreTimeZoneMoment().startOf(period);
const primaryEnd = getStoreTimeZoneMoment();
const daysSoFar = primaryEnd.diff(primaryStart, 'days');
let secondaryStart;
let secondaryEnd;
if (compare === 'previous_period') {
secondaryStart = primaryStart.clone().subtract(1, period);
secondaryEnd = primaryEnd.clone().subtract(1, period);
}
else {
secondaryStart = primaryStart.clone().subtract(1, 'years');
// Set the end time to 23:59:59.
secondaryEnd = secondaryStart
.clone()
.add(daysSoFar + 1, 'days')
.subtract(1, 'seconds');
}
return {
primaryStart,
primaryEnd,
secondaryStart,
secondaryEnd,
};
}
exports.getCurrentPeriod = getCurrentPeriod;
/**
* Get a DateValue object for a period described by a period, compare value, and start/end
* dates, for custom dates.
*
* @param {string} period - the chosen period
* @param {string} compare - `previous_period` or `previous_year`
* @param {moment.Moment|null} [after] - after date if custom period
* @param {moment.Moment|null} [before] - before date if custom period
* @return {DateValue} - DateValue data about the selected period
*/
const getDateValue = (0, lodash_1.memoize)((period, compare, after, before) => {
switch (period) {
case 'today':
return getCurrentPeriod('day', compare);
case 'yesterday':
return getLastPeriod('day', compare);
case 'week':
return getCurrentPeriod('week', compare);
case 'last_week':
return getLastPeriod('week', compare);
case 'month':
return getCurrentPeriod('month', compare);
case 'last_month':
return getLastPeriod('month', compare);
case 'quarter':
return getCurrentPeriod('quarter', compare);
case 'last_quarter':
return getLastPeriod('quarter', compare);
case 'year':
return getCurrentPeriod('year', compare);
case 'last_year':
return getLastPeriod('year', compare);
case 'custom':
if (!after || !before) {
throw Error('Custom date range requires both after and before dates.');
}
const difference = before.diff(after, 'days');
if (compare === 'previous_period') {
const secondaryEnd = after.clone().subtract(1, 'days');
const secondaryStart = secondaryEnd
.clone()
.subtract(difference, 'days');
return {
primaryStart: after,
primaryEnd: before,
secondaryStart,
secondaryEnd,
};
}
return {
primaryStart: after,
primaryEnd: before,
secondaryStart: after.clone().subtract(1, 'years'),
secondaryEnd: before.clone().subtract(1, 'years'),
};
}
}, (period, compare, after, before) => [
period,
compare,
after && after.format(),
before && before.format(),
].join(':'));
/**
* Memoized internal logic of getDateParamsFromQuery().
*
* @param {string|undefined} period - period value, ie `last_week`
* @param {string|undefined} compare - compare value, ie `previous_year`
* @param {string|undefined} after - date in iso date format, ie `2018-07-03`
* @param {string|undefined} before - date in iso date format, ie `2018-07-03`
* @param {string} defaultDateRange - the store's default date range
* @return {DateParams} - date parameters derived from query parameters with added defaults
*/
const getDateParamsFromQueryMemoized = (0, lodash_1.memoize)((period, compare, after, before, defaultDateRange) => {
if (period && compare) {
return {
period,
compare,
after: after ? (0, moment_1.default)(after) : null,
before: before ? (0, moment_1.default)(before) : null,
};
}
const queryDefaults = (0, qs_1.parse)(defaultDateRange.replace(/&/g, '&'));
if (typeof queryDefaults.period !== 'string') {
/* eslint-disable no-console */
console.warn(`Unexpected default period type ${queryDefaults.period}`);
/* eslint-enable no-console */
queryDefaults.period = '';
}
if (typeof queryDefaults.compare !== 'string') {
/* eslint-disable no-console */
console.warn(`Unexpected default compare type ${queryDefaults.compare}`);
/* eslint-enable no-console */
queryDefaults.compare = '';
}
return {
period: queryDefaults.period,
compare: queryDefaults.compare,
after: queryDefaults.after && isValidMomentInput(queryDefaults.after)
? (0, moment_1.default)(queryDefaults.after)
: null,
before: queryDefaults.before &&
isValidMomentInput(queryDefaults.before)
? (0, moment_1.default)(queryDefaults.before)
: null,
};
}, (period, compare, after, before, defaultDateRange) => [period, compare, after, before, defaultDateRange].join(':'));
/**
* Add default date-related parameters to a query object
*
* @param {Object} query - query object
* @param {string} query.period - period value, ie `last_week`
* @param {string} query.compare - compare value, ie `previous_year`
* @param {string} query.after - date in iso date format, ie `2018-07-03`
* @param {string} query.before - date in iso date format, ie `2018-07-03`
* @param {string} defaultDateRange - the store's default date range
* @return {DateParams} - date parameters derived from query parameters with added defaults
*/
const getDateParamsFromQuery = (query, defaultDateRange = 'period=month&compare=previous_year') => {
const { period, compare, after, before } = query;
return getDateParamsFromQueryMemoized(period, compare, after, before, defaultDateRange);
};
exports.getDateParamsFromQuery = getDateParamsFromQuery;
/**
* Memoized internal logic of getCurrentDates().
*
* @param {string|undefined} period - period value, ie `last_week`
* @param {string|undefined} compare - compare value, ie `previous_year`
* @param {Object} primaryStart - primary query start DateTime, in Moment instance.
* @param {Object} primaryEnd - primary query start DateTime, in Moment instance.
* @param {Object} secondaryStart - secondary query start DateTime, in Moment instance.
* @param {Object} secondaryEnd - secondary query start DateTime, in Moment instance.
* @return {{primary: DataPickerOptions, secondary: DataPickerOptions}} - Primary and secondary DataPickerOptions objects
*/
const getCurrentDatesMemoized = (0, lodash_1.memoize)((period, compare, primaryStart, primaryEnd, secondaryStart, secondaryEnd) => {
const primaryItem = (0, lodash_1.find)(exports.presetValues, (item) => item.value === period);
if (!primaryItem) {
throw new Error(`Cannot find period: ${period}`);
}
const secondaryItem = (0, lodash_1.find)(exports.periods, (item) => item.value === compare);
if (!secondaryItem) {
throw new Error(`Cannot find compare: ${compare}`);
}
return {
primary: {
label: primaryItem.label,
range: getRangeLabel(primaryStart, primaryEnd),
after: primaryStart,
before: primaryEnd,
},
secondary: {
label: secondaryItem.label,
range: getRangeLabel(secondaryStart, secondaryEnd),
after: secondaryStart,
before: secondaryEnd,
},
};
}, (period, compare, primaryStart, primaryEnd, secondaryStart, secondaryEnd) => [
period,
compare,
primaryStart && primaryStart.format(),
primaryEnd && primaryEnd.format(),
secondaryStart && secondaryStart.format(),
secondaryEnd && secondaryEnd.format(),
].join(':'));
/**
* Get Date Value Objects for a primary and secondary date range
*
* @param {Object} query - query object
* @param {string} query.period - period value, ie `last_week`
* @param {string} query.compare - compare value, ie `previous_year`
* @param {string} query.after - date in iso date format, ie `2018-07-03`
* @param {string} query.before - date in iso date format, ie `2018-07-03`
* @param {string} defaultDateRange - the store's default date range
* @return {{primary: DataPickerOptions, secondary: DataPickerOptions}} - Primary and secondary DataPickerOptions objects
*/
const getCurrentDates = (query, defaultDateRange = 'period=month&compare=previous_year') => {
const { period, compare, after, before } = (0, exports.getDateParamsFromQuery)(query, defaultDateRange);
const dateValue = getDateValue(period, compare, after, before);
if (!dateValue) {
throw Error('Invalid date range');
}
const { primaryStart, primaryEnd, secondaryStart, secondaryEnd } = dateValue;
return getCurrentDatesMemoized(period, compare, primaryStart, primaryEnd, secondaryStart, secondaryEnd);
};
exports.getCurrentDates = getCurrentDates;
/**
* Calculates the date difference between two dates. Used in calculating a matching date for previous period.
*
* @param {string} date - Date to compare
* @param {string} date2 - Secondary date to compare
* @return {number} - Difference in days.
*/
const getDateDifferenceInDays = (date, date2) => {
const _date = (0, moment_1.default)(date);
const _date2 = (0, moment_1.default)(date2);
return _date.diff(_date2, 'days');
};
exports.getDateDifferenceInDays = getDateDifferenceInDays;
/**
* Get the previous date for either the previous period of year.
*
* @param {string} date - Base date
* @param {string} date1 - primary start
* @param {string} date2 - secondary start
* @param {string} compare - `previous_period` or `previous_year`
* @param {moment.unitOfTime.Diff} interval - interval
* @return {Object} - Calculated date
*/
const getPreviousDate = (date, date1, date2, compare = 'previous_year', interval) => {
const dateMoment = (0, moment_1.default)(date);
if (compare === 'previous_year') {
return dateMoment.clone().subtract(1, 'years');
}
const _date1 = (0, moment_1.default)(date1);
const _date2 = (0, moment_1.default)(date2);
const difference = _date1.diff(_date2, interval);
return dateMoment.clone().subtract(difference, interval);
};
exports.getPreviousDate = getPreviousDate;
/**
* Returns the allowed selectable intervals for a specific query.
*
* @param {Query} query Current query
* @param {string} defaultDateRange - the store's default date range
* @return {Array} Array containing allowed intervals.
*/
function getAllowedIntervalsForQuery(query, defaultDateRange = 'period=&compare=previous_year') {
const { period } = (0, exports.getDateParamsFromQuery)(query, defaultDateRange);
let allowed = [];
if (period === 'custom') {
const { primary } = (0, exports.getCurrentDates)(query);
const differenceInDays = (0, exports.getDateDifferenceInDays)(primary.before, primary.after);
if (differenceInDays >= 365) {
allowed = ['day', 'week', 'month', 'quarter', 'year'];
}
else if (differenceInDays >= 90) {
allowed = ['day', 'week', 'month', 'quarter'];
}
else if (differenceInDays >= 28) {
allowed = ['day', 'week', 'month'];
}
else if (differenceInDays >= 7) {
allowed = ['day', 'week'];
}
else if (differenceInDays > 1 && differenceInDays < 7) {
allowed = ['day'];
}
else {
allowed = ['hour', 'day'];
}
}
else {
switch (period) {
case 'today':
case 'yesterday':
allowed = ['hour', 'day'];
break;
case 'week':
case 'last_week':
allowed = ['day'];
break;
case 'month':
case 'last_month':
allowed = ['day', 'week'];
break;
case 'quarter':
case 'last_quarter':
allowed = ['day', 'week', 'month'];
break;
case 'year':
case 'last_year':
allowed = ['day', 'week', 'month', 'quarter'];
break;
default:
allowed = ['day'];
break;
}
}
return allowed;
}
exports.getAllowedIntervalsForQuery = getAllowedIntervalsForQuery;
/**
* Returns the current interval to use.
*
* @param {Query} query Current query
* @param {string} defaultDateRange - the store's default date range
* @return {string} Current interval.
*/
function getIntervalForQuery(query, defaultDateRange = 'period=&compare=previous_year') {
const allowed = getAllowedIntervalsForQuery(query, defaultDateRange);
const defaultInterval = allowed[0];
let current = query.interval || defaultInterval;
if (query.interval && !allowed.includes(query.interval)) {
current = defaultInterval;
}
return current;
}
exports.getIntervalForQuery = getIntervalForQuery;
/**
* Returns the current chart type to use.
*
* @param {Query} query Current query
* @param {string} query.chartType
* @return {string} Current chart type.
*/
function getChartTypeForQuery({ chartType }) {
if (chartType !== undefined && ['line', 'bar'].includes(chartType)) {
return chartType;
}
return 'line';
}
exports.getChartTypeForQuery = getChartTypeForQuery;
exports.dayTicksThreshold = 63;
exports.weekTicksThreshold = 9;
exports.defaultTableDateFormat = 'm/d/Y';
/**
* Returns d3 date formats for the current interval.
* See https://github.com/d3/d3-time-format for chart formats.
*
* @param {string} interval Interval to get date formats for.
* @param {number} [ticks] Number of ticks the axis will have.
* @return {string} Current interval.
*/
function getDateFormatsForIntervalD3(interval, ticks = 0) {
let screenReaderFormat = '%B %-d, %Y';
let tooltipLabelFormat = '%B %-d, %Y';
let xFormat = '%Y-%m-%d';
let x2Format = '%b %Y';
let tableFormat = exports.defaultTableDateFormat;
switch (interval) {
case 'hour':
screenReaderFormat = '%_I%p %B %-d, %Y';
tooltipLabelFormat = '%_I%p %b %-d, %Y';
xFormat = '%_I%p';
x2Format = '%b %-d, %Y';
tableFormat = 'h A';
break;
case 'day':
if (ticks < exports.dayTicksThreshold) {
xFormat = '%-d';
}
else {
xFormat = '%b';
x2Format = '%Y';
}
break;
case 'week':
if (ticks < exports.weekTicksThreshold) {
xFormat = '%-d';
x2Format = '%b %Y';
}
else {
xFormat = '%b';
x2Format = '%Y';
}
// eslint-disable-next-line @wordpress/i18n-translator-comments
screenReaderFormat = (0, i18n_1.__)('Week of %B %-d, %Y', 'woocommerce');
// eslint-disable-next-line @wordpress/i18n-translator-comments
tooltipLabelFormat = (0, i18n_1.__)('Week of %B %-d, %Y', 'woocommerce');
break;
case 'quarter':
case 'month':
screenReaderFormat = '%B %Y';
tooltipLabelFormat = '%B %Y';
xFormat = '%b';
x2Format = '%Y';
break;
case 'year':
screenReaderFormat = '%Y';
tooltipLabelFormat = '%Y';
xFormat = '%Y';
break;
}
return {
screenReaderFormat,
tooltipLabelFormat,
xFormat,
x2Format,
tableFormat,
};
}
exports.getDateFormatsForIntervalD3 = getDateFormatsForIntervalD3;
/**
* Returns php date formats for the current interval.
* See see https://www.php.net/manual/en/datetime.format.php.
*
* @param {string} interval Interval to get date formats for.
* @param {number} [ticks] Number of ticks the axis will have.
* @return {string} Current interval.
*/
function getDateFormatsForIntervalPhp(interval, ticks = 0) {
let screenReaderFormat = 'F j, Y';
let tooltipLabelFormat = 'F j, Y';
let xFormat = 'Y-m-d';
let x2Format = 'M Y';
let tableFormat = exports.defaultTableDateFormat;
switch (interval) {
case 'hour':
screenReaderFormat = 'gA F j, Y';
tooltipLabelFormat = 'gA M j, Y';
xFormat = 'gA';
x2Format = 'M j, Y';
tableFormat = 'h A';
break;
case 'day':
if (ticks < exports.dayTicksThreshold) {
xFormat = 'j';
}
else {
xFormat = 'M';
x2Format = 'Y';
}
break;
case 'week':
if (ticks < exports.weekTicksThreshold) {
xFormat = 'j';
x2Format = 'M Y';
}
else {
xFormat = 'M';
x2Format = 'Y';
}
// Since some alphabet letters have php associated formats, we need to escape them first.
const escapedWeekOfStr = (0, i18n_1.__)('Week of', 'woocommerce').replace(/(\w)/g, '\\$1');
screenReaderFormat = `${escapedWeekOfStr} F j, Y`;
tooltipLabelFormat = `${escapedWeekOfStr} F j, Y`;
break;
case 'quarter':
case 'month':
screenReaderFormat = 'F Y';
tooltipLabelFormat = 'F Y';
xFormat = 'M';
x2Format = 'Y';
break;
case 'year':
screenReaderFormat = 'Y';
tooltipLabelFormat = 'Y';
xFormat = 'Y';
break;
}
return {
screenReaderFormat,
tooltipLabelFormat,
xFormat,
x2Format,
tableFormat,
};
}
exports.getDateFormatsForIntervalPhp = getDateFormatsForIntervalPhp;
/**
* Returns date formats for the current interval.
*
* @param {string} interval Interval to get date formats for.
* @param {number} [ticks] Number of ticks the axis will have.
* @param {Object} [option] Options
* @param {string} [option.type] Date format type, d3 or php, defaults to d3.
* @return {string} Current interval.
*/
function getDateFormatsForInterval(interval, ticks = 0, option = { type: 'd3' }) {
switch (option.type) {
case 'php':
return getDateFormatsForIntervalPhp(interval, ticks);
case 'd3':
default:
return getDateFormatsForIntervalD3(interval, ticks);
}
}
exports.getDateFormatsForInterval = getDateFormatsForInterval;
/**
* Gutenberg's moment instance is loaded with i18n values, which are
* PHP date formats, ie 'LLL: "F j, Y g:i a"'. Override those with translations
* of moment style js formats.
*
* @param {Object} config Locale config object, from store settings.
* @param {string} config.userLocale
* @param {Array} config.weekdaysShort
*/
function loadLocaleData({ userLocale, weekdaysShort, }) {
// Don't update if the wp locale hasn't been set yet, like in unit tests, for instance.
if (moment_1.default.locale() !== 'en') {
moment_1.default.updateLocale(userLocale, {
longDateFormat: {
L: (0, i18n_1.__)('MM/DD/YYYY', 'woocommerce'),
LL: (0, i18n_1.__)('MMMM D, YYYY', 'woocommerce'),
LLL: (0, i18n_1.__)('D MMMM YYYY LT', 'woocommerce'),
LLLL: (0, i18n_1.__)('dddd, D MMMM YYYY LT', 'woocommerce'),
LT: (0, i18n_1.__)('HH:mm', 'woocommerce'),
// Set LTS to default LTS locale format because we don't have a specific format for it.
// Reference https://github.com/moment/moment/blob/develop/dist/moment.js
LTS: 'h:mm:ss A',
},
weekdaysMin: weekdaysShort,
});
}
}
exports.loadLocaleData = loadLocaleData;
exports.dateValidationMessages = {
invalid: (0, i18n_1.__)('Invalid date', 'woocommerce'),
future: (0, i18n_1.__)('Select a date in the past', 'woocommerce'),
startAfterEnd: (0, i18n_1.__)('Start date must be before end date', 'woocommerce'),
endBeforeStart: (0, i18n_1.__)('Start date must be before end date', 'woocommerce'),
};
/**
* @typedef {Object} validatedDate
* @property {Object|null} date - A resulting Moment date object or null, if invalid
* @property {string} error - An optional error message if date is invalid
*/
/**
* Validate text input supplied for a date range.
*
* @param {string} type - Designate beginning or end of range, eg `before` or `after`.
* @param {string} value - User input value
* @param {Object|null} [before] - If already designated, the before date parameter
* @param {Object|null} [after] - If already designated, the after date parameter
* @param {string} format - The expected date format in a user's locale
* @return {Object} validatedDate - validated date object
*/
function validateDateInputForRange(type, value, before, after, format) {
const date = toMoment(format, value);
if (!date) {
return {
date: null,
error: exports.dateValidationMessages.invalid,
};
}
if ((0, moment_1.default)().isBefore(date, 'day')) {
return {
date: null,
error: exports.dateValidationMessages.future,
};
}
if (type === 'after' && before && date.isAfter(before, 'day')) {
return {
date: null,
error: exports.dateValidationMessages.startAfterEnd,
};
}
if (type === 'before' && after && date.isBefore(after, 'day')) {
return {
date: null,
error: exports.dateValidationMessages.endBeforeStart,
};
}
return { date };
}
exports.validateDateInputForRange = validateDateInputForRange;
/**
* Checks whether the year is a leap year.
*
* @param year Year to check
* @return {boolean} True if leap year
*/
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
exports.isLeapYear = isLeapYear;
/**
* Checks whether a date range contains leap year.
*
* @param {string} startDate Start date
* @param {string} endDate End date
* @return {boolean} True if date range contains a leap year
*/
function containsLeapYear(startDate, endDate) {
// Parse the input dates to get the years
const startYear = new Date(startDate).getFullYear();
const endYear = new Date(endDate).getFullYear();
if (!isNaN(startYear) && !isNaN(endYear)) {
// Check each year in the range
for (let year = startYear; year <= endYear; year++) {
if (isLeapYear(year)) {
return true;
}
}
}
return false; // No leap years in the range or invalid date
}
exports.containsLeapYear = containsLeapYear;