/** * Compares a date with today in the client's time zone. The comparison is day-wise, * so that all dates since today's midnight till the next midnight are considered "today". * Yesterday and tomorrow are separate from the past and future. * * @param dateRepresentation {string|number|Date} * @returns {number} * +2 - date is in the future (after tomorrow) * +1 - tomorrow * 0 - today * -1 - yesterday * -2 - date is in the past (before yesterday) */ export function relativeToLocaleToday(dateRepresentation) { return relativeToDate(dateRepresentation, Date.now()); } /** * Compares a date with the reference date in the client's time zone. The comparison is day-wise, * so that all dates since the date's midnight till the next midnight are considered "same day". * * @param dateRepresentation {string|number|Date} * @param to {string|number|Date} * @returns {number} * +2 - the date is after the reference date (2 days or more) * +1 - the date is next to the reference date * 0 - the same day * -1 - the date is one day before the reference date * -2 - the date is earlier the reference date (2 days or more) */ export function relativeToDate(dateRepresentation, to) { const date = new Date(dateRepresentation); const referenceDate = new Date(to); if (referenceDate.toDateString() === date.toDateString()) { return 0; } const inFuture = date > referenceDate; const midnight = new Date( referenceDate.getFullYear(), referenceDate.getMonth(), referenceDate.getDate() + (inFuture ? 2 : -1) ); if (inFuture) { return midnight > date ? 1 : 2; } return midnight <= date ? -1 : -2; } /** * Returns a range of dates between 'from' and 'to' exclusively: 'from', [N+1, N+2, ...], 'to'. * If 'from' is greater than 'to', the range is reversed: 'from', [N-1, N-2, ...], 'to'. * If 'include' argument is true and 'from' is the same day as 'to' (time may be different), * the range would contain only one element, which is max('from', 'to'). * * @param from {string|number|Date} * @param to {string|number|Date} * @param [include=false] {boolean} - * include 'from' and/or 'to' to the resulting array. * @param [transform=null] {null|true|Function} - * if null, time of the dates between 'from' and 'to' would be the same as 'from'. * if true, time of the dates would be set to locale midnight, * if Function, each Date in the range would be passed to the function, * returned value is used in the resulting array. * @returns {[]} */ export function datesRange(from, to, {include = false, transform = null} = {}) { const range = []; from = new Date(from); to = new Date(to); const reversed = from > to; if (reversed) { [from, to] = [to, from]; } const insert = reversed ? range.unshift.bind(range) : range.push.bind(range); const formatter = typeof transform === 'function' ? transform : function (d) { if (transform === true) { d.setHours(0); d.setMinutes(0); d.setSeconds(0); d.setMilliseconds(0); } return d; }; const fromToOffset = relativeToDate(from, to); if (fromToOffset < -1) { const toMidnight = new Date(to.getFullYear(), to.getMonth(), to.getDate()); const nextDay = new Date(from); while (nextDay.setDate(nextDay.getDate() + 1) < toMidnight) { insert(formatter(new Date(nextDay))); } } if (include) { insert(formatter(to)); if (fromToOffset) { const resultFrom = formatter(from); if (reversed) { range.push(resultFrom); } else { range.unshift(resultFrom); } } } return range; }