luxon
Version:
Immutable date wrapper
146 lines (120 loc) • 4.05 kB
JavaScript
import {
numberBetween,
isLeapYear,
timeObject,
daysInYear,
daysInMonth,
weeksInWeekYear,
isNumber
} from './util';
const nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
function dayOfWeek(year, month, day) {
const js = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
return js === 0 ? 7 : js;
}
function computeOrdinal(year, month, day) {
return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1];
}
function uncomputeOrdinal(year, ordinal) {
const table = isLeapYear(year) ? leapLadder : nonLeapLadder,
month0 = table.findIndex(i => i < ordinal),
day = ordinal - table[month0];
return { month: month0 + 1, day };
}
/**
* @private
*/
export function gregorianToWeek(gregObj) {
const { year, month, day } = gregObj,
ordinal = computeOrdinal(year, month, day),
weekday = dayOfWeek(year, month, day);
let weekNumber = Math.floor((ordinal - weekday + 10) / 7),
weekYear;
if (weekNumber < 1) {
weekYear = year - 1;
weekNumber = weeksInWeekYear(weekYear);
} else if (weekNumber > weeksInWeekYear(year)) {
weekYear = year + 1;
weekNumber = 1;
} else {
weekYear = year;
}
return Object.assign({ weekYear, weekNumber, weekday }, timeObject(gregObj));
}
export function weekToGregorian(weekData) {
const { weekYear, weekNumber, weekday } = weekData,
weekdayOfJan4 = dayOfWeek(weekYear, 1, 4),
yearInDays = daysInYear(weekYear);
let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3,
year;
if (ordinal < 1) {
year = weekYear - 1;
ordinal += daysInYear(year);
} else if (ordinal > yearInDays) {
year = weekYear + 1;
ordinal -= daysInYear(year);
} else {
year = weekYear;
}
const { month, day } = uncomputeOrdinal(year, ordinal);
return Object.assign({ year, month, day }, timeObject(weekData));
}
export function gregorianToOrdinal(gregData) {
const { year, month, day } = gregData,
ordinal = computeOrdinal(year, month, day);
return Object.assign({ year, ordinal }, timeObject(gregData));
}
export function ordinalToGregorian(ordinalData) {
const { year, ordinal } = ordinalData,
{ month, day } = uncomputeOrdinal(year, ordinal);
return Object.assign({ year, month, day }, timeObject(ordinalData));
}
export function hasInvalidWeekData(obj) {
const validYear = isNumber(obj.weekYear),
validWeek = numberBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear)),
validWeekday = numberBetween(obj.weekday, 1, 7);
if (!validYear) {
return 'weekYear out of range';
} else if (!validWeek) {
return 'week out of range';
} else if (!validWeekday) {
return 'weekday out of range';
} else return false;
}
export function hasInvalidOrdinalData(obj) {
const validYear = isNumber(obj.year),
validOrdinal = numberBetween(obj.ordinal, 1, daysInYear(obj.year));
if (!validYear) {
return 'year out of range';
} else if (!validOrdinal) {
return 'ordinal out of range';
} else return false;
}
export function hasInvalidGregorianData(obj) {
const validYear = isNumber(obj.year),
validMonth = numberBetween(obj.month, 1, 12),
validDay = numberBetween(obj.day, 1, daysInMonth(obj.year, obj.month));
if (!validYear) {
return 'year out of range';
} else if (!validMonth) {
return 'month out of range';
} else if (!validDay) {
return 'day out of range';
} else return false;
}
export function hasInvalidTimeData(obj) {
const validHour = numberBetween(obj.hour, 0, 23),
validMinute = numberBetween(obj.minute, 0, 59),
validSecond = numberBetween(obj.second, 0, 59),
validMillisecond = numberBetween(obj.millisecond, 0, 999);
if (!validHour) {
return 'hour out of range';
} else if (!validMinute) {
return 'minute out of range';
} else if (!validSecond) {
return 'second out of range';
} else if (!validMillisecond) {
return 'millisecond out of range';
} else return false;
}