UNPKG

81.3 kBJavaScriptView Raw
1import Duration, { friendlyDuration } from "./duration.js";
2import Interval from "./interval.js";
3import Settings from "./settings.js";
4import Info from "./info.js";
5import Formatter from "./impl/formatter.js";
6import FixedOffsetZone from "./zones/fixedOffsetZone.js";
7import Locale from "./impl/locale.js";
8import {
9 isUndefined,
10 maybeArray,
11 isDate,
12 isNumber,
13 bestBy,
14 daysInMonth,
15 daysInYear,
16 isLeapYear,
17 weeksInWeekYear,
18 normalizeObject,
19 roundTo,
20 objToLocalTS,
21} from "./impl/util.js";
22import { normalizeZone } from "./impl/zoneUtil.js";
23import diff from "./impl/diff.js";
24import { parseRFC2822Date, parseISODate, parseHTTPDate, parseSQL } from "./impl/regexParser.js";
25import { parseFromTokens, explainFromTokens } from "./impl/tokenParser.js";
26import {
27 gregorianToWeek,
28 weekToGregorian,
29 gregorianToOrdinal,
30 ordinalToGregorian,
31 hasInvalidGregorianData,
32 hasInvalidWeekData,
33 hasInvalidOrdinalData,
34 hasInvalidTimeData,
35} from "./impl/conversions.js";
36import * as Formats from "./impl/formats.js";
37import {
38 InvalidArgumentError,
39 ConflictingSpecificationError,
40 InvalidUnitError,
41 InvalidDateTimeError,
42} from "./errors.js";
43import Invalid from "./impl/invalid.js";
44
45const INVALID = "Invalid DateTime";
46const MAX_DATE = 8.64e15;
47
48function unsupportedZone(zone) {
49 return new Invalid("unsupported zone", `the zone "${zone.name}" is not supported`);
50}
51
52// we cache week data on the DT object and this intermediates the cache
53function possiblyCachedWeekData(dt) {
54 if (dt.weekData === null) {
55 dt.weekData = gregorianToWeek(dt.c);
56 }
57 return dt.weekData;
58}
59
60// clone really means, "make a new object with these modifications". all "setters" really use this
61// to create a new object while only changing some of the properties
62function clone(inst, alts) {
63 const current = {
64 ts: inst.ts,
65 zone: inst.zone,
66 c: inst.c,
67 o: inst.o,
68 loc: inst.loc,
69 invalid: inst.invalid,
70 };
71 return new DateTime({ ...current, ...alts, old: current });
72}
73
74// find the right offset a given local time. The o input is our guess, which determines which
75// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
76function fixOffset(localTS, o, tz) {
77 // Our UTC time is just a guess because our offset is just a guess
78 let utcGuess = localTS - o * 60 * 1000;
79
80 // Test whether the zone matches the offset for this ts
81 const o2 = tz.offset(utcGuess);
82
83 // If so, offset didn't change and we're done
84 if (o === o2) {
85 return [utcGuess, o];
86 }
87
88 // If not, change the ts by the difference in the offset
89 utcGuess -= (o2 - o) * 60 * 1000;
90
91 // If that gives us the local time we want, we're done
92 const o3 = tz.offset(utcGuess);
93 if (o2 === o3) {
94 return [utcGuess, o2];
95 }
96
97 // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time
98 return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];
99}
100
101// convert an epoch timestamp into a calendar object with the given offset
102function tsToObj(ts, offset) {
103 ts += offset * 60 * 1000;
104
105 const d = new Date(ts);
106
107 return {
108 year: d.getUTCFullYear(),
109 month: d.getUTCMonth() + 1,
110 day: d.getUTCDate(),
111 hour: d.getUTCHours(),
112 minute: d.getUTCMinutes(),
113 second: d.getUTCSeconds(),
114 millisecond: d.getUTCMilliseconds(),
115 };
116}
117
118// convert a calendar object to a epoch timestamp
119function objToTS(obj, offset, zone) {
120 return fixOffset(objToLocalTS(obj), offset, zone);
121}
122
123// create a new DT instance by adding a duration, adjusting for DSTs
124function adjustTime(inst, dur) {
125 const oPre = inst.o,
126 year = inst.c.year + Math.trunc(dur.years),
127 month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3,
128 c = {
129 ...inst.c,
130 year,
131 month,
132 day:
133 Math.min(inst.c.day, daysInMonth(year, month)) +
134 Math.trunc(dur.days) +
135 Math.trunc(dur.weeks) * 7,
136 },
137 millisToAdd = Duration.fromObject({
138 years: dur.years - Math.trunc(dur.years),
139 quarters: dur.quarters - Math.trunc(dur.quarters),
140 months: dur.months - Math.trunc(dur.months),
141 weeks: dur.weeks - Math.trunc(dur.weeks),
142 days: dur.days - Math.trunc(dur.days),
143 hours: dur.hours,
144 minutes: dur.minutes,
145 seconds: dur.seconds,
146 milliseconds: dur.milliseconds,
147 }).as("milliseconds"),
148 localTS = objToLocalTS(c);
149
150 let [ts, o] = fixOffset(localTS, oPre, inst.zone);
151
152 if (millisToAdd !== 0) {
153 ts += millisToAdd;
154 // that could have changed the offset by going over a DST, but we want to keep the ts the same
155 o = inst.zone.offset(ts);
156 }
157
158 return { ts, o };
159}
160
161// helper useful in turning the results of parsing into real dates
162// by handling the zone options
163function parseDataToDateTime(parsed, parsedZone, opts, format, text) {
164 const { setZone, zone } = opts;
165 if (parsed && Object.keys(parsed).length !== 0) {
166 const interpretationZone = parsedZone || zone,
167 inst = DateTime.fromObject(parsed, {
168 ...opts,
169 zone: interpretationZone,
170 });
171 return setZone ? inst : inst.setZone(zone);
172 } else {
173 return DateTime.invalid(
174 new Invalid("unparsable", `the input "${text}" can't be parsed as ${format}`)
175 );
176 }
177}
178
179// if you want to output a technical format (e.g. RFC 2822), this helper
180// helps handle the details
181function toTechFormat(dt, format, allowZ = true) {
182 return dt.isValid
183 ? Formatter.create(Locale.create("en-US"), {
184 allowZ,
185 forceSimple: true,
186 }).formatDateTimeFromString(dt, format)
187 : null;
188}
189
190// technical time formats (e.g. the time part of ISO 8601), take some options
191// and this commonizes their handling
192function toTechTimeFormat(
193 dt,
194 {
195 suppressSeconds = false,
196 suppressMilliseconds = false,
197 includeOffset,
198 includePrefix = false,
199 includeZone = false,
200 spaceZone = false,
201 format = "extended",
202 }
203) {
204 let fmt = format === "basic" ? "HHmm" : "HH:mm";
205
206 if (!suppressSeconds || dt.second !== 0 || dt.millisecond !== 0) {
207 fmt += format === "basic" ? "ss" : ":ss";
208 if (!suppressMilliseconds || dt.millisecond !== 0) {
209 fmt += ".SSS";
210 }
211 }
212
213 if ((includeZone || includeOffset) && spaceZone) {
214 fmt += " ";
215 }
216
217 if (includeZone) {
218 fmt += "z";
219 } else if (includeOffset) {
220 fmt += format === "basic" ? "ZZZ" : "ZZ";
221 }
222
223 let str = toTechFormat(dt, fmt);
224
225 if (includePrefix) {
226 str = "T" + str;
227 }
228
229 return str;
230}
231
232// defaults for unspecified units in the supported calendars
233const defaultUnitValues = {
234 month: 1,
235 day: 1,
236 hour: 0,
237 minute: 0,
238 second: 0,
239 millisecond: 0,
240 },
241 defaultWeekUnitValues = {
242 weekNumber: 1,
243 weekday: 1,
244 hour: 0,
245 minute: 0,
246 second: 0,
247 millisecond: 0,
248 },
249 defaultOrdinalUnitValues = {
250 ordinal: 1,
251 hour: 0,
252 minute: 0,
253 second: 0,
254 millisecond: 0,
255 };
256
257// Units in the supported calendars, sorted by bigness
258const orderedUnits = ["year", "month", "day", "hour", "minute", "second", "millisecond"],
259 orderedWeekUnits = [
260 "weekYear",
261 "weekNumber",
262 "weekday",
263 "hour",
264 "minute",
265 "second",
266 "millisecond",
267 ],
268 orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
269
270// standardize case and plurality in units
271function normalizeUnit(unit) {
272 const normalized = {
273 year: "year",
274 years: "year",
275 month: "month",
276 months: "month",
277 day: "day",
278 days: "day",
279 hour: "hour",
280 hours: "hour",
281 minute: "minute",
282 minutes: "minute",
283 quarter: "quarter",
284 quarters: "quarter",
285 second: "second",
286 seconds: "second",
287 millisecond: "millisecond",
288 milliseconds: "millisecond",
289 weekday: "weekday",
290 weekdays: "weekday",
291 weeknumber: "weekNumber",
292 weeksnumber: "weekNumber",
293 weeknumbers: "weekNumber",
294 weekyear: "weekYear",
295 weekyears: "weekYear",
296 ordinal: "ordinal",
297 }[unit.toLowerCase()];
298
299 if (!normalized) throw new InvalidUnitError(unit);
300
301 return normalized;
302}
303
304// this is a dumbed down version of fromObject() that runs about 60% faster
305// but doesn't do any validation, makes a bunch of assumptions about what units
306// are present, and so on.
307
308// this is a dumbed down version of fromObject() that runs about 60% faster
309// but doesn't do any validation, makes a bunch of assumptions about what units
310// are present, and so on.
311function quickDT(obj, opts) {
312 const zone = normalizeZone(opts.zone, Settings.defaultZone),
313 loc = Locale.fromObject(opts),
314 tsNow = Settings.now();
315
316 let ts, o;
317
318 // assume we have the higher-order units
319 if (!isUndefined(obj.year)) {
320 for (const u of orderedUnits) {
321 if (isUndefined(obj[u])) {
322 obj[u] = defaultUnitValues[u];
323 }
324 }
325
326 const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj);
327 if (invalid) {
328 return DateTime.invalid(invalid);
329 }
330
331 const offsetProvis = zone.offset(tsNow);
332 [ts, o] = objToTS(obj, offsetProvis, zone);
333 } else {
334 ts = tsNow;
335 }
336
337 return new DateTime({ ts, zone, loc, o });
338}
339
340function diffRelative(start, end, opts) {
341 const round = isUndefined(opts.round) ? true : opts.round,
342 format = (c, unit) => {
343 c = roundTo(c, round || opts.calendary ? 0 : 2, true);
344 const formatter = end.loc.clone(opts).relFormatter(opts);
345 return formatter.format(c, unit);
346 },
347 differ = (unit) => {
348 if (opts.calendary) {
349 if (!end.hasSame(start, unit)) {
350 return end.startOf(unit).diff(start.startOf(unit), unit).get(unit);
351 } else return 0;
352 } else {
353 return end.diff(start, unit).get(unit);
354 }
355 };
356
357 if (opts.unit) {
358 return format(differ(opts.unit), opts.unit);
359 }
360
361 for (const unit of opts.units) {
362 const count = differ(unit);
363 if (Math.abs(count) >= 1) {
364 return format(count, unit);
365 }
366 }
367 return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]);
368}
369
370function lastOpts(argList) {
371 let opts = {},
372 args;
373 if (argList.length > 0 && typeof argList[argList.length - 1] === "object") {
374 opts = argList[argList.length - 1];
375 args = Array.from(argList).slice(0, argList.length - 1);
376 } else {
377 args = Array.from(argList);
378 }
379 return [opts, args];
380}
381
382/**
383 * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
384 *
385 * A DateTime comprises of:
386 * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch.
387 * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone).
388 * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`.
389 *
390 * Here is a brief overview of the most commonly used functionality it provides:
391 *
392 * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link DateTime.local}, {@link DateTime.utc}, and (most flexibly) {@link DateTime.fromObject}. To create one from a standard string format, use {@link DateTime.fromISO}, {@link DateTime.fromHTTP}, and {@link DateTime.fromRFC2822}. To create one from a custom string format, use {@link DateTime.fromFormat}. To create one from a native JS date, use {@link DateTime.fromJSDate}.
393 * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link DateTime#toObject}), use the {@link DateTime#year}, {@link DateTime#month},
394 * {@link DateTime#day}, {@link DateTime#hour}, {@link DateTime#minute}, {@link DateTime#second}, {@link DateTime#millisecond} accessors.
395 * * **Week calendar**: For ISO week calendar attributes, see the {@link DateTime#weekYear}, {@link DateTime#weekNumber}, and {@link DateTime#weekday} accessors.
396 * * **Configuration** See the {@link DateTime#locale} and {@link DateTime#numberingSystem} accessors.
397 * * **Transformation**: To transform the DateTime into other DateTimes, use {@link DateTime#set}, {@link DateTime#reconfigure}, {@link DateTime#setZone}, {@link DateTime#setLocale}, {@link DateTime.plus}, {@link DateTime#minus}, {@link DateTime#endOf}, {@link DateTime#startOf}, {@link DateTime#toUTC}, and {@link DateTime#toLocal}.
398 * * **Output**: To convert the DateTime to other representations, use the {@link DateTime#toRelative}, {@link DateTime#toRelativeCalendar}, {@link DateTime#toJSON}, {@link DateTime#toISO}, {@link DateTime#toHTTP}, {@link DateTime#toObject}, {@link DateTime#toRFC2822}, {@link DateTime#toString}, {@link DateTime#toLocaleString}, {@link DateTime#toFormat}, {@link DateTime#toMillis} and {@link DateTime#toJSDate}.
399 *
400 * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation.
401 */
402export default class DateTime {
403 /**
404 * @access private
405 */
406 constructor(config) {
407 const zone = config.zone || Settings.defaultZone;
408
409 let invalid =
410 config.invalid ||
411 (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) ||
412 (!zone.isValid ? unsupportedZone(zone) : null);
413 /**
414 * @access private
415 */
416 this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;
417
418 let c = null,
419 o = null;
420 if (!invalid) {
421 const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);
422
423 if (unchanged) {
424 [c, o] = [config.old.c, config.old.o];
425 } else {
426 const ot = zone.offset(this.ts);
427 c = tsToObj(this.ts, ot);
428 invalid = Number.isNaN(c.year) ? new Invalid("invalid input") : null;
429 c = invalid ? null : c;
430 o = invalid ? null : ot;
431 }
432 }
433
434 /**
435 * @access private
436 */
437 this._zone = zone;
438 /**
439 * @access private
440 */
441 this.loc = config.loc || Locale.create();
442 /**
443 * @access private
444 */
445 this.invalid = invalid;
446 /**
447 * @access private
448 */
449 this.weekData = null;
450 /**
451 * @access private
452 */
453 this.c = c;
454 /**
455 * @access private
456 */
457 this.o = o;
458 /**
459 * @access private
460 */
461 this.isLuxonDateTime = true;
462 }
463
464 // CONSTRUCT
465
466 /**
467 * Create a DateTime for the current instant, in the system's time zone.
468 *
469 * Use Settings to override these default values if needed.
470 * @example DateTime.now().toISO() //~> now in the ISO format
471 * @return {DateTime}
472 */
473 static now() {
474 return new DateTime({});
475 }
476
477 /**
478 * Create a local DateTime
479 * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used
480 * @param {number} [month=1] - The month, 1-indexed
481 * @param {number} [day=1] - The day of the month, 1-indexed
482 * @param {number} [hour=0] - The hour of the day, in 24-hour time
483 * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
484 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
485 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
486 * @example DateTime.local() //~> now
487 * @example DateTime.local({ zone: "America/New_York" }) //~> now, in US east coast time
488 * @example DateTime.local(2017) //~> 2017-01-01T00:00:00
489 * @example DateTime.local(2017, 3) //~> 2017-03-01T00:00:00
490 * @example DateTime.local(2017, 3, 12, { locale: "fr" }) //~> 2017-03-12T00:00:00, with a French locale
491 * @example DateTime.local(2017, 3, 12, 5) //~> 2017-03-12T05:00:00
492 * @example DateTime.local(2017, 3, 12, 5, { zone: "utc" }) //~> 2017-03-12T05:00:00, in UTC
493 * @example DateTime.local(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00
494 * @example DateTime.local(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10
495 * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765
496 * @return {DateTime}
497 */
498 static local() {
499 const [opts, args] = lastOpts(arguments),
500 [year, month, day, hour, minute, second, millisecond] = args;
501 return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);
502 }
503
504 /**
505 * Create a DateTime in UTC
506 * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used
507 * @param {number} [month=1] - The month, 1-indexed
508 * @param {number} [day=1] - The day of the month
509 * @param {number} [hour=0] - The hour of the day, in 24-hour time
510 * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
511 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
512 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
513 * @param {Object} options - configuration options for the DateTime
514 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
515 * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance
516 * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance
517 * @example DateTime.utc() //~> now
518 * @example DateTime.utc(2017) //~> 2017-01-01T00:00:00Z
519 * @example DateTime.utc(2017, 3) //~> 2017-03-01T00:00:00Z
520 * @example DateTime.utc(2017, 3, 12) //~> 2017-03-12T00:00:00Z
521 * @example DateTime.utc(2017, 3, 12, 5) //~> 2017-03-12T05:00:00Z
522 * @example DateTime.utc(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00Z
523 * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: "fr" }) //~> 2017-03-12T05:45:00Z with a French locale
524 * @example DateTime.utc(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10Z
525 * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: "fr" }) //~> 2017-03-12T05:45:10.765Z with a French locale
526 * @return {DateTime}
527 */
528 static utc() {
529 const [opts, args] = lastOpts(arguments),
530 [year, month, day, hour, minute, second, millisecond] = args;
531
532 opts.zone = FixedOffsetZone.utcInstance;
533 return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);
534 }
535
536 /**
537 * Create a DateTime from a JavaScript Date object. Uses the default zone.
538 * @param {Date} date - a JavaScript Date object
539 * @param {Object} options - configuration options for the DateTime
540 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
541 * @return {DateTime}
542 */
543 static fromJSDate(date, options = {}) {
544 const ts = isDate(date) ? date.valueOf() : NaN;
545 if (Number.isNaN(ts)) {
546 return DateTime.invalid("invalid input");
547 }
548
549 const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);
550 if (!zoneToUse.isValid) {
551 return DateTime.invalid(unsupportedZone(zoneToUse));
552 }
553
554 return new DateTime({
555 ts: ts,
556 zone: zoneToUse,
557 loc: Locale.fromObject(options),
558 });
559 }
560
561 /**
562 * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
563 * @param {number} milliseconds - a number of milliseconds since 1970 UTC
564 * @param {Object} options - configuration options for the DateTime
565 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
566 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
567 * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
568 * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
569 * @return {DateTime}
570 */
571 static fromMillis(milliseconds, options = {}) {
572 if (!isNumber(milliseconds)) {
573 throw new InvalidArgumentError(
574 `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`
575 );
576 } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {
577 // this isn't perfect because because we can still end up out of range because of additional shifting, but it's a start
578 return DateTime.invalid("Timestamp out of range");
579 } else {
580 return new DateTime({
581 ts: milliseconds,
582 zone: normalizeZone(options.zone, Settings.defaultZone),
583 loc: Locale.fromObject(options),
584 });
585 }
586 }
587
588 /**
589 * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
590 * @param {number} seconds - a number of seconds since 1970 UTC
591 * @param {Object} options - configuration options for the DateTime
592 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
593 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
594 * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
595 * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
596 * @return {DateTime}
597 */
598 static fromSeconds(seconds, options = {}) {
599 if (!isNumber(seconds)) {
600 throw new InvalidArgumentError("fromSeconds requires a numerical input");
601 } else {
602 return new DateTime({
603 ts: seconds * 1000,
604 zone: normalizeZone(options.zone, Settings.defaultZone),
605 loc: Locale.fromObject(options),
606 });
607 }
608 }
609
610 /**
611 * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults.
612 * @param {Object} obj - the object to create the DateTime from
613 * @param {number} obj.year - a year, such as 1987
614 * @param {number} obj.month - a month, 1-12
615 * @param {number} obj.day - a day of the month, 1-31, depending on the month
616 * @param {number} obj.ordinal - day of the year, 1-365 or 366
617 * @param {number} obj.weekYear - an ISO week year
618 * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year
619 * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday
620 * @param {number} obj.hour - hour of the day, 0-23
621 * @param {number} obj.minute - minute of the hour, 0-59
622 * @param {number} obj.second - second of the minute, 0-59
623 * @param {number} obj.millisecond - millisecond of the second, 0-999
624 * @param {Object} opts - options for creating this DateTime
625 * @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()
626 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
627 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
628 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
629 * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'
630 * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01'
631 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06
632 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }),
633 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' })
634 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' })
635 * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'
636 * @return {DateTime}
637 */
638 static fromObject(obj, opts = {}) {
639 obj = obj || {};
640 const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone);
641 if (!zoneToUse.isValid) {
642 return DateTime.invalid(unsupportedZone(zoneToUse));
643 }
644
645 const tsNow = Settings.now(),
646 offsetProvis = zoneToUse.offset(tsNow),
647 normalized = normalizeObject(obj, normalizeUnit),
648 containsOrdinal = !isUndefined(normalized.ordinal),
649 containsGregorYear = !isUndefined(normalized.year),
650 containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
651 containsGregor = containsGregorYear || containsGregorMD,
652 definiteWeekDef = normalized.weekYear || normalized.weekNumber,
653 loc = Locale.fromObject(opts);
654
655 // cases:
656 // just a weekday -> this week's instance of that weekday, no worries
657 // (gregorian data or ordinal) + (weekYear or weekNumber) -> error
658 // (gregorian month or day) + ordinal -> error
659 // otherwise just use weeks or ordinals or gregorian, depending on what's specified
660
661 if ((containsGregor || containsOrdinal) && definiteWeekDef) {
662 throw new ConflictingSpecificationError(
663 "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
664 );
665 }
666
667 if (containsGregorMD && containsOrdinal) {
668 throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
669 }
670
671 const useWeekData = definiteWeekDef || (normalized.weekday && !containsGregor);
672
673 // configure ourselves to deal with gregorian dates or week stuff
674 let units,
675 defaultValues,
676 objNow = tsToObj(tsNow, offsetProvis);
677 if (useWeekData) {
678 units = orderedWeekUnits;
679 defaultValues = defaultWeekUnitValues;
680 objNow = gregorianToWeek(objNow);
681 } else if (containsOrdinal) {
682 units = orderedOrdinalUnits;
683 defaultValues = defaultOrdinalUnitValues;
684 objNow = gregorianToOrdinal(objNow);
685 } else {
686 units = orderedUnits;
687 defaultValues = defaultUnitValues;
688 }
689
690 // set default values for missing stuff
691 let foundFirst = false;
692 for (const u of units) {
693 const v = normalized[u];
694 if (!isUndefined(v)) {
695 foundFirst = true;
696 } else if (foundFirst) {
697 normalized[u] = defaultValues[u];
698 } else {
699 normalized[u] = objNow[u];
700 }
701 }
702
703 // make sure the values we have are in range
704 const higherOrderInvalid = useWeekData
705 ? hasInvalidWeekData(normalized)
706 : containsOrdinal
707 ? hasInvalidOrdinalData(normalized)
708 : hasInvalidGregorianData(normalized),
709 invalid = higherOrderInvalid || hasInvalidTimeData(normalized);
710
711 if (invalid) {
712 return DateTime.invalid(invalid);
713 }
714
715 // compute the actual time
716 const gregorian = useWeekData
717 ? weekToGregorian(normalized)
718 : containsOrdinal
719 ? ordinalToGregorian(normalized)
720 : normalized,
721 [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse),
722 inst = new DateTime({
723 ts: tsFinal,
724 zone: zoneToUse,
725 o: offsetFinal,
726 loc,
727 });
728
729 // gregorian data + weekday serves only to validate
730 if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {
731 return DateTime.invalid(
732 "mismatched weekday",
733 `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`
734 );
735 }
736
737 return inst;
738 }
739
740 /**
741 * Create a DateTime from an ISO 8601 string
742 * @param {string} text - the ISO string
743 * @param {Object} opts - options to affect the creation
744 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone
745 * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
746 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
747 * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance
748 * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance
749 * @example DateTime.fromISO('2016-05-25T09:08:34.123')
750 * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00')
751 * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true})
752 * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'})
753 * @example DateTime.fromISO('2016-W05-4')
754 * @return {DateTime}
755 */
756 static fromISO(text, opts = {}) {
757 const [vals, parsedZone] = parseISODate(text);
758 return parseDataToDateTime(vals, parsedZone, opts, "ISO 8601", text);
759 }
760
761 /**
762 * Create a DateTime from an RFC 2822 string
763 * @param {string} text - the RFC 2822 string
764 * @param {Object} opts - options to affect the creation
765 * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
766 * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
767 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
768 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
769 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
770 * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT')
771 * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600')
772 * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z')
773 * @return {DateTime}
774 */
775 static fromRFC2822(text, opts = {}) {
776 const [vals, parsedZone] = parseRFC2822Date(text);
777 return parseDataToDateTime(vals, parsedZone, opts, "RFC 2822", text);
778 }
779
780 /**
781 * Create a DateTime from an HTTP header date
782 * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
783 * @param {string} text - the HTTP header date
784 * @param {Object} opts - options to affect the creation
785 * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
786 * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods.
787 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
788 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
789 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
790 * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT')
791 * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT')
792 * @example DateTime.fromHTTP('Sun Nov 6 08:49:37 1994')
793 * @return {DateTime}
794 */
795 static fromHTTP(text, opts = {}) {
796 const [vals, parsedZone] = parseHTTPDate(text);
797 return parseDataToDateTime(vals, parsedZone, opts, "HTTP", opts);
798 }
799
800 /**
801 * Create a DateTime from an input string and format string.
802 * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens).
803 * @param {string} text - the string to parse
804 * @param {string} fmt - the format the string is expected to be in (see the link below for the formats)
805 * @param {Object} opts - options to affect the creation
806 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
807 * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
808 * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
809 * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
810 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
811 * @return {DateTime}
812 */
813 static fromFormat(text, fmt, opts = {}) {
814 if (isUndefined(text) || isUndefined(fmt)) {
815 throw new InvalidArgumentError("fromFormat requires an input string and a format");
816 }
817
818 const { locale = null, numberingSystem = null } = opts,
819 localeToUse = Locale.fromOpts({
820 locale,
821 numberingSystem,
822 defaultToEN: true,
823 }),
824 [vals, parsedZone, invalid] = parseFromTokens(localeToUse, text, fmt);
825 if (invalid) {
826 return DateTime.invalid(invalid);
827 } else {
828 return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text);
829 }
830 }
831
832 /**
833 * @deprecated use fromFormat instead
834 */
835 static fromString(text, fmt, opts = {}) {
836 return DateTime.fromFormat(text, fmt, opts);
837 }
838
839 /**
840 * Create a DateTime from a SQL date, time, or datetime
841 * Defaults to en-US if no locale has been specified, regardless of the system's locale
842 * @param {string} text - the string to parse
843 * @param {Object} opts - options to affect the creation
844 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
845 * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
846 * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
847 * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
848 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
849 * @example DateTime.fromSQL('2017-05-15')
850 * @example DateTime.fromSQL('2017-05-15 09:12:34')
851 * @example DateTime.fromSQL('2017-05-15 09:12:34.342')
852 * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00')
853 * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles')
854 * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true })
855 * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' })
856 * @example DateTime.fromSQL('09:12:34.342')
857 * @return {DateTime}
858 */
859 static fromSQL(text, opts = {}) {
860 const [vals, parsedZone] = parseSQL(text);
861 return parseDataToDateTime(vals, parsedZone, opts, "SQL", text);
862 }
863
864 /**
865 * Create an invalid DateTime.
866 * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent
867 * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
868 * @return {DateTime}
869 */
870 static invalid(reason, explanation = null) {
871 if (!reason) {
872 throw new InvalidArgumentError("need to specify a reason the DateTime is invalid");
873 }
874
875 const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
876
877 if (Settings.throwOnInvalid) {
878 throw new InvalidDateTimeError(invalid);
879 } else {
880 return new DateTime({ invalid });
881 }
882 }
883
884 /**
885 * Check if an object is a DateTime. Works across context boundaries
886 * @param {object} o
887 * @return {boolean}
888 */
889 static isDateTime(o) {
890 return (o && o.isLuxonDateTime) || false;
891 }
892
893 // INFO
894
895 /**
896 * Get the value of unit.
897 * @param {string} unit - a unit such as 'minute' or 'day'
898 * @example DateTime.local(2017, 7, 4).get('month'); //=> 7
899 * @example DateTime.local(2017, 7, 4).get('day'); //=> 4
900 * @return {number}
901 */
902 get(unit) {
903 return this[unit];
904 }
905
906 /**
907 * Returns whether the DateTime is valid. Invalid DateTimes occur when:
908 * * The DateTime was created from invalid calendar information, such as the 13th month or February 30
909 * * The DateTime was created by an operation on another invalid date
910 * @type {boolean}
911 */
912 get isValid() {
913 return this.invalid === null;
914 }
915
916 /**
917 * Returns an error code if this DateTime is invalid, or null if the DateTime is valid
918 * @type {string}
919 */
920 get invalidReason() {
921 return this.invalid ? this.invalid.reason : null;
922 }
923
924 /**
925 * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid
926 * @type {string}
927 */
928 get invalidExplanation() {
929 return this.invalid ? this.invalid.explanation : null;
930 }
931
932 /**
933 * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime
934 *
935 * @type {string}
936 */
937 get locale() {
938 return this.isValid ? this.loc.locale : null;
939 }
940
941 /**
942 * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime
943 *
944 * @type {string}
945 */
946 get numberingSystem() {
947 return this.isValid ? this.loc.numberingSystem : null;
948 }
949
950 /**
951 * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime
952 *
953 * @type {string}
954 */
955 get outputCalendar() {
956 return this.isValid ? this.loc.outputCalendar : null;
957 }
958
959 /**
960 * Get the time zone associated with this DateTime.
961 * @type {Zone}
962 */
963 get zone() {
964 return this._zone;
965 }
966
967 /**
968 * Get the name of the time zone.
969 * @type {string}
970 */
971 get zoneName() {
972 return this.isValid ? this.zone.name : null;
973 }
974
975 /**
976 * Get the year
977 * @example DateTime.local(2017, 5, 25).year //=> 2017
978 * @type {number}
979 */
980 get year() {
981 return this.isValid ? this.c.year : NaN;
982 }
983
984 /**
985 * Get the quarter
986 * @example DateTime.local(2017, 5, 25).quarter //=> 2
987 * @type {number}
988 */
989 get quarter() {
990 return this.isValid ? Math.ceil(this.c.month / 3) : NaN;
991 }
992
993 /**
994 * Get the month (1-12).
995 * @example DateTime.local(2017, 5, 25).month //=> 5
996 * @type {number}
997 */
998 get month() {
999 return this.isValid ? this.c.month : NaN;
1000 }
1001
1002 /**
1003 * Get the day of the month (1-30ish).
1004 * @example DateTime.local(2017, 5, 25).day //=> 25
1005 * @type {number}
1006 */
1007 get day() {
1008 return this.isValid ? this.c.day : NaN;
1009 }
1010
1011 /**
1012 * Get the hour of the day (0-23).
1013 * @example DateTime.local(2017, 5, 25, 9).hour //=> 9
1014 * @type {number}
1015 */
1016 get hour() {
1017 return this.isValid ? this.c.hour : NaN;
1018 }
1019
1020 /**
1021 * Get the minute of the hour (0-59).
1022 * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30
1023 * @type {number}
1024 */
1025 get minute() {
1026 return this.isValid ? this.c.minute : NaN;
1027 }
1028
1029 /**
1030 * Get the second of the minute (0-59).
1031 * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52
1032 * @type {number}
1033 */
1034 get second() {
1035 return this.isValid ? this.c.second : NaN;
1036 }
1037
1038 /**
1039 * Get the millisecond of the second (0-999).
1040 * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654
1041 * @type {number}
1042 */
1043 get millisecond() {
1044 return this.isValid ? this.c.millisecond : NaN;
1045 }
1046
1047 /**
1048 * Get the week year
1049 * @see https://en.wikipedia.org/wiki/ISO_week_date
1050 * @example DateTime.local(2014, 12, 31).weekYear //=> 2015
1051 * @type {number}
1052 */
1053 get weekYear() {
1054 return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN;
1055 }
1056
1057 /**
1058 * Get the week number of the week year (1-52ish).
1059 * @see https://en.wikipedia.org/wiki/ISO_week_date
1060 * @example DateTime.local(2017, 5, 25).weekNumber //=> 21
1061 * @type {number}
1062 */
1063 get weekNumber() {
1064 return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN;
1065 }
1066
1067 /**
1068 * Get the day of the week.
1069 * 1 is Monday and 7 is Sunday
1070 * @see https://en.wikipedia.org/wiki/ISO_week_date
1071 * @example DateTime.local(2014, 11, 31).weekday //=> 4
1072 * @type {number}
1073 */
1074 get weekday() {
1075 return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;
1076 }
1077
1078 /**
1079 * Get the ordinal (meaning the day of the year)
1080 * @example DateTime.local(2017, 5, 25).ordinal //=> 145
1081 * @type {number|DateTime}
1082 */
1083 get ordinal() {
1084 return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN;
1085 }
1086
1087 /**
1088 * Get the human readable short month name, such as 'Oct'.
1089 * Defaults to the system's locale if no locale has been specified
1090 * @example DateTime.local(2017, 10, 30).monthShort //=> Oct
1091 * @type {string}
1092 */
1093 get monthShort() {
1094 return this.isValid ? Info.months("short", { locObj: this.loc })[this.month - 1] : null;
1095 }
1096
1097 /**
1098 * Get the human readable long month name, such as 'October'.
1099 * Defaults to the system's locale if no locale has been specified
1100 * @example DateTime.local(2017, 10, 30).monthLong //=> October
1101 * @type {string}
1102 */
1103 get monthLong() {
1104 return this.isValid ? Info.months("long", { locObj: this.loc })[this.month - 1] : null;
1105 }
1106
1107 /**
1108 * Get the human readable short weekday, such as 'Mon'.
1109 * Defaults to the system's locale if no locale has been specified
1110 * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon
1111 * @type {string}
1112 */
1113 get weekdayShort() {
1114 return this.isValid ? Info.weekdays("short", { locObj: this.loc })[this.weekday - 1] : null;
1115 }
1116
1117 /**
1118 * Get the human readable long weekday, such as 'Monday'.
1119 * Defaults to the system's locale if no locale has been specified
1120 * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday
1121 * @type {string}
1122 */
1123 get weekdayLong() {
1124 return this.isValid ? Info.weekdays("long", { locObj: this.loc })[this.weekday - 1] : null;
1125 }
1126
1127 /**
1128 * Get the UTC offset of this DateTime in minutes
1129 * @example DateTime.now().offset //=> -240
1130 * @example DateTime.utc().offset //=> 0
1131 * @type {number}
1132 */
1133 get offset() {
1134 return this.isValid ? +this.o : NaN;
1135 }
1136
1137 /**
1138 * Get the short human name for the zone's current offset, for example "EST" or "EDT".
1139 * Defaults to the system's locale if no locale has been specified
1140 * @type {string}
1141 */
1142 get offsetNameShort() {
1143 if (this.isValid) {
1144 return this.zone.offsetName(this.ts, {
1145 format: "short",
1146 locale: this.locale,
1147 });
1148 } else {
1149 return null;
1150 }
1151 }
1152
1153 /**
1154 * Get the long human name for the zone's current offset, for example "Eastern Standard Time" or "Eastern Daylight Time".
1155 * Defaults to the system's locale if no locale has been specified
1156 * @type {string}
1157 */
1158 get offsetNameLong() {
1159 if (this.isValid) {
1160 return this.zone.offsetName(this.ts, {
1161 format: "long",
1162 locale: this.locale,
1163 });
1164 } else {
1165 return null;
1166 }
1167 }
1168
1169 /**
1170 * Get whether this zone's offset ever changes, as in a DST.
1171 * @type {boolean}
1172 */
1173 get isOffsetFixed() {
1174 return this.isValid ? this.zone.isUniversal : null;
1175 }
1176
1177 /**
1178 * Get whether the DateTime is in a DST.
1179 * @type {boolean}
1180 */
1181 get isInDST() {
1182 if (this.isOffsetFixed) {
1183 return false;
1184 } else {
1185 return (
1186 this.offset > this.set({ month: 1 }).offset || this.offset > this.set({ month: 5 }).offset
1187 );
1188 }
1189 }
1190
1191 /**
1192 * Returns true if this DateTime is in a leap year, false otherwise
1193 * @example DateTime.local(2016).isInLeapYear //=> true
1194 * @example DateTime.local(2013).isInLeapYear //=> false
1195 * @type {boolean}
1196 */
1197 get isInLeapYear() {
1198 return isLeapYear(this.year);
1199 }
1200
1201 /**
1202 * Returns the number of days in this DateTime's month
1203 * @example DateTime.local(2016, 2).daysInMonth //=> 29
1204 * @example DateTime.local(2016, 3).daysInMonth //=> 31
1205 * @type {number}
1206 */
1207 get daysInMonth() {
1208 return daysInMonth(this.year, this.month);
1209 }
1210
1211 /**
1212 * Returns the number of days in this DateTime's year
1213 * @example DateTime.local(2016).daysInYear //=> 366
1214 * @example DateTime.local(2013).daysInYear //=> 365
1215 * @type {number}
1216 */
1217 get daysInYear() {
1218 return this.isValid ? daysInYear(this.year) : NaN;
1219 }
1220
1221 /**
1222 * Returns the number of weeks in this DateTime's year
1223 * @see https://en.wikipedia.org/wiki/ISO_week_date
1224 * @example DateTime.local(2004).weeksInWeekYear //=> 53
1225 * @example DateTime.local(2013).weeksInWeekYear //=> 52
1226 * @type {number}
1227 */
1228 get weeksInWeekYear() {
1229 return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;
1230 }
1231
1232 /**
1233 * Returns the resolved Intl options for this DateTime.
1234 * This is useful in understanding the behavior of formatting methods
1235 * @param {Object} opts - the same options as toLocaleString
1236 * @return {Object}
1237 */
1238 resolvedLocaleOptions(opts = {}) {
1239 const { locale, numberingSystem, calendar } = Formatter.create(
1240 this.loc.clone(opts),
1241 opts
1242 ).resolvedOptions(this);
1243 return { locale, numberingSystem, outputCalendar: calendar };
1244 }
1245
1246 // TRANSFORM
1247
1248 /**
1249 * "Set" the DateTime's zone to UTC. Returns a newly-constructed DateTime.
1250 *
1251 * Equivalent to {@link DateTime.setZone}('utc')
1252 * @param {number} [offset=0] - optionally, an offset from UTC in minutes
1253 * @param {Object} [opts={}] - options to pass to `setZone()`
1254 * @return {DateTime}
1255 */
1256 toUTC(offset = 0, opts = {}) {
1257 return this.setZone(FixedOffsetZone.instance(offset), opts);
1258 }
1259
1260 /**
1261 * "Set" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime.
1262 *
1263 * Equivalent to `setZone('local')`
1264 * @return {DateTime}
1265 */
1266 toLocal() {
1267 return this.setZone(Settings.defaultZone);
1268 }
1269
1270 /**
1271 * "Set" the DateTime's zone to specified zone. Returns a newly-constructed DateTime.
1272 *
1273 * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime.plus}. You may wish to use {@link DateTime.toLocal} and {@link DateTime.toUTC} which provide simple convenience wrappers for commonly used zones.
1274 * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime.Zone} class.
1275 * @param {Object} opts - options
1276 * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this.
1277 * @return {DateTime}
1278 */
1279 setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) {
1280 zone = normalizeZone(zone, Settings.defaultZone);
1281 if (zone.equals(this.zone)) {
1282 return this;
1283 } else if (!zone.isValid) {
1284 return DateTime.invalid(unsupportedZone(zone));
1285 } else {
1286 let newTS = this.ts;
1287 if (keepLocalTime || keepCalendarTime) {
1288 const offsetGuess = zone.offset(this.ts);
1289 const asObj = this.toObject();
1290 [newTS] = objToTS(asObj, offsetGuess, zone);
1291 }
1292 return clone(this, { ts: newTS, zone });
1293 }
1294 }
1295
1296 /**
1297 * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime.
1298 * @param {Object} properties - the properties to set
1299 * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' })
1300 * @return {DateTime}
1301 */
1302 reconfigure({ locale, numberingSystem, outputCalendar } = {}) {
1303 const loc = this.loc.clone({ locale, numberingSystem, outputCalendar });
1304 return clone(this, { loc });
1305 }
1306
1307 /**
1308 * "Set" the locale. Returns a newly-constructed DateTime.
1309 * Just a convenient alias for reconfigure({ locale })
1310 * @example DateTime.local(2017, 5, 25).setLocale('en-GB')
1311 * @return {DateTime}
1312 */
1313 setLocale(locale) {
1314 return this.reconfigure({ locale });
1315 }
1316
1317 /**
1318 * "Set" the values of specified units. Returns a newly-constructed DateTime.
1319 * You can only set units with this method; for "setting" metadata, see {@link DateTime.reconfigure} and {@link DateTime.setZone}.
1320 * @param {Object} values - a mapping of units to numbers
1321 * @example dt.set({ year: 2017 })
1322 * @example dt.set({ hour: 8, minute: 30 })
1323 * @example dt.set({ weekday: 5 })
1324 * @example dt.set({ year: 2005, ordinal: 234 })
1325 * @return {DateTime}
1326 */
1327 set(values) {
1328 if (!this.isValid) return this;
1329
1330 const normalized = normalizeObject(values, normalizeUnit),
1331 settingWeekStuff =
1332 !isUndefined(normalized.weekYear) ||
1333 !isUndefined(normalized.weekNumber) ||
1334 !isUndefined(normalized.weekday),
1335 containsOrdinal = !isUndefined(normalized.ordinal),
1336 containsGregorYear = !isUndefined(normalized.year),
1337 containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
1338 containsGregor = containsGregorYear || containsGregorMD,
1339 definiteWeekDef = normalized.weekYear || normalized.weekNumber;
1340
1341 if ((containsGregor || containsOrdinal) && definiteWeekDef) {
1342 throw new ConflictingSpecificationError(
1343 "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
1344 );
1345 }
1346
1347 if (containsGregorMD && containsOrdinal) {
1348 throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
1349 }
1350
1351 let mixed;
1352 if (settingWeekStuff) {
1353 mixed = weekToGregorian({ ...gregorianToWeek(this.c), ...normalized });
1354 } else if (!isUndefined(normalized.ordinal)) {
1355 mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });
1356 } else {
1357 mixed = { ...this.toObject(), ...normalized };
1358
1359 // if we didn't set the day but we ended up on an overflow date,
1360 // use the last day of the right month
1361 if (isUndefined(normalized.day)) {
1362 mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
1363 }
1364 }
1365
1366 const [ts, o] = objToTS(mixed, this.o, this.zone);
1367 return clone(this, { ts, o });
1368 }
1369
1370 /**
1371 * Add a period of time to this DateTime and return the resulting DateTime
1372 *
1373 * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between.
1374 * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
1375 * @example DateTime.now().plus(123) //~> in 123 milliseconds
1376 * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes
1377 * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow
1378 * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday
1379 * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min
1380 * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min
1381 * @return {DateTime}
1382 */
1383 plus(duration) {
1384 if (!this.isValid) return this;
1385 const dur = friendlyDuration(duration);
1386 return clone(this, adjustTime(this, dur));
1387 }
1388
1389 /**
1390 * Subtract a period of time to this DateTime and return the resulting DateTime
1391 * See {@link DateTime.plus}
1392 * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
1393 @return {DateTime}
1394 */
1395 minus(duration) {
1396 if (!this.isValid) return this;
1397 const dur = friendlyDuration(duration).negate();
1398 return clone(this, adjustTime(this, dur));
1399 }
1400
1401 /**
1402 * "Set" this DateTime to the beginning of a unit of time.
1403 * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
1404 * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'
1405 * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'
1406 * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays
1407 * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00'
1408 * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'
1409 * @return {DateTime}
1410 */
1411 startOf(unit) {
1412 if (!this.isValid) return this;
1413 const o = {},
1414 normalizedUnit = Duration.normalizeUnit(unit);
1415 switch (normalizedUnit) {
1416 case "years":
1417 o.month = 1;
1418 // falls through
1419 case "quarters":
1420 case "months":
1421 o.day = 1;
1422 // falls through
1423 case "weeks":
1424 case "days":
1425 o.hour = 0;
1426 // falls through
1427 case "hours":
1428 o.minute = 0;
1429 // falls through
1430 case "minutes":
1431 o.second = 0;
1432 // falls through
1433 case "seconds":
1434 o.millisecond = 0;
1435 break;
1436 case "milliseconds":
1437 break;
1438 // no default, invalid units throw in normalizeUnit()
1439 }
1440
1441 if (normalizedUnit === "weeks") {
1442 o.weekday = 1;
1443 }
1444
1445 if (normalizedUnit === "quarters") {
1446 const q = Math.ceil(this.month / 3);
1447 o.month = (q - 1) * 3 + 1;
1448 }
1449
1450 return this.set(o);
1451 }
1452
1453 /**
1454 * "Set" this DateTime to the end (meaning the last millisecond) of a unit of time
1455 * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
1456 * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'
1457 * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'
1458 * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays
1459 * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00'
1460 * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'
1461 * @return {DateTime}
1462 */
1463 endOf(unit) {
1464 return this.isValid
1465 ? this.plus({ [unit]: 1 })
1466 .startOf(unit)
1467 .minus(1)
1468 : this;
1469 }
1470
1471 // OUTPUT
1472
1473 /**
1474 * Returns a string representation of this DateTime formatted according to the specified format string.
1475 * **You may not want this.** See {@link DateTime.toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens).
1476 * Defaults to en-US if no locale has been specified, regardless of the system's locale.
1477 * @param {string} fmt - the format string
1478 * @param {Object} opts - opts to override the configuration options on this DateTime
1479 * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22'
1480 * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22'
1481 * @example DateTime.now().toFormat('yyyy LLL dd', { locale: "fr" }) //=> '2017 avr. 22'
1482 * @example DateTime.now().toFormat("HH 'hours and' mm 'minutes'") //=> '20 hours and 55 minutes'
1483 * @return {string}
1484 */
1485 toFormat(fmt, opts = {}) {
1486 return this.isValid
1487 ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt)
1488 : INVALID;
1489 }
1490
1491 /**
1492 * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`.
1493 * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation
1494 * of the DateTime in the assigned locale.
1495 * Defaults to the system's locale if no locale has been specified
1496 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
1497 * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options
1498 * @param {Object} opts - opts to override the configuration options on this DateTime
1499 * @example DateTime.now().toLocaleString(); //=> 4/20/2017
1500 * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017'
1501 * @example DateTime.now().toLocaleString({ locale: 'en-gb' }); //=> '20/04/2017'
1502 * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017'
1503 * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM'
1504 * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM'
1505 * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20'
1506 * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM'
1507 * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32'
1508 * @return {string}
1509 */
1510 toLocaleString(formatOpts = Formats.DATE_SHORT, opts = {}) {
1511 return this.isValid
1512 ? Formatter.create(this.loc.clone(opts), formatOpts).formatDateTime(this)
1513 : INVALID;
1514 }
1515
1516 /**
1517 * Returns an array of format "parts", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output.
1518 * Defaults to the system's locale if no locale has been specified
1519 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
1520 * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`.
1521 * @example DateTime.now().toLocaleParts(); //=> [
1522 * //=> { type: 'day', value: '25' },
1523 * //=> { type: 'literal', value: '/' },
1524 * //=> { type: 'month', value: '05' },
1525 * //=> { type: 'literal', value: '/' },
1526 * //=> { type: 'year', value: '1982' }
1527 * //=> ]
1528 */
1529 toLocaleParts(opts = {}) {
1530 return this.isValid
1531 ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this)
1532 : [];
1533 }
1534
1535 /**
1536 * Returns an ISO 8601-compliant string representation of this DateTime
1537 * @param {Object} opts - options
1538 * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
1539 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
1540 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1541 * @param {string} [opts.format='extended'] - choose between the basic and extended format
1542 * @example DateTime.utc(1982, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'
1543 * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
1544 * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
1545 * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
1546 * @return {string}
1547 */
1548 toISO(opts = {}) {
1549 if (!this.isValid) {
1550 return null;
1551 }
1552
1553 return `${this.toISODate(opts)}T${this.toISOTime(opts)}`;
1554 }
1555
1556 /**
1557 * Returns an ISO 8601-compliant string representation of this DateTime's date component
1558 * @param {Object} opts - options
1559 * @param {string} [opts.format='extended'] - choose between the basic and extended format
1560 * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
1561 * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
1562 * @return {string}
1563 */
1564 toISODate({ format = "extended" } = {}) {
1565 let fmt = format === "basic" ? "yyyyMMdd" : "yyyy-MM-dd";
1566 if (this.year > 9999) {
1567 fmt = "+" + fmt;
1568 }
1569
1570 return toTechFormat(this, fmt);
1571 }
1572
1573 /**
1574 * Returns an ISO 8601-compliant string representation of this DateTime's week date
1575 * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2'
1576 * @return {string}
1577 */
1578 toISOWeekDate() {
1579 return toTechFormat(this, "kkkk-'W'WW-c");
1580 }
1581
1582 /**
1583 * Returns an ISO 8601-compliant string representation of this DateTime's time component
1584 * @param {Object} opts - options
1585 * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
1586 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
1587 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1588 * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
1589 * @param {string} [opts.format='extended'] - choose between the basic and extended format
1590 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'
1591 * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'
1592 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'
1593 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'
1594 * @return {string}
1595 */
1596 toISOTime({
1597 suppressMilliseconds = false,
1598 suppressSeconds = false,
1599 includeOffset = true,
1600 includePrefix = false,
1601 format = "extended",
1602 } = {}) {
1603 return toTechTimeFormat(this, {
1604 suppressSeconds,
1605 suppressMilliseconds,
1606 includeOffset,
1607 includePrefix,
1608 format,
1609 });
1610 }
1611
1612 /**
1613 * Returns an RFC 2822-compatible string representation of this DateTime, always in UTC
1614 * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000'
1615 * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400'
1616 * @return {string}
1617 */
1618 toRFC2822() {
1619 return toTechFormat(this, "EEE, dd LLL yyyy HH:mm:ss ZZZ", false);
1620 }
1621
1622 /**
1623 * Returns a string representation of this DateTime appropriate for use in HTTP headers.
1624 * Specifically, the string conforms to RFC 1123.
1625 * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
1626 * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT'
1627 * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT'
1628 * @return {string}
1629 */
1630 toHTTP() {
1631 return toTechFormat(this.toUTC(), "EEE, dd LLL yyyy HH:mm:ss 'GMT'");
1632 }
1633
1634 /**
1635 * Returns a string representation of this DateTime appropriate for use in SQL Date
1636 * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
1637 * @return {string}
1638 */
1639 toSQLDate() {
1640 return toTechFormat(this, "yyyy-MM-dd");
1641 }
1642
1643 /**
1644 * Returns a string representation of this DateTime appropriate for use in SQL Time
1645 * @param {Object} opts - options
1646 * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
1647 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1648 * @example DateTime.utc().toSQL() //=> '05:15:16.345'
1649 * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00'
1650 * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345'
1651 * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York'
1652 * @return {string}
1653 */
1654 toSQLTime({ includeOffset = true, includeZone = false } = {}) {
1655 return toTechTimeFormat(this, {
1656 includeOffset,
1657 includeZone,
1658 spaceZone: true,
1659 });
1660 }
1661
1662 /**
1663 * Returns a string representation of this DateTime appropriate for use in SQL DateTime
1664 * @param {Object} opts - options
1665 * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
1666 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1667 * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z'
1668 * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00'
1669 * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000'
1670 * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York'
1671 * @return {string}
1672 */
1673 toSQL(opts = {}) {
1674 if (!this.isValid) {
1675 return null;
1676 }
1677
1678 return `${this.toSQLDate()} ${this.toSQLTime(opts)}`;
1679 }
1680
1681 /**
1682 * Returns a string representation of this DateTime appropriate for debugging
1683 * @return {string}
1684 */
1685 toString() {
1686 return this.isValid ? this.toISO() : INVALID;
1687 }
1688
1689 /**
1690 * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime.toMillis}
1691 * @return {number}
1692 */
1693 valueOf() {
1694 return this.toMillis();
1695 }
1696
1697 /**
1698 * Returns the epoch milliseconds of this DateTime.
1699 * @return {number}
1700 */
1701 toMillis() {
1702 return this.isValid ? this.ts : NaN;
1703 }
1704
1705 /**
1706 * Returns the epoch seconds of this DateTime.
1707 * @return {number}
1708 */
1709 toSeconds() {
1710 return this.isValid ? this.ts / 1000 : NaN;
1711 }
1712
1713 /**
1714 * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON.
1715 * @return {string}
1716 */
1717 toJSON() {
1718 return this.toISO();
1719 }
1720
1721 /**
1722 * Returns a BSON serializable equivalent to this DateTime.
1723 * @return {Date}
1724 */
1725 toBSON() {
1726 return this.toJSDate();
1727 }
1728
1729 /**
1730 * Returns a JavaScript object with this DateTime's year, month, day, and so on.
1731 * @param opts - options for generating the object
1732 * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output
1733 * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 }
1734 * @return {Object}
1735 */
1736 toObject(opts = {}) {
1737 if (!this.isValid) return {};
1738
1739 const base = { ...this.c };
1740
1741 if (opts.includeConfig) {
1742 base.outputCalendar = this.outputCalendar;
1743 base.numberingSystem = this.loc.numberingSystem;
1744 base.locale = this.loc.locale;
1745 }
1746 return base;
1747 }
1748
1749 /**
1750 * Returns a JavaScript Date equivalent to this DateTime.
1751 * @return {Date}
1752 */
1753 toJSDate() {
1754 return new Date(this.isValid ? this.ts : NaN);
1755 }
1756
1757 // COMPARE
1758
1759 /**
1760 * Return the difference between two DateTimes as a Duration.
1761 * @param {DateTime} otherDateTime - the DateTime to compare this one to
1762 * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration.
1763 * @param {Object} opts - options that affect the creation of the Duration
1764 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
1765 * @example
1766 * var i1 = DateTime.fromISO('1982-05-25T09:45'),
1767 * i2 = DateTime.fromISO('1983-10-14T10:30');
1768 * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 }
1769 * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 }
1770 * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 }
1771 * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 }
1772 * @return {Duration}
1773 */
1774 diff(otherDateTime, unit = "milliseconds", opts = {}) {
1775 if (!this.isValid || !otherDateTime.isValid) {
1776 return Duration.invalid("created by diffing an invalid DateTime");
1777 }
1778
1779 const durOpts = { locale: this.locale, numberingSystem: this.numberingSystem, ...opts };
1780
1781 const units = maybeArray(unit).map(Duration.normalizeUnit),
1782 otherIsLater = otherDateTime.valueOf() > this.valueOf(),
1783 earlier = otherIsLater ? this : otherDateTime,
1784 later = otherIsLater ? otherDateTime : this,
1785 diffed = diff(earlier, later, units, durOpts);
1786
1787 return otherIsLater ? diffed.negate() : diffed;
1788 }
1789
1790 /**
1791 * Return the difference between this DateTime and right now.
1792 * See {@link DateTime.diff}
1793 * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration
1794 * @param {Object} opts - options that affect the creation of the Duration
1795 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
1796 * @return {Duration}
1797 */
1798 diffNow(unit = "milliseconds", opts = {}) {
1799 return this.diff(DateTime.now(), unit, opts);
1800 }
1801
1802 /**
1803 * Return an Interval spanning between this DateTime and another DateTime
1804 * @param {DateTime} otherDateTime - the other end point of the Interval
1805 * @return {Interval}
1806 */
1807 until(otherDateTime) {
1808 return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
1809 }
1810
1811 /**
1812 * Return whether this DateTime is in the same unit of time as another DateTime.
1813 * Higher-order units must also be identical for this function to return `true`.
1814 * 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.
1815 * @param {DateTime} otherDateTime - the other DateTime
1816 * @param {string} unit - the unit of time to check sameness on
1817 * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day
1818 * @return {boolean}
1819 */
1820 hasSame(otherDateTime, unit) {
1821 if (!this.isValid) return false;
1822
1823 const inputMs = otherDateTime.valueOf();
1824 const otherZoneDateTime = this.setZone(otherDateTime.zone, { keepLocalTime: true });
1825 return otherZoneDateTime.startOf(unit) <= inputMs && inputMs <= otherZoneDateTime.endOf(unit);
1826 }
1827
1828 /**
1829 * Equality check
1830 * Two DateTimes are equal iff they represent the same millisecond, have the same zone and location, and are both valid.
1831 * To compare just the millisecond values, use `+dt1 === +dt2`.
1832 * @param {DateTime} other - the other DateTime
1833 * @return {boolean}
1834 */
1835 equals(other) {
1836 return (
1837 this.isValid &&
1838 other.isValid &&
1839 this.valueOf() === other.valueOf() &&
1840 this.zone.equals(other.zone) &&
1841 this.loc.equals(other.loc)
1842 );
1843 }
1844
1845 /**
1846 * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your
1847 * platform supports Intl.RelativeTimeFormat. Rounds down by default.
1848 * @param {Object} options - options that affect the output
1849 * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
1850 * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow"
1851 * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds"
1852 * @param {boolean} [options.round=true] - whether to round the numbers in the output.
1853 * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.
1854 * @param {string} options.locale - override the locale of this DateTime
1855 * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
1856 * @example DateTime.now().plus({ days: 1 }).toRelative() //=> "in 1 day"
1857 * @example DateTime.now().setLocale("es").toRelative({ days: 1 }) //=> "dentro de 1 día"
1858 * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: "fr" }) //=> "dans 23 heures"
1859 * @example DateTime.now().minus({ days: 2 }).toRelative() //=> "2 days ago"
1860 * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: "hours" }) //=> "48 hours ago"
1861 * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> "1.5 days ago"
1862 */
1863 toRelative(options = {}) {
1864 if (!this.isValid) return null;
1865 const base = options.base || DateTime.fromObject({}, { zone: this.zone }),
1866 padding = options.padding ? (this < base ? -options.padding : options.padding) : 0;
1867 let units = ["years", "months", "days", "hours", "minutes", "seconds"];
1868 let unit = options.unit;
1869 if (Array.isArray(options.unit)) {
1870 units = options.unit;
1871 unit = undefined;
1872 }
1873 return diffRelative(base, this.plus(padding), {
1874 ...options,
1875 numeric: "always",
1876 units,
1877 unit,
1878 });
1879 }
1880
1881 /**
1882 * Returns a string representation of this date relative to today, such as "yesterday" or "next month".
1883 * Only internationalizes on platforms that supports Intl.RelativeTimeFormat.
1884 * @param {Object} options - options that affect the output
1885 * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
1886 * @param {string} options.locale - override the locale of this DateTime
1887 * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of "years", "quarters", "months", "weeks", or "days"
1888 * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
1889 * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> "tomorrow"
1890 * @example DateTime.now().setLocale("es").plus({ days: 1 }).toRelative() //=> ""mañana"
1891 * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: "fr" }) //=> "demain"
1892 * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> "2 days ago"
1893 */
1894 toRelativeCalendar(options = {}) {
1895 if (!this.isValid) return null;
1896
1897 return diffRelative(options.base || DateTime.fromObject({}, { zone: this.zone }), this, {
1898 ...options,
1899 numeric: "auto",
1900 units: ["years", "months", "days"],
1901 calendary: true,
1902 });
1903 }
1904
1905 /**
1906 * Return the min of several date times
1907 * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum
1908 * @return {DateTime} the min DateTime, or undefined if called with no argument
1909 */
1910 static min(...dateTimes) {
1911 if (!dateTimes.every(DateTime.isDateTime)) {
1912 throw new InvalidArgumentError("min requires all arguments be DateTimes");
1913 }
1914 return bestBy(dateTimes, (i) => i.valueOf(), Math.min);
1915 }
1916
1917 /**
1918 * Return the max of several date times
1919 * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum
1920 * @return {DateTime} the max DateTime, or undefined if called with no argument
1921 */
1922 static max(...dateTimes) {
1923 if (!dateTimes.every(DateTime.isDateTime)) {
1924 throw new InvalidArgumentError("max requires all arguments be DateTimes");
1925 }
1926 return bestBy(dateTimes, (i) => i.valueOf(), Math.max);
1927 }
1928
1929 // MISC
1930
1931 /**
1932 * Explain how a string would be parsed by fromFormat()
1933 * @param {string} text - the string to parse
1934 * @param {string} fmt - the format the string is expected to be in (see description)
1935 * @param {Object} options - options taken by fromFormat()
1936 * @return {Object}
1937 */
1938 static fromFormatExplain(text, fmt, options = {}) {
1939 const { locale = null, numberingSystem = null } = options,
1940 localeToUse = Locale.fromOpts({
1941 locale,
1942 numberingSystem,
1943 defaultToEN: true,
1944 });
1945 return explainFromTokens(localeToUse, text, fmt);
1946 }
1947
1948 /**
1949 * @deprecated use fromFormatExplain instead
1950 */
1951 static fromStringExplain(text, fmt, options = {}) {
1952 return DateTime.fromFormatExplain(text, fmt, options);
1953 }
1954
1955 // FORMAT PRESETS
1956
1957 /**
1958 * {@link DateTime.toLocaleString} format like 10/14/1983
1959 * @type {Object}
1960 */
1961 static get DATE_SHORT() {
1962 return Formats.DATE_SHORT;
1963 }
1964
1965 /**
1966 * {@link DateTime.toLocaleString} format like 'Oct 14, 1983'
1967 * @type {Object}
1968 */
1969 static get DATE_MED() {
1970 return Formats.DATE_MED;
1971 }
1972
1973 /**
1974 * {@link DateTime.toLocaleString} format like 'Fri, Oct 14, 1983'
1975 * @type {Object}
1976 */
1977 static get DATE_MED_WITH_WEEKDAY() {
1978 return Formats.DATE_MED_WITH_WEEKDAY;
1979 }
1980
1981 /**
1982 * {@link DateTime.toLocaleString} format like 'October 14, 1983'
1983 * @type {Object}
1984 */
1985 static get DATE_FULL() {
1986 return Formats.DATE_FULL;
1987 }
1988
1989 /**
1990 * {@link DateTime.toLocaleString} format like 'Tuesday, October 14, 1983'
1991 * @type {Object}
1992 */
1993 static get DATE_HUGE() {
1994 return Formats.DATE_HUGE;
1995 }
1996
1997 /**
1998 * {@link DateTime.toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is.
1999 * @type {Object}
2000 */
2001 static get TIME_SIMPLE() {
2002 return Formats.TIME_SIMPLE;
2003 }
2004
2005 /**
2006 * {@link DateTime.toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is.
2007 * @type {Object}
2008 */
2009 static get TIME_WITH_SECONDS() {
2010 return Formats.TIME_WITH_SECONDS;
2011 }
2012
2013 /**
2014 * {@link DateTime.toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is.
2015 * @type {Object}
2016 */
2017 static get TIME_WITH_SHORT_OFFSET() {
2018 return Formats.TIME_WITH_SHORT_OFFSET;
2019 }
2020
2021 /**
2022 * {@link DateTime.toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2023 * @type {Object}
2024 */
2025 static get TIME_WITH_LONG_OFFSET() {
2026 return Formats.TIME_WITH_LONG_OFFSET;
2027 }
2028
2029 /**
2030 * {@link DateTime.toLocaleString} format like '09:30', always 24-hour.
2031 * @type {Object}
2032 */
2033 static get TIME_24_SIMPLE() {
2034 return Formats.TIME_24_SIMPLE;
2035 }
2036
2037 /**
2038 * {@link DateTime.toLocaleString} format like '09:30:23', always 24-hour.
2039 * @type {Object}
2040 */
2041 static get TIME_24_WITH_SECONDS() {
2042 return Formats.TIME_24_WITH_SECONDS;
2043 }
2044
2045 /**
2046 * {@link DateTime.toLocaleString} format like '09:30:23 EDT', always 24-hour.
2047 * @type {Object}
2048 */
2049 static get TIME_24_WITH_SHORT_OFFSET() {
2050 return Formats.TIME_24_WITH_SHORT_OFFSET;
2051 }
2052
2053 /**
2054 * {@link DateTime.toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour.
2055 * @type {Object}
2056 */
2057 static get TIME_24_WITH_LONG_OFFSET() {
2058 return Formats.TIME_24_WITH_LONG_OFFSET;
2059 }
2060
2061 /**
2062 * {@link DateTime.toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.
2063 * @type {Object}
2064 */
2065 static get DATETIME_SHORT() {
2066 return Formats.DATETIME_SHORT;
2067 }
2068
2069 /**
2070 * {@link DateTime.toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.
2071 * @type {Object}
2072 */
2073 static get DATETIME_SHORT_WITH_SECONDS() {
2074 return Formats.DATETIME_SHORT_WITH_SECONDS;
2075 }
2076
2077 /**
2078 * {@link DateTime.toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is.
2079 * @type {Object}
2080 */
2081 static get DATETIME_MED() {
2082 return Formats.DATETIME_MED;
2083 }
2084
2085 /**
2086 * {@link DateTime.toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is.
2087 * @type {Object}
2088 */
2089 static get DATETIME_MED_WITH_SECONDS() {
2090 return Formats.DATETIME_MED_WITH_SECONDS;
2091 }
2092
2093 /**
2094 * {@link DateTime.toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is.
2095 * @type {Object}
2096 */
2097 static get DATETIME_MED_WITH_WEEKDAY() {
2098 return Formats.DATETIME_MED_WITH_WEEKDAY;
2099 }
2100
2101 /**
2102 * {@link DateTime.toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is.
2103 * @type {Object}
2104 */
2105 static get DATETIME_FULL() {
2106 return Formats.DATETIME_FULL;
2107 }
2108
2109 /**
2110 * {@link DateTime.toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is.
2111 * @type {Object}
2112 */
2113 static get DATETIME_FULL_WITH_SECONDS() {
2114 return Formats.DATETIME_FULL_WITH_SECONDS;
2115 }
2116
2117 /**
2118 * {@link DateTime.toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2119 * @type {Object}
2120 */
2121 static get DATETIME_HUGE() {
2122 return Formats.DATETIME_HUGE;
2123 }
2124
2125 /**
2126 * {@link DateTime.toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2127 * @type {Object}
2128 */
2129 static get DATETIME_HUGE_WITH_SECONDS() {
2130 return Formats.DATETIME_HUGE_WITH_SECONDS;
2131 }
2132}
2133
2134/**
2135 * @private
2136 */
2137export function friendlyDateTime(dateTimeish) {
2138 if (DateTime.isDateTime(dateTimeish)) {
2139 return dateTimeish;
2140 } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) {
2141 return DateTime.fromJSDate(dateTimeish);
2142 } else if (dateTimeish && typeof dateTimeish === "object") {
2143 return DateTime.fromObject(dateTimeish);
2144 } else {
2145 throw new InvalidArgumentError(
2146 `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`
2147 );
2148 }
2149}