mirror of
https://github.com/arthur-pbty/portfolio2023.git
synced 2026-06-05 08:11:40 +02:00
restart portfolio
This commit is contained in:
+153
-16
@@ -38,6 +38,8 @@ import {
|
||||
hasInvalidWeekData,
|
||||
hasInvalidOrdinalData,
|
||||
hasInvalidTimeData,
|
||||
usesLocalWeekValues,
|
||||
isoWeekdayToLocal,
|
||||
} from "./impl/conversions.js";
|
||||
import * as Formats from "./impl/formats.js";
|
||||
import {
|
||||
@@ -56,6 +58,9 @@ function unsupportedZone(zone) {
|
||||
}
|
||||
|
||||
// we cache week data on the DT object and this intermediates the cache
|
||||
/**
|
||||
* @param {DateTime} dt
|
||||
*/
|
||||
function possiblyCachedWeekData(dt) {
|
||||
if (dt.weekData === null) {
|
||||
dt.weekData = gregorianToWeek(dt.c);
|
||||
@@ -63,6 +68,20 @@ function possiblyCachedWeekData(dt) {
|
||||
return dt.weekData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DateTime} dt
|
||||
*/
|
||||
function possiblyCachedLocalWeekData(dt) {
|
||||
if (dt.localWeekData === null) {
|
||||
dt.localWeekData = gregorianToWeek(
|
||||
dt.c,
|
||||
dt.loc.getMinDaysInFirstWeek(),
|
||||
dt.loc.getStartOfWeek()
|
||||
);
|
||||
}
|
||||
return dt.localWeekData;
|
||||
}
|
||||
|
||||
// clone really means, "make a new object with these modifications". all "setters" really use this
|
||||
// to create a new object while only changing some of the properties
|
||||
function clone(inst, alts) {
|
||||
@@ -334,6 +353,22 @@ function normalizeUnit(unit) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeUnitWithLocalWeeks(unit) {
|
||||
switch (unit.toLowerCase()) {
|
||||
case "localweekday":
|
||||
case "localweekdays":
|
||||
return "localWeekday";
|
||||
case "localweeknumber":
|
||||
case "localweeknumbers":
|
||||
return "localWeekNumber";
|
||||
case "localweekyear":
|
||||
case "localweekyears":
|
||||
return "localWeekYear";
|
||||
default:
|
||||
return normalizeUnit(unit);
|
||||
}
|
||||
}
|
||||
|
||||
// this is a dumbed down version of fromObject() that runs about 60% faster
|
||||
// but doesn't do any validation, makes a bunch of assumptions about what units
|
||||
// are present, and so on.
|
||||
@@ -476,6 +511,10 @@ export default class DateTime {
|
||||
* @access private
|
||||
*/
|
||||
this.weekData = null;
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
this.localWeekData = null;
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
@@ -646,13 +685,16 @@ export default class DateTime {
|
||||
* @param {number} obj.weekYear - an ISO week year
|
||||
* @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year
|
||||
* @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday
|
||||
* @param {number} obj.localWeekYear - a week year, according to the locale
|
||||
* @param {number} obj.localWeekNumber - a week number, between 1 and 52 or 53, depending on the year, according to the locale
|
||||
* @param {number} obj.localWeekday - a weekday, 1-7, where 1 is the first and 7 is the last day of the week, according to the locale
|
||||
* @param {number} obj.hour - hour of the day, 0-23
|
||||
* @param {number} obj.minute - minute of the hour, 0-59
|
||||
* @param {number} obj.second - second of the minute, 0-59
|
||||
* @param {number} obj.millisecond - millisecond of the second, 0-999
|
||||
* @param {Object} opts - options for creating this DateTime
|
||||
* @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone()
|
||||
* @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
|
||||
* @param {string} [opts.locale='system\'s locale'] - a locale to set on the resulting DateTime instance
|
||||
* @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
|
||||
* @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
|
||||
* @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'
|
||||
@@ -662,6 +704,7 @@ export default class DateTime {
|
||||
* @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' })
|
||||
* @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' })
|
||||
* @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'
|
||||
* @example DateTime.fromObject({ localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 }, { locale: "en-US" }).toISODate() //=> '2021-12-26'
|
||||
* @return {DateTime}
|
||||
*/
|
||||
static fromObject(obj, opts = {}) {
|
||||
@@ -671,17 +714,19 @@ export default class DateTime {
|
||||
return DateTime.invalid(unsupportedZone(zoneToUse));
|
||||
}
|
||||
|
||||
const loc = Locale.fromObject(opts);
|
||||
const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks);
|
||||
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, loc);
|
||||
|
||||
const tsNow = Settings.now(),
|
||||
offsetProvis = !isUndefined(opts.specificOffset)
|
||||
? opts.specificOffset
|
||||
: zoneToUse.offset(tsNow),
|
||||
normalized = normalizeObject(obj, normalizeUnit),
|
||||
containsOrdinal = !isUndefined(normalized.ordinal),
|
||||
containsGregorYear = !isUndefined(normalized.year),
|
||||
containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
|
||||
containsGregor = containsGregorYear || containsGregorMD,
|
||||
definiteWeekDef = normalized.weekYear || normalized.weekNumber,
|
||||
loc = Locale.fromObject(opts);
|
||||
definiteWeekDef = normalized.weekYear || normalized.weekNumber;
|
||||
|
||||
// cases:
|
||||
// just a weekday -> this week's instance of that weekday, no worries
|
||||
@@ -708,7 +753,7 @@ export default class DateTime {
|
||||
if (useWeekData) {
|
||||
units = orderedWeekUnits;
|
||||
defaultValues = defaultWeekUnitValues;
|
||||
objNow = gregorianToWeek(objNow);
|
||||
objNow = gregorianToWeek(objNow, minDaysInFirstWeek, startOfWeek);
|
||||
} else if (containsOrdinal) {
|
||||
units = orderedOrdinalUnits;
|
||||
defaultValues = defaultOrdinalUnitValues;
|
||||
@@ -733,7 +778,7 @@ export default class DateTime {
|
||||
|
||||
// make sure the values we have are in range
|
||||
const higherOrderInvalid = useWeekData
|
||||
? hasInvalidWeekData(normalized)
|
||||
? hasInvalidWeekData(normalized, minDaysInFirstWeek, startOfWeek)
|
||||
: containsOrdinal
|
||||
? hasInvalidOrdinalData(normalized)
|
||||
: hasInvalidGregorianData(normalized),
|
||||
@@ -745,7 +790,7 @@ export default class DateTime {
|
||||
|
||||
// compute the actual time
|
||||
const gregorian = useWeekData
|
||||
? weekToGregorian(normalized)
|
||||
? weekToGregorian(normalized, minDaysInFirstWeek, startOfWeek)
|
||||
: containsOrdinal
|
||||
? ordinalToGregorian(normalized)
|
||||
: normalized,
|
||||
@@ -1129,6 +1174,43 @@ export default class DateTime {
|
||||
return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this date is on a weekend according to the locale, false otherwise
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isWeekend() {
|
||||
return this.isValid && this.loc.getWeekendDays().includes(this.weekday);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the day of the week according to the locale.
|
||||
* 1 is the first day of the week and 7 is the last day of the week.
|
||||
* If the locale assigns Sunday as the first day of the week, then a date which is a Sunday will return 1,
|
||||
* @returns {number}
|
||||
*/
|
||||
get localWeekday() {
|
||||
return this.isValid ? possiblyCachedLocalWeekData(this).weekday : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the week number of the week year according to the locale. Different locales assign week numbers differently,
|
||||
* because the week can start on different days of the week (see localWeekday) and because a different number of days
|
||||
* is required for a week to count as the first week of a year.
|
||||
* @returns {number}
|
||||
*/
|
||||
get localWeekNumber() {
|
||||
return this.isValid ? possiblyCachedLocalWeekData(this).weekNumber : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the week year according to the locale. Different locales assign week numbers (and therefor week years)
|
||||
* differently, see localWeekNumber.
|
||||
* @returns {number}
|
||||
*/
|
||||
get localWeekYear() {
|
||||
return this.isValid ? possiblyCachedLocalWeekData(this).weekYear : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ordinal (meaning the day of the year)
|
||||
* @example DateTime.local(2017, 5, 25).ordinal //=> 145
|
||||
@@ -1321,6 +1403,22 @@ export default class DateTime {
|
||||
return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of weeks in this DateTime's local week year
|
||||
* @example DateTime.local(2020, 6, {locale: 'en-US'}).weeksInLocalWeekYear //=> 52
|
||||
* @example DateTime.local(2020, 6, {locale: 'de-DE'}).weeksInLocalWeekYear //=> 53
|
||||
* @type {number}
|
||||
*/
|
||||
get weeksInLocalWeekYear() {
|
||||
return this.isValid
|
||||
? weeksInWeekYear(
|
||||
this.localWeekYear,
|
||||
this.loc.getMinDaysInFirstWeek(),
|
||||
this.loc.getStartOfWeek()
|
||||
)
|
||||
: NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved Intl options for this DateTime.
|
||||
* This is useful in understanding the behavior of formatting methods
|
||||
@@ -1409,6 +1507,9 @@ export default class DateTime {
|
||||
/**
|
||||
* "Set" the values of specified units. Returns a newly-constructed DateTime.
|
||||
* You can only set units with this method; for "setting" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}.
|
||||
*
|
||||
* This method also supports setting locale-based week units, i.e. `localWeekday`, `localWeekNumber` and `localWeekYear`.
|
||||
* They cannot be mixed with ISO-week units like `weekday`.
|
||||
* @param {Object} values - a mapping of units to numbers
|
||||
* @example dt.set({ year: 2017 })
|
||||
* @example dt.set({ hour: 8, minute: 30 })
|
||||
@@ -1419,8 +1520,10 @@ export default class DateTime {
|
||||
set(values) {
|
||||
if (!this.isValid) return this;
|
||||
|
||||
const normalized = normalizeObject(values, normalizeUnit),
|
||||
settingWeekStuff =
|
||||
const normalized = normalizeObject(values, normalizeUnitWithLocalWeeks);
|
||||
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, this.loc);
|
||||
|
||||
const settingWeekStuff =
|
||||
!isUndefined(normalized.weekYear) ||
|
||||
!isUndefined(normalized.weekNumber) ||
|
||||
!isUndefined(normalized.weekday),
|
||||
@@ -1442,7 +1545,11 @@ export default class DateTime {
|
||||
|
||||
let mixed;
|
||||
if (settingWeekStuff) {
|
||||
mixed = weekToGregorian({ ...gregorianToWeek(this.c), ...normalized });
|
||||
mixed = weekToGregorian(
|
||||
{ ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized },
|
||||
minDaysInFirstWeek,
|
||||
startOfWeek
|
||||
);
|
||||
} else if (!isUndefined(normalized.ordinal)) {
|
||||
mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });
|
||||
} else {
|
||||
@@ -1493,6 +1600,8 @@ export default class DateTime {
|
||||
/**
|
||||
* "Set" this DateTime to the beginning of a unit of time.
|
||||
* @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
|
||||
* @param {Object} opts - options
|
||||
* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week
|
||||
* @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'
|
||||
* @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'
|
||||
* @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays
|
||||
@@ -1500,8 +1609,9 @@ export default class DateTime {
|
||||
* @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'
|
||||
* @return {DateTime}
|
||||
*/
|
||||
startOf(unit) {
|
||||
startOf(unit, { useLocaleWeeks = false } = {}) {
|
||||
if (!this.isValid) return this;
|
||||
|
||||
const o = {},
|
||||
normalizedUnit = Duration.normalizeUnit(unit);
|
||||
switch (normalizedUnit) {
|
||||
@@ -1531,7 +1641,16 @@ export default class DateTime {
|
||||
}
|
||||
|
||||
if (normalizedUnit === "weeks") {
|
||||
o.weekday = 1;
|
||||
if (useLocaleWeeks) {
|
||||
const startOfWeek = this.loc.getStartOfWeek();
|
||||
const { weekday } = this;
|
||||
if (weekday < startOfWeek) {
|
||||
o.weekNumber = this.weekNumber - 1;
|
||||
}
|
||||
o.weekday = startOfWeek;
|
||||
} else {
|
||||
o.weekday = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalizedUnit === "quarters") {
|
||||
@@ -1545,6 +1664,8 @@ export default class DateTime {
|
||||
/**
|
||||
* "Set" this DateTime to the end (meaning the last millisecond) of a unit of time
|
||||
* @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
|
||||
* @param {Object} opts - options
|
||||
* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week
|
||||
* @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'
|
||||
* @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'
|
||||
* @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays
|
||||
@@ -1552,10 +1673,10 @@ export default class DateTime {
|
||||
* @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'
|
||||
* @return {DateTime}
|
||||
*/
|
||||
endOf(unit) {
|
||||
endOf(unit, opts) {
|
||||
return this.isValid
|
||||
? this.plus({ [unit]: 1 })
|
||||
.startOf(unit)
|
||||
.startOf(unit, opts)
|
||||
.minus(1)
|
||||
: this;
|
||||
}
|
||||
@@ -1814,6 +1935,18 @@ export default class DateTime {
|
||||
return this.isValid ? this.toISO() : INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this DateTime appropriate for the REPL.
|
||||
* @return {string}
|
||||
*/
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
if (this.isValid) {
|
||||
return `DateTime { ts: ${this.toISO()}, zone: ${this.zone.name}, locale: ${this.locale} }`;
|
||||
} else {
|
||||
return `DateTime { Invalid, reason: ${this.invalidReason} }`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis}
|
||||
* @return {number}
|
||||
@@ -1950,15 +2083,19 @@ export default class DateTime {
|
||||
* Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed.
|
||||
* @param {DateTime} otherDateTime - the other DateTime
|
||||
* @param {string} unit - the unit of time to check sameness on
|
||||
* @param {Object} opts - options
|
||||
* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; only the locale of this DateTime is used
|
||||
* @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasSame(otherDateTime, unit) {
|
||||
hasSame(otherDateTime, unit, opts) {
|
||||
if (!this.isValid) return false;
|
||||
|
||||
const inputMs = otherDateTime.valueOf();
|
||||
const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });
|
||||
return adjustedToZone.startOf(unit) <= inputMs && inputMs <= adjustedToZone.endOf(unit);
|
||||
return (
|
||||
adjustedToZone.startOf(unit, opts) <= inputMs && inputMs <= adjustedToZone.endOf(unit, opts)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+17
-4
@@ -484,9 +484,10 @@ export default class Duration {
|
||||
|
||||
/**
|
||||
* Returns a string representation of a Duration with all units included.
|
||||
* To modify its behavior use the `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
|
||||
* @param opts - On option object to override the formatting. Accepts the same keys as the options parameter of the native `Int.NumberFormat` constructor, as well as `listStyle`.
|
||||
* To modify its behavior, use `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options
|
||||
* @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`.
|
||||
* @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor.
|
||||
* @example
|
||||
* ```js
|
||||
* var dur = Duration.fromObject({ days: 1, hours: 5, minutes: 6 })
|
||||
@@ -607,6 +608,18 @@ export default class Duration {
|
||||
return this.toISO();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Duration appropriate for the REPL.
|
||||
* @return {string}
|
||||
*/
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
if (this.isValid) {
|
||||
return `Duration { values: ${JSON.stringify(this.values)} }`;
|
||||
} else {
|
||||
return `Duration { Invalid, reason: ${this.invalidReason} }`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an milliseconds value of this Duration.
|
||||
* @return {number}
|
||||
@@ -727,7 +740,7 @@ export default class Duration {
|
||||
* Assuming the overall value of the Duration is positive, this means:
|
||||
* - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example)
|
||||
* - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise
|
||||
* the overall value would be negative, see second example)
|
||||
* the overall value would be negative, see third example)
|
||||
* - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example)
|
||||
*
|
||||
* If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`.
|
||||
|
||||
+57
-12
@@ -6,8 +6,10 @@ import {
|
||||
daysInMonth,
|
||||
weeksInWeekYear,
|
||||
isInteger,
|
||||
isUndefined,
|
||||
} from "./util.js";
|
||||
import Invalid from "./invalid.js";
|
||||
import { ConflictingSpecificationError } from "../errors.js";
|
||||
|
||||
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];
|
||||
@@ -19,7 +21,7 @@ function unitOutOfRange(unit, value) {
|
||||
);
|
||||
}
|
||||
|
||||
function dayOfWeek(year, month, day) {
|
||||
export function dayOfWeek(year, month, day) {
|
||||
const d = new Date(Date.UTC(year, month - 1, day));
|
||||
|
||||
if (year < 100 && year >= 0) {
|
||||
@@ -42,22 +44,26 @@ function uncomputeOrdinal(year, ordinal) {
|
||||
return { month: month0 + 1, day };
|
||||
}
|
||||
|
||||
export function isoWeekdayToLocal(isoWeekday, startOfWeek) {
|
||||
return ((isoWeekday - startOfWeek + 7) % 7) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
export function gregorianToWeek(gregObj) {
|
||||
export function gregorianToWeek(gregObj, minDaysInFirstWeek = 4, startOfWeek = 1) {
|
||||
const { year, month, day } = gregObj,
|
||||
ordinal = computeOrdinal(year, month, day),
|
||||
weekday = dayOfWeek(year, month, day);
|
||||
weekday = isoWeekdayToLocal(dayOfWeek(year, month, day), startOfWeek);
|
||||
|
||||
let weekNumber = Math.floor((ordinal - weekday + 10) / 7),
|
||||
let weekNumber = Math.floor((ordinal - weekday + 14 - minDaysInFirstWeek) / 7),
|
||||
weekYear;
|
||||
|
||||
if (weekNumber < 1) {
|
||||
weekYear = year - 1;
|
||||
weekNumber = weeksInWeekYear(weekYear);
|
||||
} else if (weekNumber > weeksInWeekYear(year)) {
|
||||
weekNumber = weeksInWeekYear(weekYear, minDaysInFirstWeek, startOfWeek);
|
||||
} else if (weekNumber > weeksInWeekYear(year, minDaysInFirstWeek, startOfWeek)) {
|
||||
weekYear = year + 1;
|
||||
weekNumber = 1;
|
||||
} else {
|
||||
@@ -67,12 +73,12 @@ export function gregorianToWeek(gregObj) {
|
||||
return { weekYear, weekNumber, weekday, ...timeObject(gregObj) };
|
||||
}
|
||||
|
||||
export function weekToGregorian(weekData) {
|
||||
export function weekToGregorian(weekData, minDaysInFirstWeek = 4, startOfWeek = 1) {
|
||||
const { weekYear, weekNumber, weekday } = weekData,
|
||||
weekdayOfJan4 = dayOfWeek(weekYear, 1, 4),
|
||||
weekdayOfJan4 = isoWeekdayToLocal(dayOfWeek(weekYear, 1, minDaysInFirstWeek), startOfWeek),
|
||||
yearInDays = daysInYear(weekYear);
|
||||
|
||||
let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3,
|
||||
let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 7 + minDaysInFirstWeek,
|
||||
year;
|
||||
|
||||
if (ordinal < 1) {
|
||||
@@ -101,15 +107,54 @@ export function ordinalToGregorian(ordinalData) {
|
||||
return { year, month, day, ...timeObject(ordinalData) };
|
||||
}
|
||||
|
||||
export function hasInvalidWeekData(obj) {
|
||||
/**
|
||||
* Check if local week units like localWeekday are used in obj.
|
||||
* If so, validates that they are not mixed with ISO week units and then copies them to the normal week unit properties.
|
||||
* Modifies obj in-place!
|
||||
* @param obj the object values
|
||||
*/
|
||||
export function usesLocalWeekValues(obj, loc) {
|
||||
const hasLocaleWeekData =
|
||||
!isUndefined(obj.localWeekday) ||
|
||||
!isUndefined(obj.localWeekNumber) ||
|
||||
!isUndefined(obj.localWeekYear);
|
||||
if (hasLocaleWeekData) {
|
||||
const hasIsoWeekData =
|
||||
!isUndefined(obj.weekday) || !isUndefined(obj.weekNumber) || !isUndefined(obj.weekYear);
|
||||
|
||||
if (hasIsoWeekData) {
|
||||
throw new ConflictingSpecificationError(
|
||||
"Cannot mix locale-based week fields with ISO-based week fields"
|
||||
);
|
||||
}
|
||||
if (!isUndefined(obj.localWeekday)) obj.weekday = obj.localWeekday;
|
||||
if (!isUndefined(obj.localWeekNumber)) obj.weekNumber = obj.localWeekNumber;
|
||||
if (!isUndefined(obj.localWeekYear)) obj.weekYear = obj.localWeekYear;
|
||||
delete obj.localWeekday;
|
||||
delete obj.localWeekNumber;
|
||||
delete obj.localWeekYear;
|
||||
return {
|
||||
minDaysInFirstWeek: loc.getMinDaysInFirstWeek(),
|
||||
startOfWeek: loc.getStartOfWeek(),
|
||||
};
|
||||
} else {
|
||||
return { minDaysInFirstWeek: 4, startOfWeek: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
export function hasInvalidWeekData(obj, minDaysInFirstWeek = 4, startOfWeek = 1) {
|
||||
const validYear = isInteger(obj.weekYear),
|
||||
validWeek = integerBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear)),
|
||||
validWeek = integerBetween(
|
||||
obj.weekNumber,
|
||||
1,
|
||||
weeksInWeekYear(obj.weekYear, minDaysInFirstWeek, startOfWeek)
|
||||
),
|
||||
validWeekday = integerBetween(obj.weekday, 1, 7);
|
||||
|
||||
if (!validYear) {
|
||||
return unitOutOfRange("weekYear", obj.weekYear);
|
||||
} else if (!validWeek) {
|
||||
return unitOutOfRange("week", obj.week);
|
||||
return unitOutOfRange("week", obj.weekNumber);
|
||||
} else if (!validWeekday) {
|
||||
return unitOutOfRange("weekday", obj.weekday);
|
||||
} else return false;
|
||||
|
||||
+8
@@ -337,6 +337,14 @@ export default class Formatter {
|
||||
return this.num(dt.weekNumber);
|
||||
case "WW":
|
||||
return this.num(dt.weekNumber, 2);
|
||||
case "n":
|
||||
return this.num(dt.localWeekNumber);
|
||||
case "nn":
|
||||
return this.num(dt.localWeekNumber, 2);
|
||||
case "ii":
|
||||
return this.num(dt.localWeekYear.toString().slice(-2), 2);
|
||||
case "iiii":
|
||||
return this.num(dt.localWeekYear, 4);
|
||||
case "o":
|
||||
return this.num(dt.ordinal);
|
||||
case "ooo":
|
||||
|
||||
+56
-7
@@ -1,4 +1,4 @@
|
||||
import { padStart, roundTo, hasRelative, formatOffset } from "./util.js";
|
||||
import { hasLocaleWeekInfo, hasRelative, padStart, roundTo, validateWeekSettings } from "./util.js";
|
||||
import * as English from "./english.js";
|
||||
import Settings from "../settings.js";
|
||||
import DateTime from "../datetime.js";
|
||||
@@ -61,6 +61,18 @@ function systemLocale() {
|
||||
}
|
||||
}
|
||||
|
||||
let weekInfoCache = {};
|
||||
function getCachedWeekInfo(locString) {
|
||||
let data = weekInfoCache[locString];
|
||||
if (!data) {
|
||||
const locale = new Intl.Locale(locString);
|
||||
// browsers currently implement this as a property, but spec says it should be a getter function
|
||||
data = "getWeekInfo" in locale ? locale.getWeekInfo() : locale.weekInfo;
|
||||
weekInfoCache[locString] = data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function parseLocaleString(localeStr) {
|
||||
// I really want to avoid writing a BCP 47 parser
|
||||
// see, e.g. https://github.com/wooorm/bcp-47
|
||||
@@ -305,22 +317,35 @@ class PolyRelFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
const fallbackWeekSettings = {
|
||||
firstDay: 1,
|
||||
minimalDays: 4,
|
||||
weekend: [6, 7],
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
export default class Locale {
|
||||
static fromOpts(opts) {
|
||||
return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN);
|
||||
return Locale.create(
|
||||
opts.locale,
|
||||
opts.numberingSystem,
|
||||
opts.outputCalendar,
|
||||
opts.weekSettings,
|
||||
opts.defaultToEN
|
||||
);
|
||||
}
|
||||
|
||||
static create(locale, numberingSystem, outputCalendar, defaultToEN = false) {
|
||||
static create(locale, numberingSystem, outputCalendar, weekSettings, defaultToEN = false) {
|
||||
const specifiedLocale = locale || Settings.defaultLocale;
|
||||
// the system locale is useful for human readable strings but annoying for parsing/formatting known formats
|
||||
const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale());
|
||||
const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;
|
||||
const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;
|
||||
return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale);
|
||||
const weekSettingsR = validateWeekSettings(weekSettings) || Settings.defaultWeekSettings;
|
||||
return new Locale(localeR, numberingSystemR, outputCalendarR, weekSettingsR, specifiedLocale);
|
||||
}
|
||||
|
||||
static resetCache() {
|
||||
@@ -330,16 +355,17 @@ export default class Locale {
|
||||
intlRelCache = {};
|
||||
}
|
||||
|
||||
static fromObject({ locale, numberingSystem, outputCalendar } = {}) {
|
||||
return Locale.create(locale, numberingSystem, outputCalendar);
|
||||
static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {
|
||||
return Locale.create(locale, numberingSystem, outputCalendar, weekSettings);
|
||||
}
|
||||
|
||||
constructor(locale, numbering, outputCalendar, specifiedLocale) {
|
||||
constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) {
|
||||
const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);
|
||||
|
||||
this.locale = parsedLocale;
|
||||
this.numberingSystem = numbering || parsedNumberingSystem || null;
|
||||
this.outputCalendar = outputCalendar || parsedOutputCalendar || null;
|
||||
this.weekSettings = weekSettings;
|
||||
this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);
|
||||
|
||||
this.weekdaysCache = { format: {}, standalone: {} };
|
||||
@@ -375,6 +401,7 @@ export default class Locale {
|
||||
alts.locale || this.specifiedLocale,
|
||||
alts.numberingSystem || this.numberingSystem,
|
||||
alts.outputCalendar || this.outputCalendar,
|
||||
validateWeekSettings(alts.weekSettings) || this.weekSettings,
|
||||
alts.defaultToEN || false
|
||||
);
|
||||
}
|
||||
@@ -483,6 +510,28 @@ export default class Locale {
|
||||
);
|
||||
}
|
||||
|
||||
getWeekSettings() {
|
||||
if (this.weekSettings) {
|
||||
return this.weekSettings;
|
||||
} else if (!hasLocaleWeekInfo()) {
|
||||
return fallbackWeekSettings;
|
||||
} else {
|
||||
return getCachedWeekInfo(this.locale);
|
||||
}
|
||||
}
|
||||
|
||||
getStartOfWeek() {
|
||||
return this.getWeekSettings().firstDay;
|
||||
}
|
||||
|
||||
getMinDaysInFirstWeek() {
|
||||
return this.getWeekSettings().minimalDays;
|
||||
}
|
||||
|
||||
getWeekendDays() {
|
||||
return this.getWeekSettings().weekend;
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
return (
|
||||
this.locale === other.locale &&
|
||||
|
||||
+45
-10
@@ -6,6 +6,7 @@
|
||||
|
||||
import { InvalidArgumentError } from "../errors.js";
|
||||
import Settings from "../settings.js";
|
||||
import { dayOfWeek, isoWeekdayToLocal } from "./conversions.js";
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -43,6 +44,18 @@ export function hasRelative() {
|
||||
}
|
||||
}
|
||||
|
||||
export function hasLocaleWeekInfo() {
|
||||
try {
|
||||
return (
|
||||
typeof Intl !== "undefined" &&
|
||||
!!Intl.Locale &&
|
||||
("weekInfo" in Intl.Locale.prototype || "getWeekInfo" in Intl.Locale.prototype)
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// OBJECTS AND ARRAYS
|
||||
|
||||
export function maybeArray(thing) {
|
||||
@@ -76,6 +89,28 @@ export function hasOwnProperty(obj, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
}
|
||||
|
||||
export function validateWeekSettings(settings) {
|
||||
if (settings == null) {
|
||||
return null;
|
||||
} else if (typeof settings !== "object") {
|
||||
throw new InvalidArgumentError("Week settings must be an object");
|
||||
} else {
|
||||
if (
|
||||
!integerBetween(settings.firstDay, 1, 7) ||
|
||||
!integerBetween(settings.minimalDays, 1, 7) ||
|
||||
!Array.isArray(settings.weekend) ||
|
||||
settings.weekend.some((v) => !integerBetween(v, 1, 7))
|
||||
) {
|
||||
throw new InvalidArgumentError("Invalid week settings");
|
||||
}
|
||||
return {
|
||||
firstDay: settings.firstDay,
|
||||
minimalDays: settings.minimalDays,
|
||||
weekend: Array.from(settings.weekend),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// NUMBERS AND STRINGS
|
||||
|
||||
export function integerBetween(thing, bottom, top) {
|
||||
@@ -174,16 +209,16 @@ export function objToLocalTS(obj) {
|
||||
return +d;
|
||||
}
|
||||
|
||||
export function weeksInWeekYear(weekYear) {
|
||||
const p1 =
|
||||
(weekYear +
|
||||
Math.floor(weekYear / 4) -
|
||||
Math.floor(weekYear / 100) +
|
||||
Math.floor(weekYear / 400)) %
|
||||
7,
|
||||
last = weekYear - 1,
|
||||
p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7;
|
||||
return p1 === 4 || p2 === 3 ? 53 : 52;
|
||||
// adapted from moment.js: https://github.com/moment/moment/blob/000ac1800e620f770f4eb31b5ae908f6167b0ab2/src/lib/units/week-calendar-utils.js
|
||||
function firstWeekOffset(year, minDaysInFirstWeek, startOfWeek) {
|
||||
const fwdlw = isoWeekdayToLocal(dayOfWeek(year, 1, minDaysInFirstWeek), startOfWeek);
|
||||
return -fwdlw + minDaysInFirstWeek - 1;
|
||||
}
|
||||
|
||||
export function weeksInWeekYear(weekYear, minDaysInFirstWeek = 4, startOfWeek = 1) {
|
||||
const weekOffset = firstWeekOffset(weekYear, minDaysInFirstWeek, startOfWeek);
|
||||
const weekOffsetNext = firstWeekOffset(weekYear + 1, minDaysInFirstWeek, startOfWeek);
|
||||
return (daysInYear(weekYear) - weekOffset + weekOffsetNext) / 7;
|
||||
}
|
||||
|
||||
export function untruncateYear(year) {
|
||||
|
||||
+39
-3
@@ -4,7 +4,7 @@ import Locale from "./impl/locale.js";
|
||||
import IANAZone from "./zones/IANAZone.js";
|
||||
import { normalizeZone } from "./impl/zoneUtil.js";
|
||||
|
||||
import { hasRelative } from "./impl/util.js";
|
||||
import { hasLocaleWeekInfo, hasRelative } from "./impl/util.js";
|
||||
|
||||
/**
|
||||
* The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.
|
||||
@@ -48,6 +48,41 @@ export default class Info {
|
||||
return normalizeZone(input, Settings.defaultZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weekday on which the week starts according to the given locale.
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number} the start of the week, 1 for Monday through 7 for Sunday
|
||||
*/
|
||||
static getStartOfWeek({ locale = null, locObj = null } = {}) {
|
||||
return (locObj || Locale.create(locale)).getStartOfWeek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum number of days necessary in a week before it is considered part of the next year according
|
||||
* to the given locale.
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number}
|
||||
*/
|
||||
static getMinimumDaysInFirstWeek({ locale = null, locObj = null } = {}) {
|
||||
return (locObj || Locale.create(locale)).getMinDaysInFirstWeek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weekdays, which are considered the weekend according to the given locale
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number[]} an array of weekdays, 1 for Monday through 7 for Sunday
|
||||
*/
|
||||
static getWeekendWeekdays({ locale = null, locObj = null } = {}) {
|
||||
// copy the array, because we cache it internally
|
||||
return (locObj || Locale.create(locale)).getWeekendDays().slice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of standalone month names.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
|
||||
@@ -160,10 +195,11 @@ export default class Info {
|
||||
* Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case.
|
||||
* Keys:
|
||||
* * `relative`: whether this environment supports relative time formatting
|
||||
* @example Info.features() //=> { relative: false }
|
||||
* * `localeWeek`: whether this environment supports different weekdays for the start of the week based on the locale
|
||||
* @example Info.features() //=> { relative: false, localeWeek: true }
|
||||
* @return {Object}
|
||||
*/
|
||||
static features() {
|
||||
return { relative: hasRelative() };
|
||||
return { relative: hasRelative(), localeWeek: hasLocaleWeekInfo() };
|
||||
}
|
||||
}
|
||||
|
||||
+24
-4
@@ -234,12 +234,20 @@ export default class Interval {
|
||||
* Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
|
||||
* asks 'what dates are included in this interval?', not 'how many days long is this interval?'
|
||||
* @param {string} [unit='milliseconds'] - the unit of time to count.
|
||||
* @param {Object} opts - options
|
||||
* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime
|
||||
* @return {number}
|
||||
*/
|
||||
count(unit = "milliseconds") {
|
||||
count(unit = "milliseconds", opts) {
|
||||
if (!this.isValid) return NaN;
|
||||
const start = this.start.startOf(unit),
|
||||
end = this.end.startOf(unit);
|
||||
const start = this.start.startOf(unit, opts);
|
||||
let end;
|
||||
if (opts?.useLocaleWeeks) {
|
||||
end = this.end.reconfigure({ locale: start.locale });
|
||||
} else {
|
||||
end = this.end;
|
||||
}
|
||||
end = end.startOf(unit, opts);
|
||||
return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());
|
||||
}
|
||||
|
||||
@@ -312,7 +320,7 @@ export default class Interval {
|
||||
const sorted = dateTimes
|
||||
.map(friendlyDateTime)
|
||||
.filter((d) => this.contains(d))
|
||||
.sort(),
|
||||
.sort((a, b) => a.toMillis() - b.toMillis()),
|
||||
results = [];
|
||||
let { s } = this,
|
||||
i = 0;
|
||||
@@ -531,6 +539,18 @@ export default class Interval {
|
||||
return `[${this.s.toISO()} – ${this.e.toISO()})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Interval appropriate for the REPL.
|
||||
* @return {string}
|
||||
*/
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
if (this.isValid) {
|
||||
return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`;
|
||||
} else {
|
||||
return `Interval { Invalid, reason: ${this.invalidReason} }`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized string representing this Interval. Accepts the same options as the
|
||||
* Intl.DateTimeFormat constructor and any presets defined by Luxon, such as
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import InvalidZone from "./zones/invalidZone.js";
|
||||
import SystemZone from "./zones/systemZone.js";
|
||||
import Settings from "./settings.js";
|
||||
|
||||
const VERSION = "3.4.3";
|
||||
const VERSION = "3.4.4";
|
||||
|
||||
export {
|
||||
VERSION,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"type": "module",
|
||||
"version": "3.4.3"
|
||||
"version": "3.4.4"
|
||||
}
|
||||
|
||||
+28
-1
@@ -3,6 +3,7 @@ import IANAZone from "./zones/IANAZone.js";
|
||||
import Locale from "./impl/locale.js";
|
||||
|
||||
import { normalizeZone } from "./impl/zoneUtil.js";
|
||||
import { validateWeekSettings } from "./impl/util.js";
|
||||
|
||||
let now = () => Date.now(),
|
||||
defaultZone = "system",
|
||||
@@ -10,7 +11,8 @@ let now = () => Date.now(),
|
||||
defaultNumberingSystem = null,
|
||||
defaultOutputCalendar = null,
|
||||
twoDigitCutoffYear = 60,
|
||||
throwOnInvalid;
|
||||
throwOnInvalid,
|
||||
defaultWeekSettings = null;
|
||||
|
||||
/**
|
||||
* Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here.
|
||||
@@ -101,6 +103,31 @@ export default class Settings {
|
||||
defaultOutputCalendar = outputCalendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} WeekSettings
|
||||
* @property {number} firstDay
|
||||
* @property {number} minimalDays
|
||||
* @property {number[]} weekend
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {WeekSettings|null}
|
||||
*/
|
||||
static get defaultWeekSettings() {
|
||||
return defaultWeekSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding the default locale week settings, i.e. the start of the week, the weekend and
|
||||
* how many days are required in the first week of a year.
|
||||
* Does not affect existing instances.
|
||||
*
|
||||
* @param {WeekSettings|null} weekSettings
|
||||
*/
|
||||
static set defaultWeekSettings(weekSettings) {
|
||||
defaultWeekSettings = validateWeekSettings(weekSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.
|
||||
* @type {number}
|
||||
|
||||
Reference in New Issue
Block a user