2021-07-01 15:56:08 +03:00
class LuxonError extends Error {
}
class InvalidDateTimeError extends LuxonError {
2021-09-01 16:11:55 +03:00
constructor ( reason ) {
super ( ` Invalid DateTime: ${ reason . toMessage ( ) } ` ) ;
2021-01-25 15:15:42 +03:00
}
}
2021-07-01 15:56:08 +03:00
class InvalidIntervalError extends LuxonError {
2021-12-02 10:28:09 +03:00
constructor ( reason ) {
super ( ` Invalid Interval: ${ reason . toMessage ( ) } ` ) ;
2021-07-01 15:56:08 +03:00
}
}
class InvalidDurationError extends LuxonError {
2021-12-02 10:28:09 +03:00
constructor ( reason ) {
super ( ` Invalid Duration: ${ reason . toMessage ( ) } ` ) ;
2021-07-01 15:56:08 +03:00
}
}
class ConflictingSpecificationError extends LuxonError {
}
class InvalidUnitError extends LuxonError {
2021-12-02 10:28:09 +03:00
constructor ( unit ) {
super ( ` Invalid unit ${ unit } ` ) ;
2021-07-01 15:56:08 +03:00
}
}
class InvalidArgumentError extends LuxonError {
}
class ZoneIsAbstractError extends LuxonError {
constructor ( ) {
super ( "Zone is an abstract class" ) ;
}
2021-01-25 15:15:42 +03:00
}
function isUndefined ( o ) {
return typeof o === "undefined" ;
}
function isNumber ( o ) {
return typeof o === "number" ;
}
function isInteger ( o ) {
return typeof o === "number" && o % 1 === 0 ;
}
function isString ( o ) {
return typeof o === "string" ;
}
function isDate ( o ) {
return Object . prototype . toString . call ( o ) === "[object Date]" ;
}
function hasIntl() {
try {
return typeof Intl !== "undefined" && Intl . DateTimeFormat ;
} catch ( e ) {
return false ;
}
}
function hasFormatToParts() {
return ! isUndefined ( Intl . DateTimeFormat . prototype . formatToParts ) ;
}
function hasRelative() {
try {
return typeof Intl !== "undefined" && ! ! Intl . RelativeTimeFormat ;
} catch ( e ) {
return false ;
}
}
function maybeArray ( thing ) {
return Array . isArray ( thing ) ? thing : [
thing
] ;
}
function bestBy ( arr , by , compare ) {
if ( arr . length === 0 ) {
return undefined ;
}
return arr . reduce ( ( best , next ) = > {
const pair = [
by ( next ) ,
next
] ;
if ( ! best ) {
return pair ;
} else if ( compare ( best [ 0 ] , pair [ 0 ] ) === best [ 0 ] ) {
return best ;
} else {
return pair ;
}
} , null ) [ 1 ] ;
}
function pick ( obj , keys ) {
return keys . reduce ( ( a , k ) = > {
a [ k ] = obj [ k ] ;
return a ;
2022-01-02 23:54:58 +03:00
} , { } ) ;
2021-01-25 15:15:42 +03:00
}
function hasOwnProperty ( obj , prop ) {
return Object . prototype . hasOwnProperty . call ( obj , prop ) ;
}
function integerBetween ( thing , bottom , top ) {
return isInteger ( thing ) && thing >= bottom && thing <= top ;
}
2022-06-15 17:36:57 +03:00
function floorMod ( x , n ) {
return x - n * Math . floor ( x / n ) ;
2021-01-25 15:15:42 +03:00
}
2022-06-15 17:36:57 +03:00
function padStart ( input , n = 2 ) {
if ( input . toString ( ) . length < n ) {
return ( "0" . repeat ( n ) + input ) . slice ( - n ) ;
2021-01-25 15:15:42 +03:00
} else {
return input . toString ( ) ;
}
}
function parseInteger ( string ) {
if ( isUndefined ( string ) || string === null || string === "" ) {
return undefined ;
} else {
return parseInt ( string , 10 ) ;
}
}
function parseMillis ( fraction ) {
if ( isUndefined ( fraction ) || fraction === null || fraction === "" ) {
return undefined ;
} else {
const f = parseFloat ( "0." + fraction ) * 1000 ;
return Math . floor ( f ) ;
}
}
function roundTo ( number , digits , towardZero = false ) {
const factor = 10 * * digits , rounder = towardZero ? Math . trunc : Math.round ;
return rounder ( number * factor ) / factor ;
}
function isLeapYear ( year ) {
return year % 4 === 0 && ( year % 100 !== 0 || year % 400 === 0 ) ;
}
function daysInYear ( year ) {
return isLeapYear ( year ) ? 366 : 365 ;
}
function daysInMonth ( year , month ) {
const modMonth = floorMod ( month - 1 , 12 ) + 1 , modYear = year + ( month - modMonth ) / 12 ;
if ( modMonth === 2 ) {
return isLeapYear ( modYear ) ? 29 : 28 ;
} else {
return [
31 ,
null ,
31 ,
30 ,
31 ,
30 ,
31 ,
31 ,
30 ,
31 ,
30 ,
31
] [ modMonth - 1 ] ;
}
}
function objToLocalTS ( obj ) {
let d = Date . UTC ( obj . year , obj . month - 1 , obj . day , obj . hour , obj . minute , obj . second , obj . millisecond ) ;
if ( obj . year < 100 && obj . year >= 0 ) {
d = new Date ( d ) ;
d . setUTCFullYear ( d . getUTCFullYear ( ) - 1900 ) ;
}
return + d ;
}
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 ;
}
function untruncateYear ( year ) {
if ( year > 99 ) {
return year ;
} else return year > 60 ? 1900 + year : 2000 + year ;
}
function parseZoneInfo ( ts , offsetFormat , locale , timeZone = null ) {
2022-06-15 17:36:57 +03:00
const date = new Date ( ts ) , intlOpts = {
2021-01-25 15:15:42 +03:00
hour12 : false ,
year : "numeric" ,
month : "2-digit" ,
day : "2-digit" ,
hour : "2-digit" ,
minute : "2-digit"
} ;
if ( timeZone ) {
intlOpts . timeZone = timeZone ;
}
const modified = Object . assign ( {
timeZoneName : offsetFormat
} , intlOpts ) , intl = hasIntl ( ) ;
if ( intl && hasFormatToParts ( ) ) {
2022-06-15 17:36:57 +03:00
const parsed = new Intl . DateTimeFormat ( locale , modified ) . formatToParts ( date ) . find ( ( m ) = > m . type . toLowerCase ( ) === "timezonename" ) ;
2021-01-25 15:15:42 +03:00
return parsed ? parsed.value : null ;
} else if ( intl ) {
2022-06-15 17:36:57 +03:00
const without = new Intl . DateTimeFormat ( locale , intlOpts ) . format ( date ) , included = new Intl . DateTimeFormat ( locale , modified ) . format ( date ) , diffed = included . substring ( without . length ) , trimmed = diffed . replace ( /^[, \u200e]+/ , "" ) ;
2021-01-25 15:15:42 +03:00
return trimmed ;
} else {
return null ;
}
}
function signedOffset ( offHourStr , offMinuteStr ) {
let offHour = parseInt ( offHourStr , 10 ) ;
if ( Number . isNaN ( offHour ) ) {
offHour = 0 ;
}
const offMin = parseInt ( offMinuteStr , 10 ) || 0 , offMinSigned = offHour < 0 || Object . is ( offHour , - 0 ) ? - offMin : offMin ;
return offHour * 60 + offMinSigned ;
}
function asNumber ( value ) {
const numericValue = Number ( value ) ;
if ( typeof value === "boolean" || value === "" || Number . isNaN ( numericValue ) ) throw new InvalidArgumentError ( ` Invalid unit value ${ value } ` ) ;
return numericValue ;
}
function normalizeObject ( obj , normalizer , nonUnitKeys ) {
2022-01-02 23:54:58 +03:00
const normalized = { } ;
2021-01-25 15:15:42 +03:00
for ( const u in obj ) {
if ( hasOwnProperty ( obj , u ) ) {
if ( nonUnitKeys . indexOf ( u ) >= 0 ) continue ;
const v = obj [ u ] ;
if ( v === undefined || v === null ) continue ;
normalized [ normalizer ( u ) ] = asNumber ( v ) ;
}
}
return normalized ;
}
2022-06-15 17:36:57 +03:00
function formatOffset ( offset , format ) {
const hours = Math . trunc ( Math . abs ( offset / 60 ) ) , minutes = Math . trunc ( Math . abs ( offset % 60 ) ) , sign = offset >= 0 ? "+" : "-" ;
2021-01-25 15:15:42 +03:00
switch ( format ) {
case "short" :
return ` ${ sign } ${ padStart ( hours , 2 ) } : ${ padStart ( minutes , 2 ) } ` ;
case "narrow" :
return ` ${ sign } ${ hours } ${ minutes > 0 ? ` : ${ minutes } ` : "" } ` ;
case "techie" :
return ` ${ sign } ${ padStart ( hours , 2 ) } ${ padStart ( minutes , 2 ) } ` ;
default :
throw new RangeError ( ` Value format ${ format } is out of range for property format ` ) ;
}
}
function timeObject ( obj ) {
return pick ( obj , [
"hour" ,
"minute" ,
"second" ,
"millisecond"
] ) ;
}
2021-02-22 11:27:40 +03:00
const ianaRegex = /[A-Za-z_+-]{1,256}(:?\/[A-Za-z_+-]{1,256}(\/[A-Za-z_+-]{1,256})?)?/ ;
2021-12-03 19:55:27 +03:00
const n = "numeric" , s = "short" , l = "long" ;
2021-07-01 15:56:08 +03:00
const DATE_SHORT = {
2021-12-03 19:55:27 +03:00
year : n ,
month : n ,
day : n
2021-05-25 08:30:17 +03:00
} ;
2021-07-01 15:56:08 +03:00
const DATE_MED = {
2021-12-03 19:55:27 +03:00
year : n ,
month : s ,
day : n
2021-05-25 08:30:17 +03:00
} ;
2021-07-01 15:56:08 +03:00
const DATE_MED_WITH_WEEKDAY = {
2021-12-03 19:55:27 +03:00
year : n ,
month : s ,
day : n ,
weekday : s
2021-05-25 08:30:17 +03:00
} ;
2021-07-01 15:56:08 +03:00
const DATE_FULL = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n
2021-07-01 15:56:08 +03:00
} ;
const DATE_HUGE = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n ,
2021-07-01 15:56:08 +03:00
weekday : l
} ;
const TIME_SIMPLE = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n
2021-07-01 15:56:08 +03:00
} ;
const TIME_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n
2021-07-01 15:56:08 +03:00
} ;
const TIME_WITH_SHORT_OFFSET = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
timeZoneName : s
2021-07-01 15:56:08 +03:00
} ;
const TIME_WITH_LONG_OFFSET = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
2021-07-01 15:56:08 +03:00
timeZoneName : l
} ;
const TIME_24_SIMPLE = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
2021-07-01 15:56:08 +03:00
hour12 : false
} ;
const TIME_24_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
2021-07-01 15:56:08 +03:00
hour12 : false
} ;
const TIME_24_WITH_SHORT_OFFSET = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
2021-07-01 15:56:08 +03:00
hour12 : false ,
2021-12-03 19:55:27 +03:00
timeZoneName : s
2021-07-01 15:56:08 +03:00
} ;
const TIME_24_WITH_LONG_OFFSET = {
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
2021-07-01 15:56:08 +03:00
hour12 : false ,
timeZoneName : l
} ;
const DATETIME_SHORT = {
2021-12-03 19:55:27 +03:00
year : n ,
month : n ,
day : n ,
hour : n ,
minute : n
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_SHORT_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
year : n ,
month : n ,
day : n ,
hour : n ,
minute : n ,
second : n
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_MED = {
2021-12-03 19:55:27 +03:00
year : n ,
month : s ,
day : n ,
hour : n ,
minute : n
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_MED_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
year : n ,
month : s ,
day : n ,
hour : n ,
minute : n ,
second : n
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_MED_WITH_WEEKDAY = {
2021-12-03 19:55:27 +03:00
year : n ,
month : s ,
day : n ,
weekday : s ,
hour : n ,
minute : n
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_FULL = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n ,
hour : n ,
minute : n ,
timeZoneName : s
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_FULL_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n ,
hour : n ,
minute : n ,
second : n ,
timeZoneName : s
2021-07-01 15:56:08 +03:00
} ;
const DATETIME_HUGE = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n ,
2021-07-01 15:56:08 +03:00
weekday : l ,
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
2021-07-01 15:56:08 +03:00
timeZoneName : l
} ;
const DATETIME_HUGE_WITH_SECONDS = {
2021-12-03 19:55:27 +03:00
year : n ,
2021-07-01 15:56:08 +03:00
month : l ,
2021-12-03 19:55:27 +03:00
day : n ,
2021-07-01 15:56:08 +03:00
weekday : l ,
2021-12-03 19:55:27 +03:00
hour : n ,
minute : n ,
second : n ,
2021-07-01 15:56:08 +03:00
timeZoneName : l
2021-05-25 08:30:17 +03:00
} ;
2021-07-01 15:56:08 +03:00
function stringify ( obj ) {
return JSON . stringify ( obj , Object . keys ( obj ) . sort ( ) ) ;
}
2021-05-25 08:30:17 +03:00
const monthsLong = [
"January" ,
"February" ,
"March" ,
"April" ,
"May" ,
"June" ,
"July" ,
"August" ,
"September" ,
"October" ,
"November" ,
"December"
] ;
const monthsShort = [
"Jan" ,
"Feb" ,
"Mar" ,
"Apr" ,
"May" ,
"Jun" ,
"Jul" ,
"Aug" ,
"Sep" ,
"Oct" ,
"Nov" ,
"Dec"
] ;
const monthsNarrow = [
"J" ,
"F" ,
"M" ,
"A" ,
"M" ,
"J" ,
"J" ,
"A" ,
"S" ,
"O" ,
"N" ,
"D"
] ;
function months ( length ) {
switch ( length ) {
case "narrow" :
return monthsNarrow ;
case "short" :
return monthsShort ;
case "long" :
return monthsLong ;
case "numeric" :
return [
"1" ,
"2" ,
"3" ,
"4" ,
"5" ,
"6" ,
"7" ,
"8" ,
"9" ,
"10" ,
"11" ,
"12"
] ;
case "2-digit" :
return [
"01" ,
"02" ,
"03" ,
"04" ,
"05" ,
"06" ,
"07" ,
"08" ,
"09" ,
"10" ,
"11" ,
"12"
] ;
default :
return null ;
}
}
const weekdaysLong = [
"Monday" ,
"Tuesday" ,
"Wednesday" ,
"Thursday" ,
"Friday" ,
"Saturday" ,
"Sunday"
] ;
const weekdaysShort = [
"Mon" ,
"Tue" ,
"Wed" ,
"Thu" ,
"Fri" ,
"Sat" ,
"Sun"
] ;
const weekdaysNarrow = [
"M" ,
"T" ,
"W" ,
"T" ,
"F" ,
"S" ,
"S"
] ;
function weekdays ( length ) {
switch ( length ) {
case "narrow" :
return weekdaysNarrow ;
case "short" :
return weekdaysShort ;
case "long" :
return weekdaysLong ;
case "numeric" :
return [
"1" ,
"2" ,
"3" ,
"4" ,
"5" ,
"6" ,
"7"
] ;
default :
return null ;
}
}
2021-07-01 15:56:08 +03:00
const meridiems = [
"AM" ,
"PM"
] ;
const erasLong = [
"Before Christ" ,
"Anno Domini"
] ;
const erasShort = [
"BC" ,
"AD"
] ;
const erasNarrow = [
"B" ,
"A"
] ;
function eras ( length ) {
switch ( length ) {
case "narrow" :
return erasNarrow ;
case "short" :
return erasShort ;
case "long" :
return erasLong ;
default :
return null ;
}
}
2022-06-15 17:36:57 +03:00
function meridiemForDateTime ( dt ) {
return meridiems [ dt . hour < 12 ? 0 : 1 ] ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
function weekdayForDateTime ( dt , length ) {
return weekdays ( length ) [ dt . weekday - 1 ] ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
function monthForDateTime ( dt , length ) {
return months ( length ) [ dt . month - 1 ] ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
function eraForDateTime ( dt , length ) {
return eras ( length ) [ dt . year < 0 ? 0 : 1 ] ;
2021-07-01 15:56:08 +03:00
}
2021-09-01 16:11:55 +03:00
function formatRelativeTime ( unit , count , numeric = "always" , narrow = false ) {
2021-07-01 15:56:08 +03:00
const units = {
years : [
"year" ,
"yr."
] ,
quarters : [
"quarter" ,
"qtr."
] ,
months : [
"month" ,
"mo."
] ,
weeks : [
"week" ,
"wk."
] ,
days : [
"day" ,
"day" ,
"days"
] ,
hours : [
"hour" ,
"hr."
] ,
minutes : [
"minute" ,
"min."
] ,
seconds : [
"second" ,
"sec."
]
} ;
const lastable = [
"hours" ,
"minutes" ,
"seconds"
2021-09-01 16:11:55 +03:00
] . indexOf ( unit ) === - 1 ;
2021-07-01 15:56:08 +03:00
if ( numeric === "auto" && lastable ) {
2021-09-01 16:11:55 +03:00
const isDay = unit === "days" ;
2021-07-01 15:56:08 +03:00
switch ( count ) {
case 1 :
2021-09-01 16:11:55 +03:00
return isDay ? "tomorrow" : ` next ${ units [ unit ] [ 0 ] } ` ;
2021-07-01 15:56:08 +03:00
case - 1 :
2021-09-01 16:11:55 +03:00
return isDay ? "yesterday" : ` last ${ units [ unit ] [ 0 ] } ` ;
2021-07-01 15:56:08 +03:00
case 0 :
2021-09-01 16:11:55 +03:00
return isDay ? "today" : ` this ${ units [ unit ] [ 0 ] } ` ;
2021-07-01 15:56:08 +03:00
default :
}
}
2021-09-01 16:11:55 +03:00
const isInPast = Object . is ( count , - 0 ) || count < 0 , fmtValue = Math . abs ( count ) , singular = fmtValue === 1 , lilUnits = units [ unit ] , fmtUnit = narrow ? singular ? lilUnits [ 1 ] : lilUnits [ 2 ] || lilUnits [ 1 ] : singular ? units [ unit ] [ 0 ] : unit ;
2021-07-01 15:56:08 +03:00
return isInPast ? ` ${ fmtValue } ${ fmtUnit } ago ` : ` in ${ fmtValue } ${ fmtUnit } ` ;
}
function formatString ( knownFormat ) {
const filtered = pick ( knownFormat , [
"weekday" ,
"era" ,
"year" ,
"month" ,
"day" ,
"hour" ,
"minute" ,
"second" ,
"timeZoneName" ,
"hour12"
] ) , key = stringify ( filtered ) , dateTimeHuge = "EEEE, LLLL d, yyyy, h:mm a" ;
switch ( key ) {
case stringify ( DATE_SHORT ) :
return "M/d/yyyy" ;
case stringify ( DATE_MED ) :
return "LLL d, yyyy" ;
case stringify ( DATE_MED_WITH_WEEKDAY ) :
return "EEE, LLL d, yyyy" ;
case stringify ( DATE_FULL ) :
return "LLLL d, yyyy" ;
case stringify ( DATE_HUGE ) :
return "EEEE, LLLL d, yyyy" ;
case stringify ( TIME_SIMPLE ) :
return "h:mm a" ;
case stringify ( TIME_WITH_SECONDS ) :
return "h:mm:ss a" ;
case stringify ( TIME_WITH_SHORT_OFFSET ) :
return "h:mm a" ;
case stringify ( TIME_WITH_LONG_OFFSET ) :
return "h:mm a" ;
case stringify ( TIME_24_SIMPLE ) :
return "HH:mm" ;
case stringify ( TIME_24_WITH_SECONDS ) :
return "HH:mm:ss" ;
case stringify ( TIME_24_WITH_SHORT_OFFSET ) :
return "HH:mm" ;
case stringify ( TIME_24_WITH_LONG_OFFSET ) :
return "HH:mm" ;
case stringify ( DATETIME_SHORT ) :
return "M/d/yyyy, h:mm a" ;
case stringify ( DATETIME_MED ) :
return "LLL d, yyyy, h:mm a" ;
case stringify ( DATETIME_FULL ) :
return "LLLL d, yyyy, h:mm a" ;
case stringify ( DATETIME_HUGE ) :
return dateTimeHuge ;
case stringify ( DATETIME_SHORT_WITH_SECONDS ) :
return "M/d/yyyy, h:mm:ss a" ;
case stringify ( DATETIME_MED_WITH_SECONDS ) :
return "LLL d, yyyy, h:mm:ss a" ;
case stringify ( DATETIME_MED_WITH_WEEKDAY ) :
return "EEE, d LLL yyyy, h:mm a" ;
case stringify ( DATETIME_FULL_WITH_SECONDS ) :
return "LLLL d, yyyy, h:mm:ss a" ;
case stringify ( DATETIME_HUGE_WITH_SECONDS ) :
return "EEEE, LLLL d, yyyy, h:mm:ss a" ;
2021-05-25 08:30:17 +03:00
default :
2021-07-01 15:56:08 +03:00
return dateTimeHuge ;
2021-05-25 08:30:17 +03:00
}
}
2021-07-01 15:56:08 +03:00
function stringifyTokens ( splits , tokenToString ) {
2022-06-15 17:36:57 +03:00
let s = "" ;
2021-07-01 15:56:08 +03:00
for ( const token of splits ) {
if ( token . literal ) {
2022-06-15 17:36:57 +03:00
s += token . val ;
2021-07-01 15:56:08 +03:00
} else {
2022-06-15 17:36:57 +03:00
s += tokenToString ( token . val ) ;
2021-07-01 15:56:08 +03:00
}
}
2022-06-15 17:36:57 +03:00
return s ;
2021-05-25 08:30:17 +03:00
}
const macroTokenToFormatOpts = {
D : DATE_SHORT ,
DD : DATE_MED ,
DDD : DATE_FULL ,
DDDD : DATE_HUGE ,
t : TIME_SIMPLE ,
tt : TIME_WITH_SECONDS ,
ttt : TIME_WITH_SHORT_OFFSET ,
tttt : TIME_WITH_LONG_OFFSET ,
T : TIME_24_SIMPLE ,
TT : TIME_24_WITH_SECONDS ,
TTT : TIME_24_WITH_SHORT_OFFSET ,
TTTT : TIME_24_WITH_LONG_OFFSET ,
f : DATETIME_SHORT ,
ff : DATETIME_MED ,
fff : DATETIME_FULL ,
ffff : DATETIME_HUGE ,
F : DATETIME_SHORT_WITH_SECONDS ,
FF : DATETIME_MED_WITH_SECONDS ,
FFF : DATETIME_FULL_WITH_SECONDS ,
FFFF : DATETIME_HUGE_WITH_SECONDS
} ;
class Formatter {
2022-01-02 23:54:58 +03:00
static create ( locale , opts = { } ) {
2021-12-02 10:28:09 +03:00
return new Formatter ( locale , opts ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static parseFormat ( fmt ) {
2021-05-25 08:30:17 +03:00
let current = null , currentFull = "" , bracketed = false ;
const splits = [ ] ;
2021-12-02 10:28:09 +03:00
for ( let i = 0 ; i < fmt . length ; i ++ ) {
const c = fmt . charAt ( i ) ;
2021-05-25 08:30:17 +03:00
if ( c === "'" ) {
if ( currentFull . length > 0 ) {
splits . push ( {
literal : bracketed ,
val : currentFull
} ) ;
}
current = null ;
currentFull = "" ;
bracketed = ! bracketed ;
} else if ( bracketed ) {
currentFull += c ;
} else if ( c === current ) {
currentFull += c ;
} else {
if ( currentFull . length > 0 ) {
splits . push ( {
literal : false ,
val : currentFull
} ) ;
}
currentFull = c ;
current = c ;
}
}
if ( currentFull . length > 0 ) {
splits . push ( {
literal : bracketed ,
val : currentFull
} ) ;
}
return splits ;
}
2021-12-02 10:28:09 +03:00
static macroTokenToFormatOpts ( token ) {
return macroTokenToFormatOpts [ token ] ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
constructor ( locale , formatOpts ) {
this . opts = formatOpts ;
this . loc = locale ;
2021-05-25 08:30:17 +03:00
this . systemLoc = null ;
}
2022-06-15 17:36:57 +03:00
formatWithSystemDefault ( dt , opts ) {
2021-05-25 08:30:17 +03:00
if ( this . systemLoc === null ) {
this . systemLoc = this . loc . redefaultToSystem ( ) ;
}
2022-06-15 17:36:57 +03:00
const df = this . systemLoc . dtFormatter ( dt , Object . assign ( { } , this . opts , opts ) ) ;
2021-05-25 08:30:17 +03:00
return df . format ( ) ;
}
2022-06-15 17:36:57 +03:00
formatDateTime ( dt , opts = { } ) {
const df = this . loc . dtFormatter ( dt , Object . assign ( { } , this . opts , opts ) ) ;
2021-05-25 08:30:17 +03:00
return df . format ( ) ;
}
2022-06-15 17:36:57 +03:00
formatDateTimeParts ( dt , opts = { } ) {
const df = this . loc . dtFormatter ( dt , Object . assign ( { } , this . opts , opts ) ) ;
2021-05-25 08:30:17 +03:00
return df . formatToParts ( ) ;
}
2022-06-15 17:36:57 +03:00
resolvedOptions ( dt , opts = { } ) {
const df = this . loc . dtFormatter ( dt , Object . assign ( { } , this . opts , opts ) ) ;
2021-05-25 08:30:17 +03:00
return df . resolvedOptions ( ) ;
}
2022-06-15 17:36:57 +03:00
num ( n , p = 0 ) {
2021-05-25 08:30:17 +03:00
if ( this . opts . forceSimple ) {
2022-06-15 17:36:57 +03:00
return padStart ( n , p ) ;
2021-05-25 08:30:17 +03:00
}
2022-01-02 23:54:58 +03:00
const opts = Object . assign ( { } , this . opts ) ;
2021-12-02 10:28:09 +03:00
if ( p > 0 ) {
opts . padTo = p ;
2021-05-25 08:30:17 +03:00
}
2022-06-15 17:36:57 +03:00
return this . loc . numberFormatter ( opts ) . format ( n ) ;
2021-05-25 08:30:17 +03:00
}
2022-06-15 17:36:57 +03:00
formatDateTimeFromString ( dt , fmt ) {
const knownEnglish = this . loc . listingMode ( ) === "en" , useDateTimeFormatter = this . loc . outputCalendar && this . loc . outputCalendar !== "gregory" && hasFormatToParts ( ) , string = ( opts , extract ) = > this . loc . extract ( dt , opts , extract ) , formatOffset = ( opts ) = > {
if ( dt . isOffsetFixed && dt . offset === 0 && opts . allowZ ) {
2021-05-25 08:30:17 +03:00
return "Z" ;
}
2022-06-15 17:36:57 +03:00
return dt . isValid ? dt . zone . formatOffset ( dt . ts , opts . format ) : "" ;
} , meridiem = ( ) = > knownEnglish ? meridiemForDateTime ( dt ) : string ( {
2021-05-25 08:30:17 +03:00
hour : "numeric" ,
hour12 : true
2022-06-15 17:36:57 +03:00
} , "dayperiod" ) , month = ( length , standalone ) = > knownEnglish ? monthForDateTime ( dt , length ) : string ( standalone ? {
2021-05-25 08:30:17 +03:00
month : length
} : {
month : length ,
day : "numeric"
2022-06-15 17:36:57 +03:00
} , "month" ) , weekday = ( length , standalone ) = > knownEnglish ? weekdayForDateTime ( dt , length ) : string ( standalone ? {
2021-05-25 08:30:17 +03:00
weekday : length
} : {
weekday : length ,
month : "long" ,
day : "numeric"
2022-05-19 10:12:37 +03:00
} , "weekday" ) , maybeMacro = ( token ) = > {
2021-09-01 16:11:55 +03:00
const formatOpts = Formatter . macroTokenToFormatOpts ( token ) ;
if ( formatOpts ) {
2022-06-15 17:36:57 +03:00
return this . formatWithSystemDefault ( dt , formatOpts ) ;
2021-02-26 12:21:42 +03:00
} else {
2021-05-25 08:30:17 +03:00
return token ;
}
2022-06-15 17:36:57 +03:00
} , era = ( length ) = > knownEnglish ? eraForDateTime ( dt , length ) : string ( {
2021-05-25 08:30:17 +03:00
era : length
2022-05-19 10:12:37 +03:00
} , "era" ) , tokenToString = ( token ) = > {
2021-05-25 08:30:17 +03:00
switch ( token ) {
case "S" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . millisecond ) ;
2021-05-25 08:30:17 +03:00
case "u" :
case "SSS" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . millisecond , 3 ) ;
2021-05-25 08:30:17 +03:00
case "s" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . second ) ;
2021-05-25 08:30:17 +03:00
case "ss" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . second , 2 ) ;
2021-05-25 08:30:17 +03:00
case "m" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . minute ) ;
2021-05-25 08:30:17 +03:00
case "mm" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . minute , 2 ) ;
2021-05-25 08:30:17 +03:00
case "h" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . hour % 12 === 0 ? 12 : dt.hour % 12 ) ;
2021-05-25 08:30:17 +03:00
case "hh" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . hour % 12 === 0 ? 12 : dt.hour % 12 , 2 ) ;
2021-05-25 08:30:17 +03:00
case "H" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . hour ) ;
2021-05-25 08:30:17 +03:00
case "HH" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . hour , 2 ) ;
2021-05-25 08:30:17 +03:00
case "Z" :
2022-06-15 17:36:57 +03:00
return formatOffset ( {
2021-05-25 08:30:17 +03:00
format : "narrow" ,
allowZ : this.opts.allowZ
} ) ;
case "ZZ" :
2022-06-15 17:36:57 +03:00
return formatOffset ( {
2021-05-25 08:30:17 +03:00
format : "short" ,
allowZ : this.opts.allowZ
} ) ;
case "ZZZ" :
2022-06-15 17:36:57 +03:00
return formatOffset ( {
2021-05-25 08:30:17 +03:00
format : "techie" ,
allowZ : this.opts.allowZ
} ) ;
case "ZZZZ" :
2022-06-15 17:36:57 +03:00
return dt . zone . offsetName ( dt . ts , {
2021-05-25 08:30:17 +03:00
format : "short" ,
locale : this.loc.locale
} ) ;
case "ZZZZZ" :
2022-06-15 17:36:57 +03:00
return dt . zone . offsetName ( dt . ts , {
2021-05-25 08:30:17 +03:00
format : "long" ,
locale : this.loc.locale
} ) ;
case "z" :
2022-06-15 17:36:57 +03:00
return dt . zoneName ;
2021-05-25 08:30:17 +03:00
case "a" :
return meridiem ( ) ;
case "d" :
return useDateTimeFormatter ? string ( {
day : "numeric"
2022-06-15 17:36:57 +03:00
} , "day" ) : this . num ( dt . day ) ;
2021-05-25 08:30:17 +03:00
case "dd" :
return useDateTimeFormatter ? string ( {
day : "2-digit"
2022-06-15 17:36:57 +03:00
} , "day" ) : this . num ( dt . day , 2 ) ;
2021-05-25 08:30:17 +03:00
case "c" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekday ) ;
2021-05-25 08:30:17 +03:00
case "ccc" :
return weekday ( "short" , true ) ;
case "cccc" :
return weekday ( "long" , true ) ;
case "ccccc" :
return weekday ( "narrow" , true ) ;
case "E" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekday ) ;
2021-05-25 08:30:17 +03:00
case "EEE" :
return weekday ( "short" , false ) ;
case "EEEE" :
return weekday ( "long" , false ) ;
case "EEEEE" :
return weekday ( "narrow" , false ) ;
case "L" :
return useDateTimeFormatter ? string ( {
month : "numeric" ,
day : "numeric"
2022-06-15 17:36:57 +03:00
} , "month" ) : this . num ( dt . month ) ;
2021-05-25 08:30:17 +03:00
case "LL" :
return useDateTimeFormatter ? string ( {
month : "2-digit" ,
day : "numeric"
2022-06-15 17:36:57 +03:00
} , "month" ) : this . num ( dt . month , 2 ) ;
2021-05-25 08:30:17 +03:00
case "LLL" :
return month ( "short" , true ) ;
case "LLLL" :
return month ( "long" , true ) ;
case "LLLLL" :
return month ( "narrow" , true ) ;
case "M" :
return useDateTimeFormatter ? string ( {
month : "numeric"
2022-06-15 17:36:57 +03:00
} , "month" ) : this . num ( dt . month ) ;
2021-05-25 08:30:17 +03:00
case "MM" :
return useDateTimeFormatter ? string ( {
month : "2-digit"
2022-06-15 17:36:57 +03:00
} , "month" ) : this . num ( dt . month , 2 ) ;
2021-05-25 08:30:17 +03:00
case "MMM" :
return month ( "short" , false ) ;
case "MMMM" :
return month ( "long" , false ) ;
case "MMMMM" :
return month ( "narrow" , false ) ;
case "y" :
return useDateTimeFormatter ? string ( {
year : "numeric"
2022-06-15 17:36:57 +03:00
} , "year" ) : this . num ( dt . year ) ;
2021-05-25 08:30:17 +03:00
case "yy" :
return useDateTimeFormatter ? string ( {
year : "2-digit"
2022-06-15 17:36:57 +03:00
} , "year" ) : this . num ( dt . year . toString ( ) . slice ( - 2 ) , 2 ) ;
2021-05-25 08:30:17 +03:00
case "yyyy" :
return useDateTimeFormatter ? string ( {
year : "numeric"
2022-06-15 17:36:57 +03:00
} , "year" ) : this . num ( dt . year , 4 ) ;
2021-05-25 08:30:17 +03:00
case "yyyyyy" :
return useDateTimeFormatter ? string ( {
year : "numeric"
2022-06-15 17:36:57 +03:00
} , "year" ) : this . num ( dt . year , 6 ) ;
2021-05-25 08:30:17 +03:00
case "G" :
return era ( "short" ) ;
case "GG" :
return era ( "long" ) ;
case "GGGGG" :
return era ( "narrow" ) ;
case "kk" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekYear . toString ( ) . slice ( - 2 ) , 2 ) ;
2021-05-25 08:30:17 +03:00
case "kkkk" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekYear , 4 ) ;
2021-05-25 08:30:17 +03:00
case "W" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekNumber ) ;
2021-05-25 08:30:17 +03:00
case "WW" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . weekNumber , 2 ) ;
2021-05-25 08:30:17 +03:00
case "o" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . ordinal ) ;
2021-05-25 08:30:17 +03:00
case "ooo" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . ordinal , 3 ) ;
2021-05-25 08:30:17 +03:00
case "q" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . quarter ) ;
2021-05-25 08:30:17 +03:00
case "qq" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . quarter , 2 ) ;
2021-05-25 08:30:17 +03:00
case "X" :
2022-06-15 17:36:57 +03:00
return this . num ( Math . floor ( dt . ts / 1000 ) ) ;
2021-05-25 08:30:17 +03:00
case "x" :
2022-06-15 17:36:57 +03:00
return this . num ( dt . ts ) ;
2021-05-25 08:30:17 +03:00
default :
return maybeMacro ( token ) ;
}
} ;
2021-12-02 10:28:09 +03:00
return stringifyTokens ( Formatter . parseFormat ( fmt ) , tokenToString ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
formatDurationFromString ( dur , fmt ) {
2021-05-25 08:30:17 +03:00
const tokenToField = ( token ) = > {
switch ( token [ 0 ] ) {
case "S" :
return "millisecond" ;
case "s" :
return "second" ;
case "m" :
return "minute" ;
case "h" :
return "hour" ;
case "d" :
return "day" ;
case "M" :
return "month" ;
case "y" :
return "year" ;
default :
return null ;
}
} , tokenToString = ( lildur ) = > ( token ) = > {
const mapped = tokenToField ( token ) ;
if ( mapped ) {
return this . num ( lildur . get ( mapped ) , token . length ) ;
} else {
return token ;
2021-02-26 12:21:42 +03:00
}
2023-06-07 08:08:35 +03:00
} , tokens = Formatter . parseFormat ( fmt ) , realTokens = tokens . reduce ( ( found , { literal , val } ) = > literal ? found : found.concat ( val ) , [ ] ) , collapsed = dur . shiftTo ( . . . realTokens . map ( tokenToField ) . filter ( ( t ) = > t ) ) ;
2021-05-25 08:30:17 +03:00
return stringifyTokens ( tokens , tokenToString ( collapsed ) ) ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
class Zone {
get type ( ) {
throw new ZoneIsAbstractError ( ) ;
}
get name() {
throw new ZoneIsAbstractError ( ) ;
}
get universal() {
throw new ZoneIsAbstractError ( ) ;
2021-03-02 11:33:03 +03:00
}
2021-12-02 10:28:09 +03:00
offsetName ( ts , opts ) {
2021-07-01 15:56:08 +03:00
throw new ZoneIsAbstractError ( ) ;
2021-03-02 11:33:03 +03:00
}
2021-12-02 10:28:09 +03:00
formatOffset ( ts , format ) {
2021-07-01 15:56:08 +03:00
throw new ZoneIsAbstractError ( ) ;
}
2021-12-02 10:28:09 +03:00
offset ( ts ) {
2021-07-01 15:56:08 +03:00
throw new ZoneIsAbstractError ( ) ;
}
equals ( otherZone ) {
throw new ZoneIsAbstractError ( ) ;
}
get isValid() {
throw new ZoneIsAbstractError ( ) ;
2021-03-02 11:33:03 +03:00
}
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
let singleton = null ;
class FixedOffsetZone extends Zone {
static get utcInstance() {
if ( singleton === null ) {
singleton = new FixedOffsetZone ( 0 ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return singleton ;
2021-03-02 11:33:03 +03:00
}
2022-06-15 17:36:57 +03:00
static instance ( offset ) {
return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone ( offset ) ;
2021-03-02 11:33:03 +03:00
}
2022-06-15 17:36:57 +03:00
static parseSpecifier ( s ) {
if ( s ) {
const r = s . match ( /^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i ) ;
2021-07-01 15:56:08 +03:00
if ( r ) {
return new FixedOffsetZone ( signedOffset ( r [ 1 ] , r [ 2 ] ) ) ;
}
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return null ;
}
2022-06-15 17:36:57 +03:00
constructor ( offset ) {
2021-07-01 15:56:08 +03:00
super ( ) ;
2022-06-15 17:36:57 +03:00
this . fixed = offset ;
2021-07-01 15:56:08 +03:00
}
get type ( ) {
return "fixed" ;
}
get name() {
2021-12-03 19:55:27 +03:00
return this . fixed === 0 ? "UTC" : ` UTC ${ formatOffset ( this . fixed , "narrow" ) } ` ;
2021-07-01 15:56:08 +03:00
}
offsetName() {
return this . name ;
}
2021-12-02 10:28:09 +03:00
formatOffset ( ts , format ) {
2021-12-03 19:55:27 +03:00
return formatOffset ( this . fixed , format ) ;
2021-07-01 15:56:08 +03:00
}
get universal() {
return true ;
}
offset() {
return this . fixed ;
}
2021-12-02 10:28:09 +03:00
equals ( otherZone ) {
return otherZone . type === "fixed" && otherZone . fixed === this . fixed ;
2021-07-01 15:56:08 +03:00
}
get isValid() {
return true ;
}
}
const matchingRegex = RegExp ( ` ^ ${ ianaRegex . source } $ ` ) ;
2022-01-02 23:54:58 +03:00
let dtfCache = { } ;
2021-07-01 15:56:08 +03:00
function makeDTF ( zone ) {
if ( ! dtfCache [ zone ] ) {
dtfCache [ zone ] = new Intl . DateTimeFormat ( "en-US" , {
hour12 : false ,
timeZone : zone ,
year : "numeric" ,
month : "2-digit" ,
day : "2-digit" ,
hour : "2-digit" ,
minute : "2-digit" ,
second : "2-digit"
2021-05-25 08:30:17 +03:00
} ) ;
2021-03-02 11:33:03 +03:00
}
2021-07-01 15:56:08 +03:00
return dtfCache [ zone ] ;
}
const typeToPos = {
year : 0 ,
month : 1 ,
day : 2 ,
hour : 3 ,
minute : 4 ,
second : 5
} ;
2022-06-15 17:36:57 +03:00
function hackyOffset ( dtf , date ) {
const formatted = dtf . format ( date ) . replace ( /\u200E/g , "" ) , parsed = /(\d+)\/(\d+)\/(\d+),? (\d+):(\d+):(\d+)/ . exec ( formatted ) , [ , fMonth , fDay , fYear , fHour , fMinute , fSecond ] = parsed ;
2021-07-01 15:56:08 +03:00
return [
fYear ,
fMonth ,
fDay ,
fHour ,
fMinute ,
fSecond
] ;
}
2022-06-15 17:36:57 +03:00
function partsOffset ( dtf , date ) {
const formatted = dtf . formatToParts ( date ) , filled = [ ] ;
2021-07-01 15:56:08 +03:00
for ( let i = 0 ; i < formatted . length ; i ++ ) {
2023-06-07 08:08:35 +03:00
const { type , value } = formatted [ i ] , pos = typeToPos [ type ] ;
2021-07-01 15:56:08 +03:00
if ( ! isUndefined ( pos ) ) {
filled [ pos ] = parseInt ( value , 10 ) ;
}
}
return filled ;
}
2022-01-02 23:54:58 +03:00
let ianaZoneCache = { } ;
2021-07-01 15:56:08 +03:00
class IANAZone extends Zone {
static create ( name ) {
if ( ! ianaZoneCache [ name ] ) {
ianaZoneCache [ name ] = new IANAZone ( name ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return ianaZoneCache [ name ] ;
2021-03-02 11:33:03 +03:00
}
2021-07-01 15:56:08 +03:00
static resetCache() {
2022-01-02 23:54:58 +03:00
ianaZoneCache = { } ;
dtfCache = { } ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
static isValidSpecifier ( s ) {
return ! ! ( s && s . match ( matchingRegex ) ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
static isValidZone ( zone ) {
2021-07-01 15:56:08 +03:00
try {
new Intl . DateTimeFormat ( "en-US" , {
2021-12-02 10:28:09 +03:00
timeZone : zone
2021-07-01 15:56:08 +03:00
} ) . format ( ) ;
return true ;
} catch ( e ) {
return false ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
}
static parseGMTOffset ( specifier ) {
if ( specifier ) {
2022-06-15 17:36:57 +03:00
const match = specifier . match ( /^Etc\/GMT([+-]\d{1,2})$/i ) ;
if ( match ) {
return - 60 * parseInt ( match [ 1 ] ) ;
2021-07-01 15:56:08 +03:00
}
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return null ;
2021-03-02 11:33:03 +03:00
}
2021-12-02 10:28:09 +03:00
constructor ( name ) {
2021-07-01 15:56:08 +03:00
super ( ) ;
2021-12-02 10:28:09 +03:00
this . zoneName = name ;
this . valid = IANAZone . isValidZone ( name ) ;
2021-03-02 11:33:03 +03:00
}
2021-07-01 15:56:08 +03:00
get type ( ) {
return "iana" ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
get name() {
return this . zoneName ;
2021-03-02 11:33:03 +03:00
}
2021-07-01 15:56:08 +03:00
get universal() {
return false ;
2021-03-02 11:33:03 +03:00
}
2023-06-07 08:08:35 +03:00
offsetName ( ts , { format , locale } ) {
2021-12-02 10:28:09 +03:00
return parseZoneInfo ( ts , format , locale , this . name ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
formatOffset ( ts , format ) {
2021-12-03 19:55:27 +03:00
return formatOffset ( this . offset ( ts ) , format ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
offset ( ts ) {
2022-06-15 17:36:57 +03:00
const date = new Date ( ts ) , dtf = makeDTF ( this . name ) , [ year , month , day , hour , minute , second ] = dtf . formatToParts ? partsOffset ( dtf , date ) : hackyOffset ( dtf , date ) , adjustedHour = hour === 24 ? 0 : hour ;
2021-07-01 15:56:08 +03:00
const asUTC = objToLocalTS ( {
year ,
month ,
day ,
hour : adjustedHour ,
minute ,
second ,
millisecond : 0
2021-05-25 08:30:17 +03:00
} ) ;
2022-06-15 17:36:57 +03:00
let asTS = + date ;
2021-07-01 15:56:08 +03:00
const over = asTS % 1000 ;
asTS -= over >= 0 ? over : 1000 + over ;
return ( asUTC - asTS ) / ( 60 * 1000 ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
equals ( otherZone ) {
return otherZone . type === "iana" && otherZone . name === this . name ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
get isValid() {
return this . valid ;
}
}
const numberingSystems = {
arab : "[\u0660-\u0669]" ,
arabext : "[\u06F0-\u06F9]" ,
bali : "[\u1B50-\u1B59]" ,
beng : "[\u09E6-\u09EF]" ,
deva : "[\u0966-\u096F]" ,
fullwide : "[\uFF10-\uFF19]" ,
gujr : "[\u0AE6-\u0AEF]" ,
hanidec : "[〇 |一|二|三|四|五|六|七|八|九]" ,
khmr : "[\u17E0-\u17E9]" ,
knda : "[\u0CE6-\u0CEF]" ,
laoo : "[\u0ED0-\u0ED9]" ,
limb : "[\u1946-\u194F]" ,
mlym : "[\u0D66-\u0D6F]" ,
mong : "[\u1810-\u1819]" ,
mymr : "[\u1040-\u1049]" ,
orya : "[\u0B66-\u0B6F]" ,
tamldec : "[\u0BE6-\u0BEF]" ,
telu : "[\u0C66-\u0C6F]" ,
thai : "[\u0E50-\u0E59]" ,
tibt : "[\u0F20-\u0F29]" ,
latn : "\\d"
} ;
const numberingSystemsUTF16 = {
arab : [
1632 ,
1641
] ,
arabext : [
1776 ,
1785
] ,
bali : [
6992 ,
7001
] ,
beng : [
2534 ,
2543
] ,
deva : [
2406 ,
2415
] ,
fullwide : [
65296 ,
65303
] ,
gujr : [
2790 ,
2799
] ,
khmr : [
6112 ,
6121
] ,
knda : [
3302 ,
3311
] ,
laoo : [
3792 ,
3801
] ,
limb : [
6470 ,
6479
] ,
mlym : [
3430 ,
3439
] ,
mong : [
6160 ,
6169
] ,
mymr : [
4160 ,
4169
] ,
orya : [
2918 ,
2927
] ,
tamldec : [
3046 ,
3055
] ,
telu : [
3174 ,
3183
] ,
thai : [
3664 ,
3673
] ,
tibt : [
3872 ,
3881
]
} ;
const hanidecChars = numberingSystems . hanidec . replace ( /[\[|\]]/g , "" ) . split ( "" ) ;
function parseDigits ( str ) {
let value = parseInt ( str , 10 ) ;
if ( isNaN ( value ) ) {
value = "" ;
for ( let i = 0 ; i < str . length ; i ++ ) {
const code = str . charCodeAt ( i ) ;
if ( str [ i ] . search ( numberingSystems . hanidec ) !== - 1 ) {
value += hanidecChars . indexOf ( str [ i ] ) ;
} else {
for ( const key in numberingSystemsUTF16 ) {
const [ min , max ] = numberingSystemsUTF16 [ key ] ;
if ( code >= min && code <= max ) {
value += code - min ;
}
}
}
}
return parseInt ( value , 10 ) ;
} else {
return value ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
2023-06-07 08:08:35 +03:00
function digitRegex ( { numberingSystem } , append = "" ) {
2021-07-01 15:56:08 +03:00
return new RegExp ( ` ${ numberingSystems [ numberingSystem || "latn" ] } ${ append } ` ) ;
}
class InvalidZone extends Zone {
constructor ( zoneName ) {
super ( ) ;
this . zoneName = zoneName ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
get type ( ) {
return "invalid" ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
get name() {
return this . zoneName ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
get universal() {
return false ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
offsetName() {
return null ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
formatOffset() {
return "" ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
offset() {
return NaN ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
equals() {
return false ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
get isValid() {
return false ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
function normalizeZone ( input , defaultZone ) {
let offset ;
2021-07-01 15:56:08 +03:00
if ( isUndefined ( input ) || input === null ) {
2022-06-15 17:36:57 +03:00
return defaultZone ;
2021-07-01 15:56:08 +03:00
} else if ( input instanceof Zone ) {
return input ;
} else if ( isString ( input ) ) {
const lowered = input . toLowerCase ( ) ;
2022-06-15 17:36:57 +03:00
if ( lowered === "local" ) return defaultZone ;
2021-07-01 15:56:08 +03:00
else if ( lowered === "utc" || lowered === "gmt" ) return FixedOffsetZone . utcInstance ;
2022-06-15 17:36:57 +03:00
else if ( ( offset = IANAZone . parseGMTOffset ( input ) ) != null ) {
return FixedOffsetZone . instance ( offset ) ;
2021-07-01 15:56:08 +03:00
} else if ( IANAZone . isValidSpecifier ( lowered ) ) return IANAZone . create ( input ) ;
else return FixedOffsetZone . parseSpecifier ( lowered ) || new InvalidZone ( input ) ;
} else if ( isNumber ( input ) ) {
return FixedOffsetZone . instance ( input ) ;
} else if ( typeof input === "object" && input . offset && typeof input . offset === "number" ) {
return input ;
} else {
return new InvalidZone ( input ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
}
class Invalid {
2021-12-02 10:28:09 +03:00
constructor ( reason , explanation ) {
this . reason = reason ;
2021-09-01 16:11:55 +03:00
this . explanation = explanation ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
toMessage() {
if ( this . explanation ) {
return ` ${ this . reason } : ${ this . explanation } ` ;
} else {
return this . reason ;
2021-05-25 08:30:17 +03:00
}
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
let singleton1 = null ;
class LocalZone extends Zone {
static get instance() {
if ( singleton1 === null ) {
singleton1 = new LocalZone ( ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return singleton1 ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
get type ( ) {
return "local" ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
get name() {
if ( hasIntl ( ) ) {
return new Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ;
} else return "local" ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
get universal() {
return false ;
2021-05-25 08:30:17 +03:00
}
2023-06-07 08:08:35 +03:00
offsetName ( ts , { format , locale } ) {
2021-12-02 10:28:09 +03:00
return parseZoneInfo ( ts , format , locale ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
formatOffset ( ts , format ) {
2021-12-03 19:55:27 +03:00
return formatOffset ( this . offset ( ts ) , format ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
offset ( ts ) {
return - new Date ( ts ) . getTimezoneOffset ( ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
equals ( otherZone ) {
return otherZone . type === "local" ;
2021-05-25 08:30:17 +03:00
}
get isValid() {
2021-07-01 15:56:08 +03:00
return true ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
}
function combineRegexes ( . . . regexes ) {
2022-05-19 10:12:37 +03:00
const full = regexes . reduce ( ( f , r ) = > f + r . source , "" ) ;
2021-07-01 15:56:08 +03:00
return RegExp ( ` ^ ${ full } $ ` ) ;
}
function combineExtractors ( . . . extractors ) {
return ( m ) = > extractors . reduce ( ( [ mergedVals , mergedZone , cursor ] , ex ) = > {
const [ val , zone , next ] = ex ( m , cursor ) ;
return [
Object . assign ( mergedVals , val ) ,
mergedZone || zone ,
next
] ;
} , [
2022-01-02 23:54:58 +03:00
{ } ,
2021-07-01 15:56:08 +03:00
null ,
1
2022-05-19 10:12:37 +03:00
] ) . slice ( 0 , 2 ) ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
function parse ( s , . . . patterns ) {
if ( s == null ) {
2021-07-01 15:56:08 +03:00
return [
null ,
null
] ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
for ( const [ regex , extractor ] of patterns ) {
2022-06-15 17:36:57 +03:00
const m = regex . exec ( s ) ;
2021-07-01 15:56:08 +03:00
if ( m ) {
return extractor ( m ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
}
return [
null ,
null
] ;
}
function simpleParse ( . . . keys ) {
2022-06-15 17:36:57 +03:00
return ( match , cursor ) = > {
2022-01-02 23:54:58 +03:00
const ret = { } ;
2021-07-01 15:56:08 +03:00
let i ;
for ( i = 0 ; i < keys . length ; i ++ ) {
2022-06-15 17:36:57 +03:00
ret [ keys [ i ] ] = parseInteger ( match [ cursor + i ] ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
return [
ret ,
null ,
cursor + i
] ;
} ;
}
const offsetRegex = /(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/ , isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/ , isoTimeRegex = RegExp ( ` ${ isoTimeBaseRegex . source } ${ offsetRegex . source } ? ` ) , isoTimeExtensionRegex = RegExp ( ` (?:T ${ isoTimeRegex . source } )? ` ) , isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/ , isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/ , isoOrdinalRegex = /(\d{4})-?(\d{3})/ , extractISOWeekData = simpleParse ( "weekYear" , "weekNumber" , "weekDay" ) , extractISOOrdinalData = simpleParse ( "year" , "ordinal" ) , sqlYmdRegex = /(\d{4})-(\d\d)-(\d\d)/ , sqlTimeRegex = RegExp ( ` ${ isoTimeBaseRegex . source } ?(?: ${ offsetRegex . source } |( ${ ianaRegex . source } ))? ` ) , sqlTimeExtensionRegex = RegExp ( ` (?: ${ sqlTimeRegex . source } )? ` ) ;
2022-06-15 17:36:57 +03:00
function __int ( match , pos , fallback ) {
const m = match [ pos ] ;
2021-07-01 15:56:08 +03:00
return isUndefined ( m ) ? fallback : parseInteger ( m ) ;
}
2022-06-15 17:36:57 +03:00
function extractISOYmd ( match , cursor ) {
2021-07-01 15:56:08 +03:00
const item = {
2022-06-15 17:36:57 +03:00
year : __int ( match , cursor ) ,
month : __int ( match , cursor + 1 , 1 ) ,
day : __int ( match , cursor + 2 , 1 )
2021-07-01 15:56:08 +03:00
} ;
return [
item ,
null ,
cursor + 3
] ;
}
2022-06-15 17:36:57 +03:00
function extractISOTime ( match , cursor ) {
2021-07-01 15:56:08 +03:00
const item = {
2022-06-15 17:36:57 +03:00
hour : __int ( match , cursor , 0 ) ,
minute : __int ( match , cursor + 1 , 0 ) ,
second : __int ( match , cursor + 2 , 0 ) ,
millisecond : parseMillis ( match [ cursor + 3 ] )
2021-07-01 15:56:08 +03:00
} ;
return [
item ,
null ,
cursor + 4
] ;
}
2022-06-15 17:36:57 +03:00
function extractISOOffset ( match , cursor ) {
const local = ! match [ cursor ] && ! match [ cursor + 1 ] , fullOffset = signedOffset ( match [ cursor + 1 ] , match [ cursor + 2 ] ) , zone = local ? null : FixedOffsetZone . instance ( fullOffset ) ;
2021-07-01 15:56:08 +03:00
return [
2022-01-02 23:54:58 +03:00
{ } ,
2021-07-01 15:56:08 +03:00
zone ,
cursor + 3
] ;
}
2022-06-15 17:36:57 +03:00
function extractIANAZone ( match , cursor ) {
const zone = match [ cursor ] ? IANAZone . create ( match [ cursor ] ) : null ;
2021-07-01 15:56:08 +03:00
return [
2022-01-02 23:54:58 +03:00
{ } ,
2021-07-01 15:56:08 +03:00
zone ,
cursor + 1
] ;
}
const isoDuration = /^-?P(?:(?:(-?\d{1,9})Y)?(?:(-?\d{1,9})M)?(?:(-?\d{1,9})W)?(?:(-?\d{1,9})D)?(?:T(?:(-?\d{1,9})H)?(?:(-?\d{1,9})M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,9}))?S)?)?)$/ ;
2022-06-15 17:36:57 +03:00
function extractISODuration ( match ) {
const [ s , yearStr , monthStr , weekStr , dayStr , hourStr , minuteStr , secondStr , millisecondsStr ] = match ;
const hasNegativePrefix = s [ 0 ] === "-" ;
2022-05-19 10:12:37 +03:00
const maybeNegate = ( num ) = > num && hasNegativePrefix ? - num : num ;
2021-07-01 15:56:08 +03:00
return [
{
years : maybeNegate ( parseInteger ( yearStr ) ) ,
months : maybeNegate ( parseInteger ( monthStr ) ) ,
weeks : maybeNegate ( parseInteger ( weekStr ) ) ,
days : maybeNegate ( parseInteger ( dayStr ) ) ,
hours : maybeNegate ( parseInteger ( hourStr ) ) ,
minutes : maybeNegate ( parseInteger ( minuteStr ) ) ,
seconds : maybeNegate ( parseInteger ( secondStr ) ) ,
milliseconds : maybeNegate ( parseMillis ( millisecondsStr ) )
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
] ;
}
const obsOffsets = {
GMT : 0 ,
EDT : - 4 * 60 ,
EST : - 5 * 60 ,
CDT : - 5 * 60 ,
CST : - 6 * 60 ,
MDT : - 6 * 60 ,
MST : - 7 * 60 ,
PDT : - 7 * 60 ,
PST : - 8 * 60
} ;
function fromStrings ( weekdayStr , yearStr , monthStr , dayStr , hourStr , minuteStr , secondStr ) {
const result = {
year : yearStr.length === 2 ? untruncateYear ( parseInteger ( yearStr ) ) : parseInteger ( yearStr ) ,
month : monthsShort.indexOf ( monthStr ) + 1 ,
day : parseInteger ( dayStr ) ,
hour : parseInteger ( hourStr ) ,
minute : parseInteger ( minuteStr )
} ;
if ( secondStr ) result . second = parseInteger ( secondStr ) ;
if ( weekdayStr ) {
result . weekday = weekdayStr . length > 3 ? weekdaysLong . indexOf ( weekdayStr ) + 1 : weekdaysShort.indexOf ( weekdayStr ) + 1 ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
return result ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
const rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/ ;
2022-06-15 17:36:57 +03:00
function extractRFC2822 ( match ) {
const [ , weekdayStr , dayStr , monthStr , yearStr , hourStr , minuteStr , secondStr , obsOffset , milOffset , offHourStr , offMinuteStr ] = match , result = fromStrings ( weekdayStr , yearStr , monthStr , dayStr , hourStr , minuteStr , secondStr ) ;
let offset ;
2021-07-01 15:56:08 +03:00
if ( obsOffset ) {
2022-06-15 17:36:57 +03:00
offset = obsOffsets [ obsOffset ] ;
2021-07-01 15:56:08 +03:00
} else if ( milOffset ) {
2022-06-15 17:36:57 +03:00
offset = 0 ;
2021-07-01 15:56:08 +03:00
} else {
2022-06-15 17:36:57 +03:00
offset = signedOffset ( offHourStr , offMinuteStr ) ;
2021-07-01 15:56:08 +03:00
}
return [
result ,
2022-06-15 17:36:57 +03:00
new FixedOffsetZone ( offset )
2021-07-01 15:56:08 +03:00
] ;
}
2022-06-15 17:36:57 +03:00
function preprocessRFC2822 ( s ) {
return s . replace ( /\([^)]*\)|[\n\t]/g , " " ) . replace ( /(\s\s+)/g , " " ) . trim ( ) ;
2021-07-01 15:56:08 +03:00
}
const rfc1123 = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/ , rfc850 = /^(Monday|Tuesday|Wedsday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/ , ascii = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/ ;
2022-06-15 17:36:57 +03:00
function extractRFC1123Or850 ( match ) {
const [ , weekdayStr , dayStr , monthStr , yearStr , hourStr , minuteStr , secondStr ] = match , result = fromStrings ( weekdayStr , yearStr , monthStr , dayStr , hourStr , minuteStr , secondStr ) ;
2021-07-01 15:56:08 +03:00
return [
result ,
FixedOffsetZone . utcInstance
] ;
}
2022-06-15 17:36:57 +03:00
function extractASCII ( match ) {
const [ , weekdayStr , monthStr , dayStr , hourStr , minuteStr , secondStr , yearStr ] = match , result = fromStrings ( weekdayStr , yearStr , monthStr , dayStr , hourStr , minuteStr , secondStr ) ;
2021-07-01 15:56:08 +03:00
return [
result ,
FixedOffsetZone . utcInstance
] ;
}
const isoYmdWithTimeExtensionRegex = combineRegexes ( isoYmdRegex , isoTimeExtensionRegex ) ;
const isoWeekWithTimeExtensionRegex = combineRegexes ( isoWeekRegex , isoTimeExtensionRegex ) ;
const isoOrdinalWithTimeExtensionRegex = combineRegexes ( isoOrdinalRegex , isoTimeExtensionRegex ) ;
const isoTimeCombinedRegex = combineRegexes ( isoTimeRegex ) ;
const extractISOYmdTimeAndOffset = combineExtractors ( extractISOYmd , extractISOTime , extractISOOffset ) ;
const extractISOWeekTimeAndOffset = combineExtractors ( extractISOWeekData , extractISOTime , extractISOOffset ) ;
const extractISOOrdinalDataAndTime = combineExtractors ( extractISOOrdinalData , extractISOTime ) ;
const extractISOTimeAndOffset = combineExtractors ( extractISOTime , extractISOOffset ) ;
2022-06-15 17:36:57 +03:00
function parseISODate ( s ) {
return parse ( s , [
2021-07-01 15:56:08 +03:00
isoYmdWithTimeExtensionRegex ,
extractISOYmdTimeAndOffset
] , [
isoWeekWithTimeExtensionRegex ,
extractISOWeekTimeAndOffset
] , [
isoOrdinalWithTimeExtensionRegex ,
extractISOOrdinalDataAndTime
] , [
isoTimeCombinedRegex ,
extractISOTimeAndOffset
] ) ;
}
2022-06-15 17:36:57 +03:00
function parseRFC2822Date ( s ) {
return parse ( preprocessRFC2822 ( s ) , [
2021-07-01 15:56:08 +03:00
rfc2822 ,
extractRFC2822
] ) ;
}
2022-06-15 17:36:57 +03:00
function parseHTTPDate ( s ) {
return parse ( s , [
2021-07-01 15:56:08 +03:00
rfc1123 ,
extractRFC1123Or850
] , [
rfc850 ,
extractRFC1123Or850
] , [
ascii ,
extractASCII
] ) ;
}
2022-06-15 17:36:57 +03:00
function parseISODuration ( s ) {
return parse ( s , [
2021-07-01 15:56:08 +03:00
isoDuration ,
extractISODuration
] ) ;
}
const sqlYmdWithTimeExtensionRegex = combineRegexes ( sqlYmdRegex , sqlTimeExtensionRegex ) ;
const sqlTimeCombinedRegex = combineRegexes ( sqlTimeRegex ) ;
const extractISOYmdTimeOffsetAndIANAZone = combineExtractors ( extractISOYmd , extractISOTime , extractISOOffset , extractIANAZone ) ;
const extractISOTimeOffsetAndIANAZone = combineExtractors ( extractISOTime , extractISOOffset , extractIANAZone ) ;
2022-06-15 17:36:57 +03:00
function parseSQL ( s ) {
return parse ( s , [
2021-07-01 15:56:08 +03:00
sqlYmdWithTimeExtensionRegex ,
extractISOYmdTimeOffsetAndIANAZone
] , [
sqlTimeCombinedRegex ,
extractISOTimeOffsetAndIANAZone
] ) ;
2021-02-22 11:27:40 +03:00
}
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
] ;
2021-09-01 16:11:55 +03:00
function unitOutOfRange ( unit , value ) {
return new Invalid ( "unit out of range" , ` you specified ${ value } (of type ${ typeof value } ) as a ${ unit } , which is invalid ` ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
function dayOfWeek ( year , month , day ) {
const js = new Date ( Date . UTC ( year , month - 1 , day ) ) . getUTCDay ( ) ;
return js === 0 ? 7 : js ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
function computeOrdinal ( year , month , day ) {
return day + ( isLeapYear ( year ) ? leapLadder : nonLeapLadder ) [ month - 1 ] ;
}
function uncomputeOrdinal ( year , ordinal ) {
2022-05-19 10:12:37 +03:00
const table = isLeapYear ( year ) ? leapLadder : nonLeapLadder , month0 = table . findIndex ( ( i ) = > i < ordinal ) , day = ordinal - table [ month0 ] ;
2021-07-01 15:56:08 +03:00
return {
month : month0 + 1 ,
day
} ;
}
function gregorianToWeek ( gregObj ) {
2023-06-07 08:08:35 +03:00
const { year , month , day } = gregObj , ordinal = computeOrdinal ( year , month , day ) , weekday = dayOfWeek ( year , month , day ) ;
2021-07-01 15:56:08 +03:00
let weekNumber = Math . floor ( ( ordinal - weekday + 10 ) / 7 ) , weekYear ;
if ( weekNumber < 1 ) {
weekYear = year - 1 ;
weekNumber = weeksInWeekYear ( weekYear ) ;
} else if ( weekNumber > weeksInWeekYear ( year ) ) {
weekYear = year + 1 ;
weekNumber = 1 ;
2021-02-22 11:27:40 +03:00
} else {
2021-07-01 15:56:08 +03:00
weekYear = year ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
return Object . assign ( {
weekYear ,
weekNumber ,
weekday
} , timeObject ( gregObj ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
function weekToGregorian ( weekData ) {
2023-06-07 08:08:35 +03:00
const { weekYear , weekNumber , weekday } = weekData , weekdayOfJan4 = dayOfWeek ( weekYear , 1 , 4 ) , yearInDays = daysInYear ( weekYear ) ;
2021-07-01 15:56:08 +03:00
let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3 , year ;
if ( ordinal < 1 ) {
year = weekYear - 1 ;
ordinal += daysInYear ( year ) ;
} else if ( ordinal > yearInDays ) {
year = weekYear + 1 ;
ordinal -= daysInYear ( weekYear ) ;
2021-05-25 08:30:17 +03:00
} else {
2021-07-01 15:56:08 +03:00
year = weekYear ;
2021-03-02 11:33:03 +03:00
}
2023-06-07 08:08:35 +03:00
const { month , day } = uncomputeOrdinal ( year , ordinal ) ;
2021-07-01 15:56:08 +03:00
return Object . assign ( {
year ,
month ,
day
} , timeObject ( weekData ) ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
function gregorianToOrdinal ( gregData ) {
2023-06-07 08:08:35 +03:00
const { year , month , day } = gregData , ordinal = computeOrdinal ( year , month , day ) ;
2021-07-01 15:56:08 +03:00
return Object . assign ( {
year ,
ordinal
} , timeObject ( gregData ) ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
function ordinalToGregorian ( ordinalData ) {
2023-06-07 08:08:35 +03:00
const { year , ordinal } = ordinalData , { month , day } = uncomputeOrdinal ( year , ordinal ) ;
2021-07-01 15:56:08 +03:00
return Object . assign ( {
year ,
month ,
day
} , timeObject ( ordinalData ) ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
function hasInvalidWeekData ( obj ) {
const validYear = isInteger ( obj . weekYear ) , validWeek = integerBetween ( obj . weekNumber , 1 , weeksInWeekYear ( obj . weekYear ) ) , validWeekday = integerBetween ( obj . weekday , 1 , 7 ) ;
if ( ! validYear ) {
return unitOutOfRange ( "weekYear" , obj . weekYear ) ;
} else if ( ! validWeek ) {
return unitOutOfRange ( "week" , obj . week ) ;
} else if ( ! validWeekday ) {
return unitOutOfRange ( "weekday" , obj . weekday ) ;
} else return false ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
function hasInvalidOrdinalData ( obj ) {
const validYear = isInteger ( obj . year ) , validOrdinal = integerBetween ( obj . ordinal , 1 , daysInYear ( obj . year ) ) ;
if ( ! validYear ) {
return unitOutOfRange ( "year" , obj . year ) ;
} else if ( ! validOrdinal ) {
return unitOutOfRange ( "ordinal" , obj . ordinal ) ;
} else return false ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
function hasInvalidGregorianData ( obj ) {
const validYear = isInteger ( obj . year ) , validMonth = integerBetween ( obj . month , 1 , 12 ) , validDay = integerBetween ( obj . day , 1 , daysInMonth ( obj . year , obj . month ) ) ;
if ( ! validYear ) {
return unitOutOfRange ( "year" , obj . year ) ;
} else if ( ! validMonth ) {
return unitOutOfRange ( "month" , obj . month ) ;
} else if ( ! validDay ) {
return unitOutOfRange ( "day" , obj . day ) ;
} else return false ;
}
function hasInvalidTimeData ( obj ) {
2023-06-07 08:08:35 +03:00
const { hour , minute , second , millisecond } = obj ;
2021-07-01 15:56:08 +03:00
const validHour = integerBetween ( hour , 0 , 23 ) || hour === 24 && minute === 0 && second === 0 && millisecond === 0 , validMinute = integerBetween ( minute , 0 , 59 ) , validSecond = integerBetween ( second , 0 , 59 ) , validMillisecond = integerBetween ( millisecond , 0 , 999 ) ;
if ( ! validHour ) {
return unitOutOfRange ( "hour" , hour ) ;
} else if ( ! validMinute ) {
return unitOutOfRange ( "minute" , minute ) ;
} else if ( ! validSecond ) {
return unitOutOfRange ( "second" , second ) ;
} else if ( ! validMillisecond ) {
return unitOutOfRange ( "millisecond" , millisecond ) ;
} else return false ;
}
const INVALID = "Invalid DateTime" ;
const INVALID1 = "Invalid Duration" ;
2022-01-02 23:54:58 +03:00
let intlDTCache = { } ;
2022-05-19 10:12:37 +03:00
let now = ( ) = > Date . now ( ) , defaultZone = null , defaultLocale = null , defaultNumberingSystem = null , defaultOutputCalendar = null , throwOnInvalid = false ;
2021-07-01 15:56:08 +03:00
const INVALID2 = "Invalid Interval" ;
class Info {
2021-12-02 10:28:09 +03:00
static hasDST ( zone = Settings . defaultZone ) {
const proto = DateTime . local ( ) . setZone ( zone ) . set ( {
2021-07-01 15:56:08 +03:00
month : 12
} ) ;
2021-12-02 10:28:09 +03:00
return ! zone . universal && proto . offset !== proto . set ( {
2021-07-01 15:56:08 +03:00
month : 6
} ) . offset ;
2021-03-02 11:33:03 +03:00
}
2021-12-02 10:28:09 +03:00
static isValidIANAZone ( zone ) {
return IANAZone . isValidSpecifier ( zone ) && IANAZone . isValidZone ( zone ) ;
2021-03-02 11:33:03 +03:00
}
2021-12-02 10:28:09 +03:00
static normalizeZone ( input ) {
return normalizeZone ( input , Settings . defaultZone ) ;
2021-03-02 11:33:03 +03:00
}
2023-06-07 08:08:35 +03:00
static months ( length = "long" , { locale = null , numberingSystem = null , outputCalendar = "gregory" } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , numberingSystem , outputCalendar ) . months ( length ) ;
2021-03-02 11:33:03 +03:00
}
2023-06-07 08:08:35 +03:00
static monthsFormat ( length = "long" , { locale = null , numberingSystem = null , outputCalendar = "gregory" } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , numberingSystem , outputCalendar ) . months ( length , true ) ;
2021-07-01 15:56:08 +03:00
}
2023-06-07 08:08:35 +03:00
static weekdays ( length = "long" , { locale = null , numberingSystem = null } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , numberingSystem , null ) . weekdays ( length ) ;
2021-07-01 15:56:08 +03:00
}
2023-06-07 08:08:35 +03:00
static weekdaysFormat ( length = "long" , { locale = null , numberingSystem = null } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , numberingSystem , null ) . weekdays ( length , true ) ;
2021-07-01 15:56:08 +03:00
}
2023-06-07 08:08:35 +03:00
static meridiems ( { locale = null } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale ) . meridiems ( ) ;
2021-07-01 15:56:08 +03:00
}
2023-06-07 08:08:35 +03:00
static eras ( length = "short" , { locale = null } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , null , "gregory" ) . eras ( length ) ;
2021-07-01 15:56:08 +03:00
}
static features() {
let intl = false , intlTokens = false , zones = false , relative = false ;
if ( hasIntl ( ) ) {
intl = true ;
intlTokens = hasFormatToParts ( ) ;
relative = hasRelative ( ) ;
try {
zones = new Intl . DateTimeFormat ( "en" , {
timeZone : "America/New_York"
} ) . resolvedOptions ( ) . timeZone === "America/New_York" ;
} catch ( e ) {
zones = false ;
2021-05-25 08:30:17 +03:00
}
2021-03-02 11:33:03 +03:00
}
2021-07-01 15:56:08 +03:00
return {
intl ,
intlTokens ,
zones ,
relative
} ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
}
const lowOrderMatrix = {
weeks : {
days : 7 ,
hours : 7 * 24 ,
minutes : 7 * 24 * 60 ,
seconds : 7 * 24 * 60 * 60 ,
milliseconds : 7 * 24 * 60 * 60 * 1000
} ,
days : {
hours : 24 ,
minutes : 24 * 60 ,
seconds : 24 * 60 * 60 ,
milliseconds : 24 * 60 * 60 * 1000
} ,
hours : {
minutes : 60 ,
seconds : 60 * 60 ,
milliseconds : 60 * 60 * 1000
} ,
minutes : {
seconds : 60 ,
milliseconds : 60 * 1000
} ,
seconds : {
milliseconds : 1000
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
} , casualMatrix = Object . assign ( {
years : {
quarters : 4 ,
months : 12 ,
weeks : 52 ,
days : 365 ,
hours : 365 * 24 ,
minutes : 365 * 24 * 60 ,
seconds : 365 * 24 * 60 * 60 ,
milliseconds : 365 * 24 * 60 * 60 * 1000
} ,
quarters : {
months : 3 ,
weeks : 13 ,
days : 91 ,
hours : 91 * 24 ,
minutes : 91 * 24 * 60 ,
seconds : 91 * 24 * 60 * 60 ,
milliseconds : 91 * 24 * 60 * 60 * 1000
} ,
months : {
weeks : 4 ,
days : 30 ,
hours : 30 * 24 ,
minutes : 30 * 24 * 60 ,
seconds : 30 * 24 * 60 * 60 ,
milliseconds : 30 * 24 * 60 * 60 * 1000
2021-05-25 08:30:17 +03:00
}
2022-04-05 15:46:35 +03:00
} , lowOrderMatrix ) , daysInYearAccurate = 146097.0 / 400 , daysInMonthAccurate = 146097.0 / 4800 , accurateMatrix = Object . assign ( {
2021-07-01 15:56:08 +03:00
years : {
quarters : 4 ,
months : 12 ,
weeks : daysInYearAccurate / 7 ,
days : daysInYearAccurate ,
hours : daysInYearAccurate * 24 ,
minutes : daysInYearAccurate * 24 * 60 ,
seconds : daysInYearAccurate * 24 * 60 * 60 ,
milliseconds : daysInYearAccurate * 24 * 60 * 60 * 1000
} ,
quarters : {
months : 3 ,
weeks : daysInYearAccurate / 28 ,
days : daysInYearAccurate / 4 ,
hours : daysInYearAccurate * 24 / 4 ,
minutes : daysInYearAccurate * 24 * 60 / 4 ,
seconds : daysInYearAccurate * 24 * 60 * 60 / 4 ,
milliseconds : daysInYearAccurate * 24 * 60 * 60 * 1000 / 4
} ,
months : {
weeks : daysInMonthAccurate / 7 ,
days : daysInMonthAccurate ,
hours : daysInMonthAccurate * 24 ,
minutes : daysInMonthAccurate * 24 * 60 ,
seconds : daysInMonthAccurate * 24 * 60 * 60 ,
milliseconds : daysInMonthAccurate * 24 * 60 * 60 * 1000
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
} , lowOrderMatrix ) ;
const orderedUnits = [
"years" ,
"quarters" ,
"months" ,
"weeks" ,
"days" ,
"hours" ,
"minutes" ,
"seconds" ,
"milliseconds"
] ;
const reverseUnits = orderedUnits . slice ( 0 ) . reverse ( ) ;
function clone ( dur , alts , clear = false ) {
const conf = {
2022-01-02 23:54:58 +03:00
values : clear ? alts.values : Object.assign ( { } , dur . values , alts . values || { } ) ,
2021-07-01 15:56:08 +03:00
loc : dur.loc.clone ( alts . loc ) ,
conversionAccuracy : alts.conversionAccuracy || dur . conversionAccuracy
} ;
return new Duration ( conf ) ;
}
2022-06-15 17:36:57 +03:00
function antiTrunc ( n ) {
return n < 0 ? Math . floor ( n ) : Math . ceil ( n ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
function convert ( matrix , fromMap , fromUnit , toMap , toUnit ) {
const conv = matrix [ toUnit ] [ fromUnit ] , raw = fromMap [ fromUnit ] / conv , sameSign = Math . sign ( raw ) === Math . sign ( toMap [ toUnit ] ) , added = ! sameSign && toMap [ toUnit ] !== 0 && Math . abs ( raw ) <= 1 ? antiTrunc ( raw ) : Math . trunc ( raw ) ;
toMap [ toUnit ] += added ;
fromMap [ fromUnit ] -= added * conv ;
}
function normalizeValues ( matrix , vals ) {
reverseUnits . reduce ( ( previous , current ) = > {
if ( ! isUndefined ( vals [ current ] ) ) {
if ( previous ) {
convert ( matrix , vals , previous , vals , current ) ;
}
return current ;
2021-05-25 08:30:17 +03:00
} else {
2021-07-01 15:56:08 +03:00
return previous ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
} , null ) ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
class Duration {
constructor ( config ) {
const accurate = config . conversionAccuracy === "longterm" || false ;
this . values = config . values ;
this . loc = config . loc || Locale . create ( ) ;
this . conversionAccuracy = accurate ? "longterm" : "casual" ;
this . invalid = config . invalid || null ;
this . matrix = accurate ? accurateMatrix : casualMatrix ;
this . isLuxonDuration = true ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static fromMillis ( count , opts ) {
2021-07-01 15:56:08 +03:00
return Duration . fromObject ( Object . assign ( {
2021-12-02 10:28:09 +03:00
milliseconds : count
} , opts ) ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static fromObject ( obj ) {
if ( obj == null || typeof obj !== "object" ) {
throw new InvalidArgumentError ( ` Duration.fromObject: argument expected to be an object, got ${ obj === null ? "null" : typeof obj } ` ) ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
return new Duration ( {
2021-12-02 10:28:09 +03:00
values : normalizeObject ( obj , Duration . normalizeUnit , [
2021-07-01 15:56:08 +03:00
"locale" ,
"numberingSystem" ,
"conversionAccuracy" ,
"zone"
] ) ,
2021-12-02 10:28:09 +03:00
loc : Locale.fromObject ( obj ) ,
conversionAccuracy : obj.conversionAccuracy
2021-07-01 15:56:08 +03:00
} ) ;
2021-03-22 13:42:42 +03:00
}
2021-12-02 10:28:09 +03:00
static fromISO ( text , opts ) {
const [ parsed ] = parseISODuration ( text ) ;
2021-07-01 15:56:08 +03:00
if ( parsed ) {
2021-12-02 10:28:09 +03:00
const obj = Object . assign ( parsed , opts ) ;
2021-07-01 15:56:08 +03:00
return Duration . fromObject ( obj ) ;
2021-02-22 11:27:40 +03:00
} else {
2021-12-02 10:28:09 +03:00
return Duration . invalid ( "unparsable" , ` the input " ${ text } " can't be parsed as ISO 8601 ` ) ;
2021-01-25 15:15:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static invalid ( reason , explanation = null ) {
if ( ! reason ) {
2021-07-01 15:56:08 +03:00
throw new InvalidArgumentError ( "need to specify a reason the Duration is invalid" ) ;
}
2021-12-02 10:28:09 +03:00
const invalid = reason instanceof Invalid ? reason : new Invalid ( reason , explanation ) ;
2021-07-01 15:56:08 +03:00
if ( Settings . throwOnInvalid ) {
throw new InvalidDurationError ( invalid ) ;
2021-02-26 12:21:42 +03:00
} else {
2021-07-01 15:56:08 +03:00
return new Duration ( {
invalid
} ) ;
2021-01-25 15:15:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static normalizeUnit ( unit ) {
2021-07-01 15:56:08 +03:00
const normalized = {
year : "years" ,
years : "years" ,
quarter : "quarters" ,
quarters : "quarters" ,
month : "months" ,
months : "months" ,
week : "weeks" ,
weeks : "weeks" ,
day : "days" ,
days : "days" ,
hour : "hours" ,
hours : "hours" ,
minute : "minutes" ,
minutes : "minutes" ,
second : "seconds" ,
seconds : "seconds" ,
millisecond : "milliseconds" ,
milliseconds : "milliseconds"
2021-12-02 10:28:09 +03:00
} [ unit ? unit . toLowerCase ( ) : unit ] ;
if ( ! normalized ) throw new InvalidUnitError ( unit ) ;
2021-07-01 15:56:08 +03:00
return normalized ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static isDuration ( o ) {
return o && o . isLuxonDuration || false ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
get locale() {
return this . isValid ? this . loc.locale : null ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
get numberingSystem() {
return this . isValid ? this . loc.numberingSystem : null ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
toFormat ( fmt , opts = { } ) {
const fmtOpts = Object . assign ( { } , opts , {
2021-12-02 10:28:09 +03:00
floor : opts.round !== false && opts . floor !== false
2021-05-25 08:30:17 +03:00
} ) ;
2021-12-02 10:28:09 +03:00
return this . isValid ? Formatter . create ( this . loc , fmtOpts ) . formatDurationFromString ( this , fmt ) : INVALID1 ;
2021-05-25 08:30:17 +03:00
}
2022-01-02 23:54:58 +03:00
toObject ( opts = { } ) {
if ( ! this . isValid ) return { } ;
const base = Object . assign ( { } , this . values ) ;
2021-12-02 10:28:09 +03:00
if ( opts . includeConfig ) {
2021-07-01 15:56:08 +03:00
base . conversionAccuracy = this . conversionAccuracy ;
base . numberingSystem = this . loc . numberingSystem ;
base . locale = this . loc . locale ;
}
return base ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toISO() {
if ( ! this . isValid ) return null ;
2022-06-15 17:36:57 +03:00
let s = "P" ;
if ( this . years !== 0 ) s += this . years + "Y" ;
if ( this . months !== 0 || this . quarters !== 0 ) s += this . months + this . quarters * 3 + "M" ;
if ( this . weeks !== 0 ) s += this . weeks + "W" ;
if ( this . days !== 0 ) s += this . days + "D" ;
if ( this . hours !== 0 || this . minutes !== 0 || this . seconds !== 0 || this . milliseconds !== 0 ) s += "T" ;
if ( this . hours !== 0 ) s += this . hours + "H" ;
if ( this . minutes !== 0 ) s += this . minutes + "M" ;
if ( this . seconds !== 0 || this . milliseconds !== 0 ) s += roundTo ( this . seconds + this . milliseconds / 1000 , 3 ) + "S" ;
if ( s === "P" ) s += "T0S" ;
return s ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toJSON() {
return this . toISO ( ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toString() {
return this . toISO ( ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
valueOf() {
return this . as ( "milliseconds" ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
plus ( duration ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2022-01-02 23:54:58 +03:00
const dur = friendlyDuration ( duration ) , result = { } ;
2021-07-01 15:56:08 +03:00
for ( const k of orderedUnits ) {
if ( hasOwnProperty ( dur . values , k ) || hasOwnProperty ( this . values , k ) ) {
result [ k ] = dur . get ( k ) + this . get ( k ) ;
}
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
return clone ( this , {
values : result
} , true ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
minus ( duration ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
const dur = friendlyDuration ( duration ) ;
2021-07-01 15:56:08 +03:00
return this . plus ( dur . negate ( ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
mapUnits ( fn ) {
if ( ! this . isValid ) return this ;
2022-01-02 23:54:58 +03:00
const result = { } ;
2021-07-01 15:56:08 +03:00
for ( const k of Object . keys ( this . values ) ) {
result [ k ] = asNumber ( fn ( this . values [ k ] , k ) ) ;
}
return clone ( this , {
values : result
} , true ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
get ( unit ) {
return this [ Duration . normalizeUnit ( unit ) ] ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
set ( values ) {
if ( ! this . isValid ) return this ;
const mixed = Object . assign ( this . values , normalizeObject ( values , Duration . normalizeUnit , [ ] ) ) ;
return clone ( this , {
values : mixed
} ) ;
2021-02-22 11:27:40 +03:00
}
2023-06-07 08:08:35 +03:00
reconfigure ( { locale , numberingSystem , conversionAccuracy } = { } ) {
2021-07-01 15:56:08 +03:00
const loc = this . loc . clone ( {
2021-12-02 10:28:09 +03:00
locale ,
numberingSystem
2021-07-01 15:56:08 +03:00
} ) , opts = {
loc
} ;
if ( conversionAccuracy ) {
opts . conversionAccuracy = conversionAccuracy ;
}
return clone ( this , opts ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
as ( unit ) {
return this . isValid ? this . shiftTo ( unit ) . get ( unit ) : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
normalize() {
if ( ! this . isValid ) return this ;
const vals = this . toObject ( ) ;
normalizeValues ( this . matrix , vals ) ;
return clone ( this , {
values : vals
} , true ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
shiftTo ( . . . units ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
if ( units . length === 0 ) {
2021-07-01 15:56:08 +03:00
return this ;
}
2022-05-19 10:12:37 +03:00
units = units . map ( ( u ) = > Duration . normalizeUnit ( u ) ) ;
2022-01-02 23:54:58 +03:00
const built = { } , accumulated = { } , vals = this . toObject ( ) ;
2021-07-01 15:56:08 +03:00
let lastUnit ;
for ( const k of orderedUnits ) {
2021-12-02 10:28:09 +03:00
if ( units . indexOf ( k ) >= 0 ) {
2021-07-01 15:56:08 +03:00
lastUnit = k ;
let own = 0 ;
for ( const ak in accumulated ) {
own += this . matrix [ ak ] [ k ] * accumulated [ ak ] ;
accumulated [ ak ] = 0 ;
}
if ( isNumber ( vals [ k ] ) ) {
own += vals [ k ] ;
}
const i = Math . trunc ( own ) ;
built [ k ] = i ;
accumulated [ k ] = own - i ;
for ( const down in vals ) {
if ( orderedUnits . indexOf ( down ) > orderedUnits . indexOf ( k ) ) {
convert ( this . matrix , vals , down , built , k ) ;
}
}
} else if ( isNumber ( vals [ k ] ) ) {
accumulated [ k ] = vals [ k ] ;
}
}
for ( const key in accumulated ) {
if ( accumulated [ key ] !== 0 ) {
built [ lastUnit ] += key === lastUnit ? accumulated [ key ] : accumulated [ key ] / this . matrix [ lastUnit ] [ key ] ;
}
}
return clone ( this , {
values : built
} , true ) . normalize ( ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
negate() {
if ( ! this . isValid ) return this ;
2022-01-02 23:54:58 +03:00
const negated = { } ;
2021-07-01 15:56:08 +03:00
for ( const k of Object . keys ( this . values ) ) {
negated [ k ] = - this . values [ k ] ;
}
return clone ( this , {
values : negated
} , true ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
get years() {
return this . isValid ? this . values . years || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get quarters() {
return this . isValid ? this . values . quarters || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get months() {
return this . isValid ? this . values . months || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get weeks() {
return this . isValid ? this . values . weeks || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get days() {
return this . isValid ? this . values . days || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get hours() {
return this . isValid ? this . values . hours || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get minutes() {
return this . isValid ? this . values . minutes || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get seconds() {
return this . isValid ? this . values . seconds || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get milliseconds() {
return this . isValid ? this . values . milliseconds || 0 : NaN ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get isValid() {
return this . invalid === null ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get invalidReason() {
return this . invalid ? this . invalid.reason : null ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
get invalidExplanation() {
return this . invalid ? this . invalid.explanation : null ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
equals ( other ) {
if ( ! this . isValid || ! other . isValid ) {
return false ;
}
if ( ! this . loc . equals ( other . loc ) ) {
return false ;
}
for ( const u of orderedUnits ) {
if ( this . values [ u ] !== other . values [ u ] ) {
return false ;
}
}
return true ;
2021-05-25 08:30:17 +03:00
}
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
function dayDiff ( earlier , later ) {
2022-06-15 17:36:57 +03:00
const utcDayStart = ( dt ) = > dt . toUTC ( 0 , {
2021-07-01 15:56:08 +03:00
keepLocalTime : true
2022-05-19 10:12:37 +03:00
} ) . startOf ( "day" ) . valueOf ( ) , ms = utcDayStart ( later ) - utcDayStart ( earlier ) ;
2021-07-01 15:56:08 +03:00
return Math . floor ( Duration . fromMillis ( ms ) . as ( "days" ) ) ;
}
const MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support" ;
function unsupportedZone ( zone ) {
return new Invalid ( "unsupported zone" , ` the zone " ${ zone . name } " is not supported ` ) ;
}
2022-06-15 17:36:57 +03:00
function possiblyCachedWeekData ( dt ) {
if ( dt . weekData === null ) {
dt . weekData = gregorianToWeek ( dt . c ) ;
2021-03-22 13:42:42 +03:00
}
2022-06-15 17:36:57 +03:00
return dt . weekData ;
2021-03-22 13:42:42 +03:00
}
function clone1 ( inst , alts ) {
const current = {
ts : inst.ts ,
zone : inst.zone ,
c : inst.c ,
o : inst.o ,
loc : inst.loc ,
invalid : inst.invalid
} ;
2022-01-02 23:54:58 +03:00
return new DateTime ( Object . assign ( { } , current , alts , {
2021-03-22 13:42:42 +03:00
old : current
} ) ) ;
}
function fixOffset ( localTS , o , tz ) {
let utcGuess = localTS - o * 60 * 1000 ;
const o2 = tz . offset ( utcGuess ) ;
if ( o === o2 ) {
return [
utcGuess ,
o
] ;
2021-02-22 11:27:40 +03:00
}
2021-03-22 13:42:42 +03:00
utcGuess -= ( o2 - o ) * 60 * 1000 ;
const o3 = tz . offset ( utcGuess ) ;
if ( o2 === o3 ) {
return [
utcGuess ,
o2
] ;
2021-02-22 11:27:40 +03:00
}
2021-03-22 13:42:42 +03:00
return [
localTS - Math . min ( o2 , o3 ) * 60 * 1000 ,
Math . max ( o2 , o3 )
] ;
}
2022-06-15 17:36:57 +03:00
function tsToObj ( ts , offset ) {
ts += offset * 60 * 1000 ;
2021-03-22 13:42:42 +03:00
const d = new Date ( ts ) ;
return {
year : d.getUTCFullYear ( ) ,
month : d.getUTCMonth ( ) + 1 ,
day : d.getUTCDate ( ) ,
hour : d.getUTCHours ( ) ,
minute : d.getUTCMinutes ( ) ,
second : d.getUTCSeconds ( ) ,
millisecond : d.getUTCMilliseconds ( )
} ;
}
2022-06-15 17:36:57 +03:00
function objToTS ( obj , offset , zone ) {
return fixOffset ( objToLocalTS ( obj ) , offset , zone ) ;
2021-03-22 13:42:42 +03:00
}
function adjustTime ( inst , dur ) {
2022-01-02 23:54:58 +03:00
const oPre = inst . o , year = inst . c . year + Math . trunc ( dur . years ) , month = inst . c . month + Math . trunc ( dur . months ) + Math . trunc ( dur . quarters ) * 3 , c = Object . assign ( { } , inst . c , {
2021-03-22 13:42:42 +03:00
year ,
month ,
day : Math.min ( inst . c . day , daysInMonth ( year , month ) ) + Math . trunc ( dur . days ) + Math . trunc ( dur . weeks ) * 7
} ) , millisToAdd = Duration . fromObject ( {
years : dur.years - Math . trunc ( dur . years ) ,
quarters : dur.quarters - Math . trunc ( dur . quarters ) ,
months : dur.months - Math . trunc ( dur . months ) ,
weeks : dur.weeks - Math . trunc ( dur . weeks ) ,
days : dur.days - Math . trunc ( dur . days ) ,
hours : dur.hours ,
minutes : dur.minutes ,
seconds : dur.seconds ,
milliseconds : dur.milliseconds
} ) . as ( "milliseconds" ) , localTS = objToLocalTS ( c ) ;
let [ ts , o ] = fixOffset ( localTS , oPre , inst . zone ) ;
if ( millisToAdd !== 0 ) {
ts += millisToAdd ;
o = inst . zone . offset ( ts ) ;
2021-02-22 11:27:40 +03:00
}
2021-03-22 13:42:42 +03:00
return {
ts ,
o
} ;
}
2021-07-01 15:56:08 +03:00
function parseDataToDateTime ( parsed , parsedZone , opts , format , text ) {
2023-06-07 08:08:35 +03:00
const { setZone , zone } = opts ;
2021-03-22 13:42:42 +03:00
if ( parsed && Object . keys ( parsed ) . length !== 0 ) {
2021-07-01 15:56:08 +03:00
const interpretationZone = parsedZone || zone , inst = DateTime . fromObject ( Object . assign ( parsed , opts , {
2021-03-22 13:42:42 +03:00
zone : interpretationZone ,
setZone : undefined
} ) ) ;
return setZone ? inst : inst.setZone ( zone ) ;
} else {
return DateTime . invalid ( new Invalid ( "unparsable" , ` the input " ${ text } " can't be parsed as ${ format } ` ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-03-22 13:42:42 +03:00
}
2022-06-15 17:36:57 +03:00
function toTechFormat ( dt , format , allowZ = true ) {
return dt . isValid ? Formatter . create ( Locale . create ( "en-US" ) , {
2021-03-22 13:42:42 +03:00
allowZ ,
forceSimple : true
2022-06-15 17:36:57 +03:00
} ) . formatDateTimeFromString ( dt , format ) : null ;
2021-03-22 13:42:42 +03:00
}
2023-06-07 08:08:35 +03:00
function toTechTimeFormat ( dt , { suppressSeconds = false , suppressMilliseconds = false , includeOffset , includeZone = false , spaceZone = false , format = "extended" } ) {
2021-03-22 13:42:42 +03:00
let fmt = format === "basic" ? "HHmm" : "HH:mm" ;
2022-06-15 17:36:57 +03:00
if ( ! suppressSeconds || dt . second !== 0 || dt . millisecond !== 0 ) {
2021-03-22 13:42:42 +03:00
fmt += format === "basic" ? "ss" : ":ss" ;
2022-06-15 17:36:57 +03:00
if ( ! suppressMilliseconds || dt . millisecond !== 0 ) {
2021-03-22 13:42:42 +03:00
fmt += ".SSS" ;
2021-02-26 12:21:42 +03:00
}
}
2021-03-22 13:42:42 +03:00
if ( ( includeZone || includeOffset ) && spaceZone ) {
fmt += " " ;
2021-02-26 12:21:42 +03:00
}
2021-03-22 13:42:42 +03:00
if ( includeZone ) {
fmt += "z" ;
} else if ( includeOffset ) {
fmt += format === "basic" ? "ZZZ" : "ZZ" ;
2021-02-26 12:21:42 +03:00
}
2022-06-15 17:36:57 +03:00
return toTechFormat ( dt , fmt ) ;
2021-03-22 13:42:42 +03:00
}
const defaultUnitValues = {
month : 1 ,
day : 1 ,
hour : 0 ,
minute : 0 ,
second : 0 ,
millisecond : 0
} , defaultWeekUnitValues = {
weekNumber : 1 ,
weekday : 1 ,
hour : 0 ,
minute : 0 ,
second : 0 ,
millisecond : 0
} , defaultOrdinalUnitValues = {
ordinal : 1 ,
hour : 0 ,
minute : 0 ,
second : 0 ,
millisecond : 0
} ;
const orderedUnits1 = [
"year" ,
"month" ,
"day" ,
"hour" ,
"minute" ,
"second" ,
"millisecond"
] , orderedWeekUnits = [
"weekYear" ,
"weekNumber" ,
"weekday" ,
"hour" ,
"minute" ,
"second" ,
"millisecond"
] , orderedOrdinalUnits = [
"year" ,
"ordinal" ,
"hour" ,
"minute" ,
"second" ,
"millisecond"
] ;
2021-09-01 16:11:55 +03:00
function normalizeUnit ( unit ) {
2021-03-22 13:42:42 +03:00
const normalized = {
year : "year" ,
years : "year" ,
month : "month" ,
months : "month" ,
day : "day" ,
days : "day" ,
hour : "hour" ,
hours : "hour" ,
minute : "minute" ,
minutes : "minute" ,
quarter : "quarter" ,
quarters : "quarter" ,
second : "second" ,
seconds : "second" ,
millisecond : "millisecond" ,
milliseconds : "millisecond" ,
weekday : "weekday" ,
weekdays : "weekday" ,
weeknumber : "weekNumber" ,
weeksnumber : "weekNumber" ,
weeknumbers : "weekNumber" ,
weekyear : "weekYear" ,
weekyears : "weekYear" ,
ordinal : "ordinal"
2021-09-01 16:11:55 +03:00
} [ unit . toLowerCase ( ) ] ;
if ( ! normalized ) throw new InvalidUnitError ( unit ) ;
2021-03-22 13:42:42 +03:00
return normalized ;
}
2021-05-25 08:30:17 +03:00
function quickDT ( obj , zone ) {
for ( const u of orderedUnits1 ) {
if ( isUndefined ( obj [ u ] ) ) {
obj [ u ] = defaultUnitValues [ u ] ;
2021-03-22 13:42:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
const invalid = hasInvalidGregorianData ( obj ) || hasInvalidTimeData ( obj ) ;
if ( invalid ) {
return DateTime . invalid ( invalid ) ;
}
const tsNow = Settings . now ( ) , offsetProvis = zone . offset ( tsNow ) , [ ts , o ] = objToTS ( obj , offsetProvis , zone ) ;
return new DateTime ( {
ts ,
zone ,
o
} ) ;
}
function diffRelative ( start , end , opts ) {
2021-09-01 16:11:55 +03:00
const round = isUndefined ( opts . round ) ? true : opts . round , format = ( c , unit ) = > {
2021-07-01 15:56:08 +03:00
c = roundTo ( c , round || opts . calendary ? 0 : 2 , true ) ;
const formatter = end . loc . clone ( opts ) . relFormatter ( opts ) ;
2021-09-01 16:11:55 +03:00
return formatter . format ( c , unit ) ;
} , differ = ( unit ) = > {
2021-07-01 15:56:08 +03:00
if ( opts . calendary ) {
2021-09-01 16:11:55 +03:00
if ( ! end . hasSame ( start , unit ) ) {
return end . startOf ( unit ) . diff ( start . startOf ( unit ) , unit ) . get ( unit ) ;
2021-07-01 15:56:08 +03:00
} else return 0 ;
} else {
2021-09-01 16:11:55 +03:00
return end . diff ( start , unit ) . get ( unit ) ;
2021-07-01 15:56:08 +03:00
}
} ;
if ( opts . unit ) {
return format ( differ ( opts . unit ) , opts . unit ) ;
}
2022-06-15 17:36:57 +03:00
for ( const unit of opts . units ) {
const count = differ ( unit ) ;
2021-07-01 15:56:08 +03:00
if ( Math . abs ( count ) >= 1 ) {
2022-06-15 17:36:57 +03:00
return format ( count , unit ) ;
2021-07-01 15:56:08 +03:00
}
}
return format ( 0 , opts . units [ opts . units . length - 1 ] ) ;
}
function friendlyDuration ( durationish ) {
if ( isNumber ( durationish ) ) {
return Duration . fromMillis ( durationish ) ;
} else if ( Duration . isDuration ( durationish ) ) {
return durationish ;
} else if ( typeof durationish === "object" ) {
return Duration . fromObject ( durationish ) ;
} else {
throw new InvalidArgumentError ( ` Unknown duration argument ${ durationish } of type ${ typeof durationish } ` ) ;
}
}
class DateTime {
2021-12-02 10:28:09 +03:00
constructor ( config ) {
const zone = config . zone || Settings . defaultZone ;
let invalid = config . invalid || ( Number . isNaN ( config . ts ) ? new Invalid ( "invalid input" ) : null ) || ( ! zone . isValid ? unsupportedZone ( zone ) : null ) ;
this . ts = isUndefined ( config . ts ) ? Settings . now ( ) : config . ts ;
2021-09-01 16:11:55 +03:00
let c = null , o = null ;
2021-07-01 15:56:08 +03:00
if ( ! invalid ) {
2021-12-02 10:28:09 +03:00
const unchanged = config . old && config . old . ts === this . ts && config . old . zone . equals ( zone ) ;
2021-07-01 15:56:08 +03:00
if ( unchanged ) {
2021-09-01 16:11:55 +03:00
[ c , o ] = [
2021-12-02 10:28:09 +03:00
config . old . c ,
config . old . o
2021-07-01 15:56:08 +03:00
] ;
} else {
2021-09-01 16:11:55 +03:00
const ot = zone . offset ( this . ts ) ;
2021-07-01 15:56:08 +03:00
c = tsToObj ( this . ts , ot ) ;
invalid = Number . isNaN ( c . year ) ? new Invalid ( "invalid input" ) : null ;
c = invalid ? null : c ;
2021-09-01 16:11:55 +03:00
o = invalid ? null : ot ;
2021-07-01 15:56:08 +03:00
}
}
2021-09-01 16:11:55 +03:00
this . _zone = zone ;
2021-12-02 10:28:09 +03:00
this . loc = config . loc || Locale . create ( ) ;
2021-07-01 15:56:08 +03:00
this . invalid = invalid ;
this . weekData = null ;
this . c = c ;
2021-09-01 16:11:55 +03:00
this . o = o ;
2021-07-01 15:56:08 +03:00
this . isLuxonDateTime = true ;
}
static local ( year , month , day , hour , minute , second , millisecond ) {
if ( isUndefined ( year ) ) {
return new DateTime ( {
ts : Settings.now ( )
} ) ;
} else {
return quickDT ( {
year ,
month ,
day ,
hour ,
minute ,
second ,
millisecond
} , Settings . defaultZone ) ;
}
}
2021-12-02 10:28:09 +03:00
static utc ( year , month , day , hour , minute , second , millisecond ) {
if ( isUndefined ( year ) ) {
2021-07-01 15:56:08 +03:00
return new DateTime ( {
ts : Settings.now ( ) ,
zone : FixedOffsetZone.utcInstance
} ) ;
} else {
return quickDT ( {
2021-12-02 10:28:09 +03:00
year ,
month ,
day ,
hour ,
minute ,
second ,
millisecond
2021-07-01 15:56:08 +03:00
} , FixedOffsetZone . utcInstance ) ;
}
}
2022-06-15 17:36:57 +03:00
static fromJSDate ( date , options = { } ) {
const ts = isDate ( date ) ? date . valueOf ( ) : NaN ;
2021-07-01 15:56:08 +03:00
if ( Number . isNaN ( ts ) ) {
return DateTime . invalid ( "invalid input" ) ;
}
2021-12-02 10:28:09 +03:00
const zoneToUse = normalizeZone ( options . zone , Settings . defaultZone ) ;
2021-07-01 15:56:08 +03:00
if ( ! zoneToUse . isValid ) {
return DateTime . invalid ( unsupportedZone ( zoneToUse ) ) ;
}
return new DateTime ( {
ts : ts ,
zone : zoneToUse ,
2021-12-02 10:28:09 +03:00
loc : Locale.fromObject ( options )
2021-07-01 15:56:08 +03:00
} ) ;
}
2022-01-02 23:54:58 +03:00
static fromMillis ( milliseconds , options = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! isNumber ( milliseconds ) ) {
throw new InvalidArgumentError ( ` fromMillis requires a numerical input, but received a ${ typeof milliseconds } with value ${ milliseconds } ` ) ;
2022-04-05 15:46:35 +03:00
} else if ( milliseconds < - 8.64e15 || milliseconds > 8.64e15 ) {
2021-07-01 15:56:08 +03:00
return DateTime . invalid ( "Timestamp out of range" ) ;
} else {
return new DateTime ( {
ts : milliseconds ,
2021-12-02 10:28:09 +03:00
zone : normalizeZone ( options . zone , Settings . defaultZone ) ,
loc : Locale.fromObject ( options )
2021-07-01 15:56:08 +03:00
} ) ;
}
}
2022-01-02 23:54:58 +03:00
static fromSeconds ( seconds , options = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! isNumber ( seconds ) ) {
throw new InvalidArgumentError ( "fromSeconds requires a numerical input" ) ;
} else {
return new DateTime ( {
ts : seconds * 1000 ,
2021-12-02 10:28:09 +03:00
zone : normalizeZone ( options . zone , Settings . defaultZone ) ,
loc : Locale.fromObject ( options )
2021-07-01 15:56:08 +03:00
} ) ;
}
}
static fromObject ( obj ) {
const zoneToUse = normalizeZone ( obj . zone , Settings . defaultZone ) ;
if ( ! zoneToUse . isValid ) {
return DateTime . invalid ( unsupportedZone ( zoneToUse ) ) ;
}
const tsNow = Settings . now ( ) , offsetProvis = zoneToUse . offset ( tsNow ) , normalized = normalizeObject ( obj , normalizeUnit , [
"zone" ,
"locale" ,
"outputCalendar" ,
"numberingSystem"
] ) , 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 ( obj ) ;
if ( ( containsGregor || containsOrdinal ) && definiteWeekDef ) {
throw new ConflictingSpecificationError ( "Can't mix weekYear/weekNumber units with year/month/day or ordinals" ) ;
}
if ( containsGregorMD && containsOrdinal ) {
throw new ConflictingSpecificationError ( "Can't mix ordinal dates with month/day" ) ;
}
const useWeekData = definiteWeekDef || normalized . weekday && ! containsGregor ;
let units , defaultValues , objNow = tsToObj ( tsNow , offsetProvis ) ;
if ( useWeekData ) {
units = orderedWeekUnits ;
defaultValues = defaultWeekUnitValues ;
objNow = gregorianToWeek ( objNow ) ;
} else if ( containsOrdinal ) {
units = orderedOrdinalUnits ;
defaultValues = defaultOrdinalUnitValues ;
objNow = gregorianToOrdinal ( objNow ) ;
} else {
units = orderedUnits1 ;
defaultValues = defaultUnitValues ;
}
let foundFirst = false ;
for ( const u of units ) {
const v = normalized [ u ] ;
if ( ! isUndefined ( v ) ) {
foundFirst = true ;
} else if ( foundFirst ) {
normalized [ u ] = defaultValues [ u ] ;
} else {
normalized [ u ] = objNow [ u ] ;
}
}
2021-09-01 16:11:55 +03:00
const higherOrderInvalid = useWeekData ? hasInvalidWeekData ( normalized ) : containsOrdinal ? hasInvalidOrdinalData ( normalized ) : hasInvalidGregorianData ( normalized ) , invalid = higherOrderInvalid || hasInvalidTimeData ( normalized ) ;
if ( invalid ) {
return DateTime . invalid ( invalid ) ;
2021-07-01 15:56:08 +03:00
}
const gregorian = useWeekData ? weekToGregorian ( normalized ) : containsOrdinal ? ordinalToGregorian ( normalized ) : normalized , [ tsFinal , offsetFinal ] = objToTS ( gregorian , offsetProvis , zoneToUse ) , inst = new DateTime ( {
ts : tsFinal ,
zone : zoneToUse ,
o : offsetFinal ,
loc
} ) ;
if ( normalized . weekday && containsGregor && obj . weekday !== inst . weekday ) {
return DateTime . invalid ( "mismatched weekday" , ` you can't specify both a weekday of ${ normalized . weekday } and a date of ${ inst . toISO ( ) } ` ) ;
}
return inst ;
}
2022-01-02 23:54:58 +03:00
static fromISO ( text , opts = { } ) {
2021-07-01 15:56:08 +03:00
const [ vals , parsedZone ] = parseISODate ( text ) ;
2021-12-02 10:28:09 +03:00
return parseDataToDateTime ( vals , parsedZone , opts , "ISO 8601" , text ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
static fromRFC2822 ( text , opts = { } ) {
2021-12-02 10:28:09 +03:00
const [ vals , parsedZone ] = parseRFC2822Date ( text ) ;
return parseDataToDateTime ( vals , parsedZone , opts , "RFC 2822" , text ) ;
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
static fromHTTP ( text , opts = { } ) {
2021-12-02 10:28:09 +03:00
const [ vals , parsedZone ] = parseHTTPDate ( text ) ;
return parseDataToDateTime ( vals , parsedZone , opts , "HTTP" , opts ) ;
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
static fromFormat ( text , fmt , opts = { } ) {
2021-12-02 10:28:09 +03:00
if ( isUndefined ( text ) || isUndefined ( fmt ) ) {
2021-07-01 15:56:08 +03:00
throw new InvalidArgumentError ( "fromFormat requires an input string and a format" ) ;
}
2023-06-07 08:08:35 +03:00
const { locale = null , numberingSystem = null } = opts , localeToUse = Locale . fromOpts ( {
2021-09-01 16:11:55 +03:00
locale ,
2021-07-01 15:56:08 +03:00
numberingSystem ,
defaultToEN : true
2021-12-02 10:28:09 +03:00
} ) , [ vals , parsedZone , invalid ] = parseFromTokens ( localeToUse , text , fmt ) ;
2021-09-01 16:11:55 +03:00
if ( invalid ) {
return DateTime . invalid ( invalid ) ;
2021-03-22 13:42:42 +03:00
} else {
2021-12-02 10:28:09 +03:00
return parseDataToDateTime ( vals , parsedZone , opts , ` format ${ fmt } ` , text ) ;
2021-03-22 13:42:42 +03:00
}
}
2022-01-02 23:54:58 +03:00
static fromString ( text , fmt , opts = { } ) {
2021-12-02 10:28:09 +03:00
return DateTime . fromFormat ( text , fmt , opts ) ;
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
static fromSQL ( text , opts = { } ) {
2021-12-02 10:28:09 +03:00
const [ vals , parsedZone ] = parseSQL ( text ) ;
return parseDataToDateTime ( vals , parsedZone , opts , "SQL" , text ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
static invalid ( reason , explanation = null ) {
if ( ! reason ) {
2021-07-01 15:56:08 +03:00
throw new InvalidArgumentError ( "need to specify a reason the DateTime is invalid" ) ;
}
2021-12-02 10:28:09 +03:00
const invalid = reason instanceof Invalid ? reason : new Invalid ( reason , explanation ) ;
2021-07-01 15:56:08 +03:00
if ( Settings . throwOnInvalid ) {
2021-09-01 16:11:55 +03:00
throw new InvalidDateTimeError ( invalid ) ;
2021-07-01 15:56:08 +03:00
} else {
return new DateTime ( {
2021-09-01 16:11:55 +03:00
invalid
2021-07-01 15:56:08 +03:00
} ) ;
2021-03-22 13:42:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static isDateTime ( o ) {
return o && o . isLuxonDateTime || false ;
}
2021-12-02 10:28:09 +03:00
get ( unit ) {
return this [ unit ] ;
2021-07-01 15:56:08 +03:00
}
get isValid() {
return this . invalid === null ;
}
get invalidReason() {
return this . invalid ? this . invalid.reason : null ;
}
get invalidExplanation() {
return this . invalid ? this . invalid.explanation : null ;
}
get locale() {
return this . isValid ? this . loc.locale : null ;
}
get numberingSystem() {
return this . isValid ? this . loc.numberingSystem : null ;
}
get outputCalendar() {
return this . isValid ? this . loc.outputCalendar : null ;
}
get zone() {
return this . _zone ;
}
get zoneName() {
return this . isValid ? this . zone.name : null ;
}
get year() {
return this . isValid ? this . c.year : NaN ;
}
get quarter() {
return this . isValid ? Math . ceil ( this . c . month / 3 ) : NaN ;
}
get month() {
return this . isValid ? this . c.month : NaN ;
}
get day() {
return this . isValid ? this . c.day : NaN ;
}
get hour() {
return this . isValid ? this . c.hour : NaN ;
}
get minute() {
return this . isValid ? this . c.minute : NaN ;
}
get second() {
return this . isValid ? this . c.second : NaN ;
}
get millisecond() {
return this . isValid ? this . c.millisecond : NaN ;
}
get weekYear() {
return this . isValid ? possiblyCachedWeekData ( this ) . weekYear : NaN ;
}
get weekNumber() {
return this . isValid ? possiblyCachedWeekData ( this ) . weekNumber : NaN ;
}
get weekday() {
return this . isValid ? possiblyCachedWeekData ( this ) . weekday : NaN ;
}
get ordinal() {
return this . isValid ? gregorianToOrdinal ( this . c ) . ordinal : NaN ;
}
get monthShort() {
return this . isValid ? Info . months ( "short" , {
locale : this.locale
} ) [ this . month - 1 ] : null ;
}
get monthLong() {
return this . isValid ? Info . months ( "long" , {
locale : this.locale
} ) [ this . month - 1 ] : null ;
}
get weekdayShort() {
return this . isValid ? Info . weekdays ( "short" , {
locale : this.locale
} ) [ this . weekday - 1 ] : null ;
}
get weekdayLong() {
return this . isValid ? Info . weekdays ( "long" , {
locale : this.locale
} ) [ this . weekday - 1 ] : null ;
}
get offset() {
return this . isValid ? + this . o : NaN ;
}
get offsetNameShort() {
if ( this . isValid ) {
return this . zone . offsetName ( this . ts , {
format : "short" ,
locale : this.locale
} ) ;
} else {
return null ;
2021-03-22 13:42:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
get offsetNameLong() {
if ( this . isValid ) {
return this . zone . offsetName ( this . ts , {
format : "long" ,
locale : this.locale
2021-03-22 13:42:42 +03:00
} ) ;
} else {
2021-07-01 15:56:08 +03:00
return null ;
2021-02-22 11:27:40 +03:00
}
}
2021-07-01 15:56:08 +03:00
get isOffsetFixed() {
return this . isValid ? this . zone.universal : null ;
}
get isInDST() {
if ( this . isOffsetFixed ) {
return false ;
2021-02-22 11:27:40 +03:00
} else {
2021-07-01 15:56:08 +03:00
return this . offset > this . set ( {
month : 1
} ) . offset || this . offset > this . set ( {
month : 5
} ) . offset ;
2021-01-25 15:15:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
get isInLeapYear() {
return isLeapYear ( this . year ) ;
}
get daysInMonth() {
return daysInMonth ( this . year , this . month ) ;
}
get daysInYear() {
return this . isValid ? daysInYear ( this . year ) : NaN ;
}
get weeksInWeekYear() {
return this . isValid ? weeksInWeekYear ( this . weekYear ) : NaN ;
}
2022-01-02 23:54:58 +03:00
resolvedLocaleOpts ( opts = { } ) {
2023-06-07 08:08:35 +03:00
const { locale , numberingSystem , calendar } = Formatter . create ( this . loc . clone ( opts ) , opts ) . resolvedOptions ( this ) ;
2021-07-01 15:56:08 +03:00
return {
2021-09-01 16:11:55 +03:00
locale ,
2021-07-01 15:56:08 +03:00
numberingSystem ,
outputCalendar : calendar
} ;
2021-03-22 13:42:42 +03:00
}
2022-06-15 17:36:57 +03:00
toUTC ( offset = 0 , opts = { } ) {
return this . setZone ( FixedOffsetZone . instance ( offset ) , opts ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toLocal() {
return this . setZone ( Settings . defaultZone ) ;
}
2023-06-07 08:08:35 +03:00
setZone ( zone , { keepLocalTime = false , keepCalendarTime = false } = { } ) {
2021-12-02 10:28:09 +03:00
zone = normalizeZone ( zone , Settings . defaultZone ) ;
if ( zone . equals ( this . zone ) ) {
2021-07-01 15:56:08 +03:00
return this ;
2021-12-02 10:28:09 +03:00
} else if ( ! zone . isValid ) {
return DateTime . invalid ( unsupportedZone ( zone ) ) ;
2021-02-22 11:27:40 +03:00
} else {
2021-07-01 15:56:08 +03:00
let newTS = this . ts ;
if ( keepLocalTime || keepCalendarTime ) {
2021-12-02 10:28:09 +03:00
const offsetGuess = zone . offset ( this . ts ) ;
2021-07-01 15:56:08 +03:00
const asObj = this . toObject ( ) ;
2021-12-02 10:28:09 +03:00
[ newTS ] = objToTS ( asObj , offsetGuess , zone ) ;
2021-07-01 15:56:08 +03:00
}
return clone1 ( this , {
ts : newTS ,
2021-12-02 10:28:09 +03:00
zone
2021-05-25 08:30:17 +03:00
} ) ;
2021-01-25 15:15:42 +03:00
}
2021-03-22 13:42:42 +03:00
}
2023-06-07 08:08:35 +03:00
reconfigure ( { locale , numberingSystem , outputCalendar } = { } ) {
2021-07-01 15:56:08 +03:00
const loc = this . loc . clone ( {
2021-12-02 10:28:09 +03:00
locale ,
numberingSystem ,
outputCalendar
2021-07-01 15:56:08 +03:00
} ) ;
return clone1 ( this , {
loc
} ) ;
}
2021-12-02 10:28:09 +03:00
setLocale ( locale ) {
2021-07-01 15:56:08 +03:00
return this . reconfigure ( {
2021-12-02 10:28:09 +03:00
locale
2021-07-01 15:56:08 +03:00
} ) ;
}
2021-12-02 10:28:09 +03:00
set ( values ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
const normalized = normalizeObject ( values , normalizeUnit , [ ] ) , settingWeekStuff = ! isUndefined ( normalized . weekYear ) || ! isUndefined ( normalized . weekNumber ) || ! isUndefined ( normalized . weekday ) ;
2021-07-01 15:56:08 +03:00
let mixed ;
if ( settingWeekStuff ) {
mixed = weekToGregorian ( Object . assign ( gregorianToWeek ( this . c ) , normalized ) ) ;
} else if ( ! isUndefined ( normalized . ordinal ) ) {
mixed = ordinalToGregorian ( Object . assign ( gregorianToOrdinal ( this . c ) , normalized ) ) ;
2021-05-25 08:30:17 +03:00
} else {
2021-07-01 15:56:08 +03:00
mixed = Object . assign ( this . toObject ( ) , normalized ) ;
if ( isUndefined ( normalized . day ) ) {
mixed . day = Math . min ( daysInMonth ( mixed . year , mixed . month ) , mixed . day ) ;
2021-05-25 08:30:17 +03:00
}
2021-01-25 15:15:42 +03:00
}
2021-09-01 16:11:55 +03:00
const [ ts , o ] = objToTS ( mixed , this . o , this . zone ) ;
2021-07-01 15:56:08 +03:00
return clone1 ( this , {
ts ,
2021-09-01 16:11:55 +03:00
o
2021-03-22 13:42:42 +03:00
} ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
plus ( duration ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
const dur = friendlyDuration ( duration ) ;
2021-07-01 15:56:08 +03:00
return clone1 ( this , adjustTime ( this , dur ) ) ;
}
2021-12-02 10:28:09 +03:00
minus ( duration ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
const dur = friendlyDuration ( duration ) . negate ( ) ;
2021-07-01 15:56:08 +03:00
return clone1 ( this , adjustTime ( this , dur ) ) ;
}
2021-12-02 10:28:09 +03:00
startOf ( unit ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return this ;
2022-01-02 23:54:58 +03:00
const o = { } , normalizedUnit = Duration . normalizeUnit ( unit ) ;
2021-07-01 15:56:08 +03:00
switch ( normalizedUnit ) {
case "years" :
2021-09-01 16:11:55 +03:00
o . month = 1 ;
2021-07-01 15:56:08 +03:00
case "quarters" :
case "months" :
2021-09-01 16:11:55 +03:00
o . day = 1 ;
2021-07-01 15:56:08 +03:00
case "weeks" :
case "days" :
2021-09-01 16:11:55 +03:00
o . hour = 0 ;
2021-07-01 15:56:08 +03:00
case "hours" :
2021-09-01 16:11:55 +03:00
o . minute = 0 ;
2021-07-01 15:56:08 +03:00
case "minutes" :
2021-09-01 16:11:55 +03:00
o . second = 0 ;
2021-07-01 15:56:08 +03:00
case "seconds" :
2021-09-01 16:11:55 +03:00
o . millisecond = 0 ;
2021-07-01 15:56:08 +03:00
break ;
2021-10-08 10:47:01 +03:00
case "milliseconds" :
break ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
if ( normalizedUnit === "weeks" ) {
2021-09-01 16:11:55 +03:00
o . weekday = 1 ;
2021-07-01 15:56:08 +03:00
}
if ( normalizedUnit === "quarters" ) {
const q = Math . ceil ( this . month / 3 ) ;
2021-09-01 16:11:55 +03:00
o . month = ( q - 1 ) * 3 + 1 ;
2021-07-01 15:56:08 +03:00
}
2021-09-01 16:11:55 +03:00
return this . set ( o ) ;
2021-03-22 13:42:42 +03:00
}
2021-12-02 10:28:09 +03:00
endOf ( unit ) {
2021-07-01 15:56:08 +03:00
return this . isValid ? this . plus ( {
2021-12-02 10:28:09 +03:00
[ unit ] : 1
} ) . startOf ( unit ) . minus ( 1 ) : this ;
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
toFormat ( fmt , opts = { } ) {
2021-12-02 10:28:09 +03:00
return this . isValid ? Formatter . create ( this . loc . redefaultToEN ( opts ) ) . formatDateTimeFromString ( this , fmt ) : INVALID ;
2021-03-22 13:42:42 +03:00
}
2021-12-02 10:28:09 +03:00
toLocaleString ( opts = DATE_SHORT ) {
return this . isValid ? Formatter . create ( this . loc . clone ( opts ) , opts ) . formatDateTime ( this ) : INVALID ;
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
toLocaleParts ( opts = { } ) {
2021-12-02 10:28:09 +03:00
return this . isValid ? Formatter . create ( this . loc . clone ( opts ) , opts ) . formatDateTimeParts ( this ) : [ ] ;
2021-03-22 13:42:42 +03:00
}
2022-01-02 23:54:58 +03:00
toISO ( opts = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) {
return null ;
}
2021-12-02 10:28:09 +03:00
return ` ${ this . toISODate ( opts ) } T ${ this . toISOTime ( opts ) } ` ;
2021-03-22 13:42:42 +03:00
}
2023-06-07 08:08:35 +03:00
toISODate ( { format = "extended" } = { } ) {
2021-12-02 10:28:09 +03:00
let fmt = format === "basic" ? "yyyyMMdd" : "yyyy-MM-dd" ;
2021-07-01 15:56:08 +03:00
if ( this . year > 9999 ) {
fmt = "+" + fmt ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
return toTechFormat ( this , fmt ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toISOWeekDate() {
return toTechFormat ( this , "kkkk-'W'WW-c" ) ;
}
2023-06-07 08:08:35 +03:00
toISOTime ( { suppressMilliseconds = false , suppressSeconds = false , includeOffset = true , format = "extended" } = { } ) {
2021-07-01 15:56:08 +03:00
return toTechTimeFormat ( this , {
suppressSeconds ,
suppressMilliseconds ,
includeOffset ,
2021-12-02 10:28:09 +03:00
format
2021-07-01 15:56:08 +03:00
} ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toRFC2822() {
return toTechFormat ( this , "EEE, dd LLL yyyy HH:mm:ss ZZZ" , false ) ;
}
toHTTP() {
return toTechFormat ( this . toUTC ( ) , "EEE, dd LLL yyyy HH:mm:ss 'GMT'" ) ;
}
toSQLDate() {
return toTechFormat ( this , "yyyy-MM-dd" ) ;
}
2023-06-07 08:08:35 +03:00
toSQLTime ( { includeOffset = true , includeZone = false } = { } ) {
2021-07-01 15:56:08 +03:00
return toTechTimeFormat ( this , {
2021-12-02 10:28:09 +03:00
includeOffset ,
2021-07-01 15:56:08 +03:00
includeZone ,
spaceZone : true
} ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
toSQL ( opts = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) {
return null ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
return ` ${ this . toSQLDate ( ) } ${ this . toSQLTime ( opts ) } ` ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toString() {
return this . isValid ? this . toISO ( ) : INVALID ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
valueOf() {
return this . toMillis ( ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toMillis() {
return this . isValid ? this . ts : NaN ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toSeconds() {
return this . isValid ? this . ts / 1000 : NaN ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
toJSON() {
return this . toISO ( ) ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
toBSON() {
return this . toJSDate ( ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
toObject ( opts = { } ) {
if ( ! this . isValid ) return { } ;
const base = Object . assign ( { } , this . c ) ;
2021-12-02 10:28:09 +03:00
if ( opts . includeConfig ) {
2021-07-01 15:56:08 +03:00
base . outputCalendar = this . outputCalendar ;
base . numberingSystem = this . loc . numberingSystem ;
base . locale = this . loc . locale ;
}
return base ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
toJSDate() {
return new Date ( this . isValid ? this . ts : NaN ) ;
2021-03-22 13:42:42 +03:00
}
2022-01-02 23:54:58 +03:00
diff ( otherDateTime , unit = "milliseconds" , opts = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid || ! otherDateTime . isValid ) {
return Duration . invalid ( this . invalid || otherDateTime . invalid , "created by diffing an invalid DateTime" ) ;
}
const durOpts = Object . assign ( {
locale : this.locale ,
numberingSystem : this.numberingSystem
2021-12-02 10:28:09 +03:00
} , opts ) ;
const units = maybeArray ( unit ) . map ( Duration . normalizeUnit ) , otherIsLater = otherDateTime . valueOf ( ) > this . valueOf ( ) , earlier = otherIsLater ? this : otherDateTime , later = otherIsLater ? otherDateTime : this , diffed = __default ( earlier , later , units , durOpts ) ;
2021-07-01 15:56:08 +03:00
return otherIsLater ? diffed . negate ( ) : diffed ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
diffNow ( unit = "milliseconds" , opts = { } ) {
2021-12-02 10:28:09 +03:00
return this . diff ( DateTime . local ( ) , unit , opts ) ;
2021-03-22 13:42:42 +03:00
}
2021-12-02 10:28:09 +03:00
until ( otherDateTime ) {
return this . isValid ? Interval . fromDateTimes ( this , otherDateTime ) : this ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
hasSame ( otherDateTime , unit ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
if ( unit === "millisecond" ) {
return this . valueOf ( ) === otherDateTime . valueOf ( ) ;
2021-07-01 15:56:08 +03:00
} else {
2021-12-02 10:28:09 +03:00
const inputMs = otherDateTime . valueOf ( ) ;
return this . startOf ( unit ) <= inputMs && inputMs <= this . endOf ( unit ) ;
2021-07-01 15:56:08 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
equals ( other ) {
return this . isValid && other . isValid && this . valueOf ( ) === other . valueOf ( ) && this . zone . equals ( other . zone ) && this . loc . equals ( other . loc ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
toRelative ( options = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return null ;
2021-12-02 10:28:09 +03:00
const base = options . base || DateTime . fromObject ( {
2021-07-01 15:56:08 +03:00
zone : this.zone
2021-12-02 10:28:09 +03:00
} ) , padding = options . padding ? this < base ? - options.padding : options.padding : 0 ;
return diffRelative ( base , this . plus ( padding ) , Object . assign ( options , {
2021-07-01 15:56:08 +03:00
numeric : "always" ,
units : [
"years" ,
"months" ,
"days" ,
"hours" ,
"minutes" ,
"seconds"
]
} ) ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
toRelativeCalendar ( options = { } ) {
2021-07-01 15:56:08 +03:00
if ( ! this . isValid ) return null ;
2021-12-02 10:28:09 +03:00
return diffRelative ( options . base || DateTime . fromObject ( {
2021-07-01 15:56:08 +03:00
zone : this.zone
2021-12-02 10:28:09 +03:00
} ) , this , Object . assign ( options , {
2021-07-01 15:56:08 +03:00
numeric : "auto" ,
units : [
"years" ,
"months" ,
"days"
] ,
calendary : true
} ) ) ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static min ( . . . dateTimes ) {
if ( ! dateTimes . every ( DateTime . isDateTime ) ) {
throw new InvalidArgumentError ( "min requires all arguments be DateTimes" ) ;
}
2022-05-19 10:12:37 +03:00
return bestBy ( dateTimes , ( i ) = > i . valueOf ( ) , Math . min ) ;
2021-03-22 13:42:42 +03:00
}
2021-12-02 10:28:09 +03:00
static max ( . . . dateTimes ) {
if ( ! dateTimes . every ( DateTime . isDateTime ) ) {
2021-07-01 15:56:08 +03:00
throw new InvalidArgumentError ( "max requires all arguments be DateTimes" ) ;
}
2022-05-19 10:12:37 +03:00
return bestBy ( dateTimes , ( i ) = > i . valueOf ( ) , Math . max ) ;
2021-03-22 13:42:42 +03:00
}
2022-01-02 23:54:58 +03:00
static fromFormatExplain ( text , fmt , options = { } ) {
2023-06-07 08:08:35 +03:00
const { locale = null , numberingSystem = null } = options , localeToUse = Locale . fromOpts ( {
2021-09-01 16:11:55 +03:00
locale ,
2021-07-01 15:56:08 +03:00
numberingSystem ,
defaultToEN : true
} ) ;
2021-12-02 10:28:09 +03:00
return explainFromTokens ( localeToUse , text , fmt ) ;
2021-03-22 13:42:42 +03:00
}
2022-01-02 23:54:58 +03:00
static fromStringExplain ( text , fmt , options = { } ) {
2021-12-02 10:28:09 +03:00
return DateTime . fromFormatExplain ( text , fmt , options ) ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATE_SHORT() {
return DATE_SHORT ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATE_MED() {
return DATE_MED ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATE_MED_WITH_WEEKDAY() {
return DATE_MED_WITH_WEEKDAY ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATE_FULL() {
return DATE_FULL ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATE_HUGE() {
return DATE_HUGE ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_SIMPLE() {
return TIME_SIMPLE ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_WITH_SECONDS() {
return TIME_WITH_SECONDS ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_WITH_SHORT_OFFSET() {
return TIME_WITH_SHORT_OFFSET ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_WITH_LONG_OFFSET() {
return TIME_WITH_LONG_OFFSET ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_24_SIMPLE() {
return TIME_24_SIMPLE ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_24_WITH_SECONDS() {
return TIME_24_WITH_SECONDS ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_24_WITH_SHORT_OFFSET() {
return TIME_24_WITH_SHORT_OFFSET ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get TIME_24_WITH_LONG_OFFSET() {
return TIME_24_WITH_LONG_OFFSET ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_SHORT() {
return DATETIME_SHORT ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_SHORT_WITH_SECONDS() {
return DATETIME_SHORT_WITH_SECONDS ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_MED() {
return DATETIME_MED ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_MED_WITH_SECONDS() {
return DATETIME_MED_WITH_SECONDS ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_MED_WITH_WEEKDAY() {
return DATETIME_MED_WITH_WEEKDAY ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_FULL() {
return DATETIME_FULL ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_FULL_WITH_SECONDS() {
return DATETIME_FULL_WITH_SECONDS ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_HUGE() {
return DATETIME_HUGE ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get DATETIME_HUGE_WITH_SECONDS() {
return DATETIME_HUGE_WITH_SECONDS ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
}
function friendlyDateTime ( dateTimeish ) {
if ( DateTime . isDateTime ( dateTimeish ) ) {
return dateTimeish ;
} else if ( dateTimeish && dateTimeish . valueOf && isNumber ( dateTimeish . valueOf ( ) ) ) {
return DateTime . fromJSDate ( dateTimeish ) ;
} else if ( dateTimeish && typeof dateTimeish === "object" ) {
return DateTime . fromObject ( dateTimeish ) ;
} else {
throw new InvalidArgumentError ( ` Unknown datetime argument: ${ dateTimeish } , of type ${ typeof dateTimeish } ` ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
}
2022-01-02 23:54:58 +03:00
function getCachedDTF ( locString , opts = { } ) {
2021-07-01 15:56:08 +03:00
const key = JSON . stringify ( [
locString ,
opts
] ) ;
let dtf = intlDTCache [ key ] ;
if ( ! dtf ) {
dtf = new Intl . DateTimeFormat ( locString , opts ) ;
intlDTCache [ key ] = dtf ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
return dtf ;
}
2022-01-02 23:54:58 +03:00
let intlNumCache = { } ;
function getCachedINF ( locString , opts = { } ) {
2021-07-01 15:56:08 +03:00
const key = JSON . stringify ( [
locString ,
opts
] ) ;
let inf = intlNumCache [ key ] ;
if ( ! inf ) {
inf = new Intl . NumberFormat ( locString , opts ) ;
intlNumCache [ key ] = inf ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
return inf ;
}
2022-01-02 23:54:58 +03:00
let intlRelCache = { } ;
function getCachedRTF ( locString , opts = { } ) {
2023-06-07 08:08:35 +03:00
const { base , . . . cacheKeyOpts } = opts ;
2021-07-01 15:56:08 +03:00
const key = JSON . stringify ( [
locString ,
cacheKeyOpts
] ) ;
let inf = intlRelCache [ key ] ;
if ( ! inf ) {
inf = new Intl . RelativeTimeFormat ( locString , opts ) ;
intlRelCache [ key ] = inf ;
}
return inf ;
}
let sysLocaleCache = null ;
function systemLocale() {
if ( sysLocaleCache ) {
return sysLocaleCache ;
} else if ( hasIntl ( ) ) {
const computedSys = new Intl . DateTimeFormat ( ) . resolvedOptions ( ) . locale ;
sysLocaleCache = ! computedSys || computedSys === "und" ? "en-US" : computedSys ;
return sysLocaleCache ;
} else {
sysLocaleCache = "en-US" ;
return sysLocaleCache ;
}
}
function parseLocaleString ( localeStr ) {
const uIndex = localeStr . indexOf ( "-u-" ) ;
if ( uIndex === - 1 ) {
return [
localeStr
] ;
} else {
let options ;
const smaller = localeStr . substring ( 0 , uIndex ) ;
try {
options = getCachedDTF ( localeStr ) . resolvedOptions ( ) ;
} catch ( e ) {
options = getCachedDTF ( smaller ) . resolvedOptions ( ) ;
2021-01-25 15:15:42 +03:00
}
2023-06-07 08:08:35 +03:00
const { numberingSystem , calendar } = options ;
2021-07-01 15:56:08 +03:00
return [
smaller ,
numberingSystem ,
calendar
] ;
}
}
function intlConfigString ( localeStr , numberingSystem , outputCalendar ) {
if ( hasIntl ( ) ) {
if ( outputCalendar || numberingSystem ) {
localeStr += "-u" ;
if ( outputCalendar ) {
localeStr += ` -ca- ${ outputCalendar } ` ;
}
if ( numberingSystem ) {
localeStr += ` -nu- ${ numberingSystem } ` ;
}
return localeStr ;
} else {
return localeStr ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
} else {
return [ ] ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
function mapMonths ( f ) {
const ms = [ ] ;
for ( let i = 1 ; i <= 12 ; i ++ ) {
2022-06-15 17:36:57 +03:00
const dt = DateTime . utc ( 2016 , i , 1 ) ;
ms . push ( f ( dt ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
return ms ;
}
function mapWeekdays ( f ) {
const ms = [ ] ;
for ( let i = 1 ; i <= 7 ; i ++ ) {
2022-06-15 17:36:57 +03:00
const dt = DateTime . utc ( 2016 , 11 , 13 + i ) ;
ms . push ( f ( dt ) ) ;
2021-02-26 12:21:42 +03:00
}
2021-07-01 15:56:08 +03:00
return ms ;
}
function listStuff ( loc , length , defaultOK , englishFn , intlFn ) {
const mode = loc . listingMode ( defaultOK ) ;
if ( mode === "error" ) {
return null ;
} else if ( mode === "en" ) {
return englishFn ( length ) ;
} else {
return intlFn ( length ) ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
function supportsFastNumbers ( loc ) {
if ( loc . numberingSystem && loc . numberingSystem !== "latn" ) {
return false ;
} else {
return loc . numberingSystem === "latn" || ! loc . locale || loc . locale . startsWith ( "en" ) || hasIntl ( ) && new Intl . DateTimeFormat ( loc . intl ) . resolvedOptions ( ) . numberingSystem === "latn" ;
2021-03-22 13:42:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
class PolyNumberFormatter {
2021-12-02 10:28:09 +03:00
constructor ( intl , forceSimple , opts ) {
this . padTo = opts . padTo || 0 ;
this . floor = opts . floor || false ;
2021-07-01 15:56:08 +03:00
if ( ! forceSimple && hasIntl ( ) ) {
const intlOpts = {
useGrouping : false
} ;
2021-12-02 10:28:09 +03:00
if ( opts . padTo > 0 ) intlOpts . minimumIntegerDigits = opts . padTo ;
this . inf = getCachedINF ( intl , intlOpts ) ;
2021-01-25 15:15:42 +03:00
}
}
2021-12-02 10:28:09 +03:00
format ( i ) {
2021-07-01 15:56:08 +03:00
if ( this . inf ) {
2021-12-02 10:28:09 +03:00
const fixed = this . floor ? Math . floor ( i ) : i ;
2021-07-01 15:56:08 +03:00
return this . inf . format ( fixed ) ;
} else {
2023-01-06 06:14:10 +03:00
const fixed = this . floor ? Math . floor ( i ) : roundTo ( i , 3 ) ;
return padStart ( fixed , this . padTo ) ;
2021-01-25 15:15:42 +03:00
}
}
2021-07-01 15:56:08 +03:00
}
class PolyDateFormatter {
2022-06-15 17:36:57 +03:00
constructor ( dt , intl , opts ) {
2021-12-02 10:28:09 +03:00
this . opts = opts ;
2021-07-01 15:56:08 +03:00
this . hasIntl = hasIntl ( ) ;
2021-09-01 16:11:55 +03:00
let z ;
2022-06-15 17:36:57 +03:00
if ( dt . zone . universal && this . hasIntl ) {
2021-09-01 16:11:55 +03:00
z = "UTC" ;
2021-12-02 10:28:09 +03:00
if ( opts . timeZoneName ) {
2022-06-15 17:36:57 +03:00
this . dt = dt ;
2021-07-01 15:56:08 +03:00
} else {
2022-06-15 17:36:57 +03:00
this . dt = dt . offset === 0 ? dt : DateTime.fromMillis ( dt . ts + dt . offset * 60 * 1000 ) ;
2021-07-01 15:56:08 +03:00
}
2022-06-15 17:36:57 +03:00
} else if ( dt . zone . type === "local" ) {
this . dt = dt ;
2021-07-01 15:56:08 +03:00
} else {
2022-06-15 17:36:57 +03:00
this . dt = dt ;
z = dt . zone . name ;
2021-07-01 15:56:08 +03:00
}
if ( this . hasIntl ) {
2022-01-02 23:54:58 +03:00
const intlOpts = Object . assign ( { } , this . opts ) ;
2021-09-01 16:11:55 +03:00
if ( z ) {
intlOpts . timeZone = z ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
this . dtf = getCachedDTF ( intl , intlOpts ) ;
2021-07-01 15:56:08 +03:00
}
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
format() {
if ( this . hasIntl ) {
return this . dtf . format ( this . dt . toJSDate ( ) ) ;
} else {
const tokenFormat = formatString ( this . opts ) , loc = Locale . create ( "en-US" ) ;
return Formatter . create ( loc ) . formatDateTimeFromString ( this . dt , tokenFormat ) ;
}
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
formatToParts() {
if ( this . hasIntl && hasFormatToParts ( ) ) {
return this . dtf . formatToParts ( this . dt . toJSDate ( ) ) ;
} else {
return [ ] ;
2021-02-26 12:21:42 +03:00
}
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
resolvedOptions() {
if ( this . hasIntl ) {
return this . dtf . resolvedOptions ( ) ;
} else {
return {
locale : "en-US" ,
numberingSystem : "latn" ,
outputCalendar : "gregory"
} ;
}
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
class PolyRelFormatter {
2021-12-02 10:28:09 +03:00
constructor ( intl , isEnglish , opts ) {
2021-07-01 15:56:08 +03:00
this . opts = Object . assign ( {
style : "long"
2021-12-02 10:28:09 +03:00
} , opts ) ;
2021-07-01 15:56:08 +03:00
if ( ! isEnglish && hasRelative ( ) ) {
2021-12-02 10:28:09 +03:00
this . rtf = getCachedRTF ( intl , opts ) ;
2021-07-01 15:56:08 +03:00
}
2021-02-01 06:31:03 +03:00
}
2021-12-02 10:28:09 +03:00
format ( count , unit ) {
2021-07-01 15:56:08 +03:00
if ( this . rtf ) {
2021-12-02 10:28:09 +03:00
return this . rtf . format ( count , unit ) ;
2021-07-01 15:56:08 +03:00
} else {
2021-12-02 10:28:09 +03:00
return formatRelativeTime ( unit , count , this . opts . numeric , this . opts . style !== "long" ) ;
2021-07-01 15:56:08 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
formatToParts ( count , unit ) {
2021-07-01 15:56:08 +03:00
if ( this . rtf ) {
2021-12-02 10:28:09 +03:00
return this . rtf . formatToParts ( count , unit ) ;
2021-07-01 15:56:08 +03:00
} else {
return [ ] ;
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
}
class Locale {
2021-12-02 10:28:09 +03:00
static fromOpts ( opts ) {
return Locale . create ( opts . locale , opts . numberingSystem , opts . outputCalendar , opts . defaultToEN ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static create ( locale , numberingSystem , outputCalendar , defaultToEN = false ) {
const specifiedLocale = locale || Settings . defaultLocale , localeR = specifiedLocale || ( defaultToEN ? "en-US" : systemLocale ( ) ) , numberingSystemR = numberingSystem || Settings . defaultNumberingSystem , outputCalendarR = outputCalendar || Settings . defaultOutputCalendar ;
2021-07-01 15:56:08 +03:00
return new Locale ( localeR , numberingSystemR , outputCalendarR , specifiedLocale ) ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
static resetCache() {
sysLocaleCache = null ;
2022-01-02 23:54:58 +03:00
intlDTCache = { } ;
intlNumCache = { } ;
intlRelCache = { } ;
2021-05-25 08:30:17 +03:00
}
2023-06-07 08:08:35 +03:00
static fromObject ( { locale , numberingSystem , outputCalendar } = { } ) {
2021-12-02 10:28:09 +03:00
return Locale . create ( locale , numberingSystem , outputCalendar ) ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
constructor ( locale , numbering , outputCalendar , specifiedLocale ) {
const [ parsedLocale , parsedNumberingSystem , parsedOutputCalendar ] = parseLocaleString ( locale ) ;
2021-07-01 15:56:08 +03:00
this . locale = parsedLocale ;
this . numberingSystem = numbering || parsedNumberingSystem || null ;
2021-12-02 10:28:09 +03:00
this . outputCalendar = outputCalendar || parsedOutputCalendar || null ;
2021-07-01 15:56:08 +03:00
this . intl = intlConfigString ( this . locale , this . numberingSystem , this . outputCalendar ) ;
this . weekdaysCache = {
2022-01-02 23:54:58 +03:00
format : { } ,
standalone : { }
2021-07-01 15:56:08 +03:00
} ;
this . monthsCache = {
2022-01-02 23:54:58 +03:00
format : { } ,
standalone : { }
2021-07-01 15:56:08 +03:00
} ;
this . meridiemCache = null ;
2022-01-02 23:54:58 +03:00
this . eraCache = { } ;
2021-07-01 15:56:08 +03:00
this . specifiedLocale = specifiedLocale ;
this . fastNumbersCached = null ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
get fastNumbers() {
if ( this . fastNumbersCached == null ) {
this . fastNumbersCached = supportsFastNumbers ( this ) ;
}
return this . fastNumbersCached ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
listingMode ( defaultOK = true ) {
2021-09-01 16:11:55 +03:00
const intl = hasIntl ( ) , hasFTP = intl && hasFormatToParts ( ) , isActuallyEn = this . isEnglish ( ) , hasNoWeirdness = ( this . numberingSystem === null || this . numberingSystem === "latn" ) && ( this . outputCalendar === null || this . outputCalendar === "gregory" ) ;
2021-07-01 15:56:08 +03:00
if ( ! hasFTP && ! ( isActuallyEn && hasNoWeirdness ) && ! defaultOK ) {
return "error" ;
} else if ( ! hasFTP || isActuallyEn && hasNoWeirdness ) {
return "en" ;
2021-03-22 13:42:42 +03:00
} else {
2021-07-01 15:56:08 +03:00
return "intl" ;
2021-02-26 12:21:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
clone ( alts ) {
if ( ! alts || Object . getOwnPropertyNames ( alts ) . length === 0 ) {
return this ;
} else {
return Locale . create ( alts . locale || this . specifiedLocale , alts . numberingSystem || this . numberingSystem , alts . outputCalendar || this . outputCalendar , alts . defaultToEN || false ) ;
}
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
redefaultToEN ( alts = { } ) {
return this . clone ( Object . assign ( { } , alts , {
2021-07-01 15:56:08 +03:00
defaultToEN : true
2021-03-22 13:42:42 +03:00
} ) ) ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
redefaultToSystem ( alts = { } ) {
return this . clone ( Object . assign ( { } , alts , {
2021-07-01 15:56:08 +03:00
defaultToEN : false
2021-03-22 13:42:42 +03:00
} ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
months ( length , format = false , defaultOK = true ) {
return listStuff ( this , length , defaultOK , months , ( ) = > {
const intl = format ? {
2021-07-01 15:56:08 +03:00
month : length ,
day : "numeric"
} : {
month : length
2021-12-02 10:28:09 +03:00
} , formatStr = format ? "format" : "standalone" ;
2021-07-01 15:56:08 +03:00
if ( ! this . monthsCache [ formatStr ] [ length ] ) {
2022-06-15 17:36:57 +03:00
this . monthsCache [ formatStr ] [ length ] = mapMonths ( ( dt ) = > this . extract ( dt , intl , "month" ) ) ;
2021-07-01 15:56:08 +03:00
}
return this . monthsCache [ formatStr ] [ length ] ;
2021-03-22 13:42:42 +03:00
} ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
weekdays ( length , format = false , defaultOK = true ) {
return listStuff ( this , length , defaultOK , weekdays , ( ) = > {
const intl = format ? {
weekday : length ,
2021-07-01 15:56:08 +03:00
year : "numeric" ,
month : "long" ,
day : "numeric"
} : {
2021-12-02 10:28:09 +03:00
weekday : length
} , formatStr = format ? "format" : "standalone" ;
if ( ! this . weekdaysCache [ formatStr ] [ length ] ) {
2022-06-15 17:36:57 +03:00
this . weekdaysCache [ formatStr ] [ length ] = mapWeekdays ( ( dt ) = > this . extract ( dt , intl , "weekday" ) ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
return this . weekdaysCache [ formatStr ] [ length ] ;
2021-07-01 15:56:08 +03:00
} ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
meridiems ( defaultOK = true ) {
2022-05-19 10:12:37 +03:00
return listStuff ( this , undefined , defaultOK , ( ) = > meridiems , ( ) = > {
2021-07-01 15:56:08 +03:00
if ( ! this . meridiemCache ) {
2021-09-01 16:11:55 +03:00
const intl = {
2021-07-01 15:56:08 +03:00
hour : "numeric" ,
hour12 : true
} ;
this . meridiemCache = [
DateTime . utc ( 2016 , 11 , 13 , 9 ) ,
DateTime . utc ( 2016 , 11 , 13 , 19 )
2022-06-15 17:36:57 +03:00
] . map ( ( dt ) = > this . extract ( dt , intl , "dayperiod" ) ) ;
2021-07-01 15:56:08 +03:00
}
return this . meridiemCache ;
} ) ;
2021-01-25 15:15:42 +03:00
}
2021-12-02 10:28:09 +03:00
eras ( length , defaultOK = true ) {
return listStuff ( this , length , defaultOK , eras , ( ) = > {
2021-09-01 16:11:55 +03:00
const intl = {
2021-12-02 10:28:09 +03:00
era : length
2021-07-01 15:56:08 +03:00
} ;
2021-12-02 10:28:09 +03:00
if ( ! this . eraCache [ length ] ) {
this . eraCache [ length ] = [
2021-07-01 15:56:08 +03:00
DateTime . utc ( - 40 , 1 , 1 ) ,
DateTime . utc ( 2017 , 1 , 1 )
2022-06-15 17:36:57 +03:00
] . map ( ( dt ) = > this . extract ( dt , intl , "era" ) ) ;
2021-07-01 15:56:08 +03:00
}
2021-12-02 10:28:09 +03:00
return this . eraCache [ length ] ;
2021-07-01 15:56:08 +03:00
} ) ;
2021-01-25 15:15:42 +03:00
}
2022-06-15 17:36:57 +03:00
extract ( dt , intlOpts , field ) {
const df = this . dtFormatter ( dt , intlOpts ) , results = df . formatToParts ( ) , matching = results . find ( ( m ) = > m . type . toLowerCase ( ) === field ) ;
2021-07-01 15:56:08 +03:00
return matching ? matching.value : null ;
2021-02-22 11:27:40 +03:00
}
2022-01-02 23:54:58 +03:00
numberFormatter ( opts = { } ) {
2021-12-02 10:28:09 +03:00
return new PolyNumberFormatter ( this . intl , opts . forceSimple || this . fastNumbers , opts ) ;
2021-01-25 15:15:42 +03:00
}
2022-06-15 17:36:57 +03:00
dtFormatter ( dt , intlOpts = { } ) {
return new PolyDateFormatter ( dt , this . intl , intlOpts ) ;
2021-01-25 15:15:42 +03:00
}
2022-01-02 23:54:58 +03:00
relFormatter ( opts = { } ) {
2021-12-02 10:28:09 +03:00
return new PolyRelFormatter ( this . intl , this . isEnglish ( ) , opts ) ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
isEnglish() {
return this . locale === "en" || this . locale . toLowerCase ( ) === "en-us" || hasIntl ( ) && new Intl . DateTimeFormat ( this . intl ) . resolvedOptions ( ) . locale . startsWith ( "en-us" ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
equals ( other ) {
return this . locale === other . locale && this . numberingSystem === other . numberingSystem && this . outputCalendar === other . outputCalendar ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
}
class Settings {
static get now() {
return now ;
2021-01-25 15:15:42 +03:00
}
2022-06-15 17:36:57 +03:00
static set now ( n ) {
now = n ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get defaultZoneName() {
return Settings . defaultZone . name ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static set defaultZoneName ( z ) {
if ( ! z ) {
2021-12-03 19:55:27 +03:00
defaultZone = null ;
2021-07-01 15:56:08 +03:00
} else {
2021-12-03 19:55:27 +03:00
defaultZone = normalizeZone ( z ) ;
2021-07-01 15:56:08 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get defaultZone() {
2021-12-03 19:55:27 +03:00
return defaultZone || LocalZone . instance ;
2021-02-22 11:27:40 +03:00
}
2021-07-01 15:56:08 +03:00
static get defaultLocale() {
return defaultLocale ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static set defaultLocale ( locale ) {
defaultLocale = locale ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get defaultNumberingSystem() {
return defaultNumberingSystem ;
2021-01-25 15:15:42 +03:00
}
2021-12-02 10:28:09 +03:00
static set defaultNumberingSystem ( numberingSystem ) {
defaultNumberingSystem = numberingSystem ;
2021-01-25 15:15:42 +03:00
}
2021-07-01 15:56:08 +03:00
static get defaultOutputCalendar() {
return defaultOutputCalendar ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static set defaultOutputCalendar ( outputCalendar ) {
defaultOutputCalendar = outputCalendar ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
static get throwOnInvalid() {
return throwOnInvalid ;
2021-05-25 08:30:17 +03:00
}
2021-12-02 10:28:09 +03:00
static set throwOnInvalid ( t ) {
throwOnInvalid = t ;
2021-05-25 08:30:17 +03:00
}
2021-07-01 15:56:08 +03:00
static resetCaches() {
Locale . resetCache ( ) ;
IANAZone . resetCache ( ) ;
2021-02-26 12:21:42 +03:00
}
}
function validateStartEnd ( start , end ) {
if ( ! start || ! start . isValid ) {
return Interval . invalid ( "missing or invalid start" ) ;
} else if ( ! end || ! end . isValid ) {
return Interval . invalid ( "missing or invalid end" ) ;
} else if ( end < start ) {
return Interval . invalid ( "end before start" , ` The end of an interval must be after its start, but you had start= ${ start . toISO ( ) } and end= ${ end . toISO ( ) } ` ) ;
} else {
return null ;
}
}
class Interval {
2021-12-02 10:28:09 +03:00
constructor ( config ) {
this . s = config . start ;
this . e = config . end ;
this . invalid = config . invalid || null ;
2021-02-26 12:21:42 +03:00
this . isLuxonInterval = true ;
}
2021-12-02 10:28:09 +03:00
static invalid ( reason , explanation = null ) {
if ( ! reason ) {
2021-02-26 12:21:42 +03:00
throw new InvalidArgumentError ( "need to specify a reason the Interval is invalid" ) ;
}
2021-12-02 10:28:09 +03:00
const invalid = reason instanceof Invalid ? reason : new Invalid ( reason , explanation ) ;
2021-02-26 12:21:42 +03:00
if ( Settings . throwOnInvalid ) {
2021-09-01 16:11:55 +03:00
throw new InvalidIntervalError ( invalid ) ;
2021-01-25 15:15:42 +03:00
} else {
2021-02-26 12:21:42 +03:00
return new Interval ( {
2021-09-01 16:11:55 +03:00
invalid
2021-02-22 11:27:40 +03:00
} ) ;
2021-01-25 15:15:42 +03:00
}
}
2021-02-26 12:21:42 +03:00
static fromDateTimes ( start , end ) {
const builtStart = friendlyDateTime ( start ) , builtEnd = friendlyDateTime ( end ) ;
const validateError = validateStartEnd ( builtStart , builtEnd ) ;
if ( validateError == null ) {
return new Interval ( {
start : builtStart ,
end : builtEnd
} ) ;
2021-01-25 15:15:42 +03:00
} else {
2021-02-26 12:21:42 +03:00
return validateError ;
2021-01-25 15:15:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static after ( start , duration ) {
2022-06-15 17:36:57 +03:00
const dur = friendlyDuration ( duration ) , dt = friendlyDateTime ( start ) ;
return Interval . fromDateTimes ( dt , dt . plus ( dur ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static before ( end , duration ) {
2022-06-15 17:36:57 +03:00
const dur = friendlyDuration ( duration ) , dt = friendlyDateTime ( end ) ;
return Interval . fromDateTimes ( dt . minus ( dur ) , dt ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static fromISO ( text , opts ) {
2022-06-15 17:36:57 +03:00
const [ s , e ] = ( text || "" ) . split ( "/" , 2 ) ;
if ( s && e ) {
2021-02-26 12:21:42 +03:00
let start , startIsValid ;
try {
2022-06-15 17:36:57 +03:00
start = DateTime . fromISO ( s , opts ) ;
2021-02-26 12:21:42 +03:00
startIsValid = start . isValid ;
2023-01-06 06:14:10 +03:00
} catch ( e ) {
2021-02-26 12:21:42 +03:00
startIsValid = false ;
}
let end , endIsValid ;
try {
2021-12-02 10:28:09 +03:00
end = DateTime . fromISO ( e , opts ) ;
2021-02-26 12:21:42 +03:00
endIsValid = end . isValid ;
2023-01-06 06:14:10 +03:00
} catch ( e ) {
2021-02-26 12:21:42 +03:00
endIsValid = false ;
}
if ( startIsValid && endIsValid ) {
return Interval . fromDateTimes ( start , end ) ;
}
if ( startIsValid ) {
2021-12-02 10:28:09 +03:00
const dur = Duration . fromISO ( e , opts ) ;
2021-02-26 12:21:42 +03:00
if ( dur . isValid ) {
return Interval . after ( start , dur ) ;
}
} else if ( endIsValid ) {
2023-01-06 06:14:10 +03:00
const dur = Duration . fromISO ( s , opts ) ;
if ( dur . isValid ) {
return Interval . before ( end , dur ) ;
2021-02-26 12:21:42 +03:00
}
}
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
return Interval . invalid ( "unparsable" , ` the input " ${ text } " can't be parsed as ISO 8601 ` ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static isInterval ( o ) {
return o && o . isLuxonInterval || false ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
get start() {
return this . isValid ? this . s : null ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
get end() {
return this . isValid ? this . e : null ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
get isValid() {
return this . invalidReason === null ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
get invalidReason() {
return this . invalid ? this . invalid.reason : null ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
get invalidExplanation() {
return this . invalid ? this . invalid.explanation : null ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
length ( unit = "milliseconds" ) {
2021-02-26 12:21:42 +03:00
return this . isValid ? this . toDuration ( . . . [
2021-12-02 10:28:09 +03:00
unit
] ) . get ( unit ) : NaN ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
count ( unit = "milliseconds" ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return NaN ;
2021-12-02 10:28:09 +03:00
const start = this . start . startOf ( unit ) , end = this . end . startOf ( unit ) ;
return Math . floor ( end . diff ( start , unit ) . get ( unit ) ) + 1 ;
2021-01-25 15:15:42 +03:00
}
2021-12-02 10:28:09 +03:00
hasSame ( unit ) {
return this . isValid ? this . isEmpty ( ) || this . e . minus ( 1 ) . hasSame ( this . s , unit ) : false ;
2021-01-25 15:15:42 +03:00
}
2021-02-26 12:21:42 +03:00
isEmpty() {
return this . s . valueOf ( ) === this . e . valueOf ( ) ;
}
isAfter ( dateTime ) {
if ( ! this . isValid ) return false ;
return this . s > dateTime ;
}
2021-12-02 10:28:09 +03:00
isBefore ( dateTime ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
return this . e <= dateTime ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
contains ( dateTime ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
return this . s <= dateTime && this . e > dateTime ;
2021-02-22 11:27:40 +03:00
}
2023-06-07 08:08:35 +03:00
set ( { start , end } = { } ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return this ;
2021-12-02 10:28:09 +03:00
return Interval . fromDateTimes ( start || this . s , end || this . e ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
splitAt ( . . . dateTimes ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return [ ] ;
2022-05-19 10:12:37 +03:00
const sorted = dateTimes . map ( friendlyDateTime ) . filter ( ( d ) = > this . contains ( d ) ) . sort ( ) , results = [ ] ;
2023-06-07 08:08:35 +03:00
let { s } = this , i = 0 ;
2022-06-15 17:36:57 +03:00
while ( s < this . e ) {
2021-02-26 12:21:42 +03:00
const added = sorted [ i ] || this . e , next = + added > + this . e ? this . e : added ;
2022-06-15 17:36:57 +03:00
results . push ( Interval . fromDateTimes ( s , next ) ) ;
s = next ;
2021-02-26 12:21:42 +03:00
i += 1 ;
2021-01-25 15:15:42 +03:00
}
2021-02-26 12:21:42 +03:00
return results ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
splitBy ( duration ) {
const dur = friendlyDuration ( duration ) ;
2021-02-26 12:21:42 +03:00
if ( ! this . isValid || ! dur . isValid || dur . as ( "milliseconds" ) === 0 ) {
return [ ] ;
}
2023-06-07 08:08:35 +03:00
let { s } = this , added , next ;
2021-02-26 12:21:42 +03:00
const results = [ ] ;
2022-06-15 17:36:57 +03:00
while ( s < this . e ) {
added = s . plus ( dur ) ;
2021-02-26 12:21:42 +03:00
next = + added > + this . e ? this . e : added ;
2022-06-15 17:36:57 +03:00
results . push ( Interval . fromDateTimes ( s , next ) ) ;
s = next ;
2021-02-26 12:21:42 +03:00
}
return results ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
divideEqually ( numberOfParts ) {
if ( ! this . isValid ) return [ ] ;
return this . splitBy ( this . length ( ) / numberOfParts ) . slice ( 0 , numberOfParts ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
overlaps ( other ) {
return this . e > other . s && this . s < other . e ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
abutsStart ( other ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
return + this . e === + other . s ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
abutsEnd ( other ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
return + other . e === + this . s ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
engulfs ( other ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return false ;
2021-12-02 10:28:09 +03:00
return this . s <= other . s && this . e >= other . e ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
equals ( other ) {
if ( ! this . isValid || ! other . isValid ) {
2021-02-26 12:21:42 +03:00
return false ;
2021-01-25 15:15:42 +03:00
}
2021-12-02 10:28:09 +03:00
return this . s . equals ( other . s ) && this . e . equals ( other . e ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
intersection ( other ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return this ;
2022-06-15 17:36:57 +03:00
const s = this . s > other . s ? this . s : other.s , e = this . e < other . e ? this . e : other.e ;
if ( s > e ) {
2021-02-26 12:21:42 +03:00
return null ;
} else {
2022-06-15 17:36:57 +03:00
return Interval . fromDateTimes ( s , e ) ;
2021-01-25 15:15:42 +03:00
}
}
2021-12-02 10:28:09 +03:00
union ( other ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return this ;
2022-06-15 17:36:57 +03:00
const s = this . s < other . s ? this . s : other.s , e = this . e > other . e ? this . e : other.e ;
return Interval . fromDateTimes ( s , e ) ;
2021-01-25 15:15:42 +03:00
}
2021-02-26 12:21:42 +03:00
static merge ( intervals ) {
2022-05-19 10:12:37 +03:00
const [ found , __final ] = intervals . sort ( ( a , b ) = > a . s - b . s ) . reduce ( ( [ sofar , current ] , item ) = > {
2021-02-26 12:21:42 +03:00
if ( ! current ) {
return [
sofar ,
item
] ;
} else if ( current . overlaps ( item ) || current . abutsStart ( item ) ) {
return [
sofar ,
current . union ( item )
] ;
} else {
return [
sofar . concat ( [
current
] ) ,
item
] ;
}
} , [
[ ] ,
null
] ) ;
if ( __final ) {
found . push ( __final ) ;
}
return found ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
static xor ( intervals ) {
2021-02-26 12:21:42 +03:00
let start = null , currentCount = 0 ;
2021-12-02 10:28:09 +03:00
const results = [ ] , ends = intervals . map ( ( i ) = > [
2021-02-26 12:21:42 +03:00
{
time : i.s ,
type : "s"
} ,
{
time : i.e ,
type : "e"
}
2022-05-19 10:12:37 +03:00
] ) , flattened = Array . prototype . concat ( . . . ends ) , arr = flattened . sort ( ( a , b ) = > a . time - b . time ) ;
2022-06-15 17:36:57 +03:00
for ( const i of arr ) {
currentCount += i . type === "s" ? 1 : - 1 ;
2021-02-26 12:21:42 +03:00
if ( currentCount === 1 ) {
2022-06-15 17:36:57 +03:00
start = i . time ;
2021-02-26 12:21:42 +03:00
} else {
2022-06-15 17:36:57 +03:00
if ( start && + start !== + i . time ) {
results . push ( Interval . fromDateTimes ( start , i . time ) ) ;
2021-02-26 12:21:42 +03:00
}
start = null ;
}
2021-01-25 15:15:42 +03:00
}
2021-02-26 12:21:42 +03:00
return Interval . merge ( results ) ;
2021-01-25 15:15:42 +03:00
}
2021-12-02 10:28:09 +03:00
difference ( . . . intervals ) {
2021-02-26 12:21:42 +03:00
return Interval . xor ( [
this
2022-05-19 10:12:37 +03:00
] . concat ( intervals ) ) . map ( ( i ) = > this . intersection ( i ) ) . filter ( ( i ) = > i && ! i . isEmpty ( ) ) ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
toString() {
if ( ! this . isValid ) return INVALID2 ;
return ` [ ${ this . s . toISO ( ) } – ${ this . e . toISO ( ) } ) ` ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
toISO ( opts ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return INVALID2 ;
2021-12-02 10:28:09 +03:00
return ` ${ this . s . toISO ( opts ) } / ${ this . e . toISO ( opts ) } ` ;
2021-02-26 12:21:42 +03:00
}
toISODate() {
if ( ! this . isValid ) return INVALID2 ;
return ` ${ this . s . toISODate ( ) } / ${ this . e . toISODate ( ) } ` ;
}
2021-12-02 10:28:09 +03:00
toISOTime ( opts ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return INVALID2 ;
2021-12-02 10:28:09 +03:00
return ` ${ this . s . toISOTime ( opts ) } / ${ this . e . toISOTime ( opts ) } ` ;
2021-02-26 12:21:42 +03:00
}
2023-06-07 08:08:35 +03:00
toFormat ( dateFormat , { separator = " – " } = { } ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) return INVALID2 ;
return ` ${ this . s . toFormat ( dateFormat ) } ${ separator } ${ this . e . toFormat ( dateFormat ) } ` ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
toDuration ( unit , opts ) {
2021-02-26 12:21:42 +03:00
if ( ! this . isValid ) {
return Duration . invalid ( this . invalidReason ) ;
2021-02-22 11:27:40 +03:00
}
2021-12-02 10:28:09 +03:00
return this . e . diff ( this . s , unit , opts ) ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
mapEndpoints ( mapFn ) {
return Interval . fromDateTimes ( mapFn ( this . s ) , mapFn ( this . e ) ) ;
}
}
function highOrderDiffs ( cursor , later , units ) {
const differs = [
[
"years" ,
( a , b ) = > b . year - a . year
] ,
[
"months" ,
( a , b ) = > b . month - a . month + ( b . year - a . year ) * 12
] ,
[
"weeks" ,
( a , b ) = > {
const days = dayDiff ( a , b ) ;
return ( days - days % 7 ) / 7 ;
}
] ,
[
"days" ,
dayDiff
]
] ;
2022-01-02 23:54:58 +03:00
const results = { } ;
2021-02-26 12:21:42 +03:00
let lowestOrder , highWater ;
2021-09-01 16:11:55 +03:00
for ( const [ unit , differ ] of differs ) {
if ( units . indexOf ( unit ) >= 0 ) {
lowestOrder = unit ;
2021-02-26 12:21:42 +03:00
let delta = differ ( cursor , later ) ;
highWater = cursor . plus ( {
2021-09-01 16:11:55 +03:00
[ unit ] : delta
2021-02-26 12:21:42 +03:00
} ) ;
if ( highWater > later ) {
cursor = cursor . plus ( {
2021-09-01 16:11:55 +03:00
[ unit ] : delta - 1
2021-02-26 12:21:42 +03:00
} ) ;
delta -= 1 ;
} else {
cursor = highWater ;
}
2021-09-01 16:11:55 +03:00
results [ unit ] = delta ;
2021-02-26 12:21:42 +03:00
}
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
return [
cursor ,
results ,
highWater ,
lowestOrder
] ;
}
2021-09-01 16:11:55 +03:00
function __default ( earlier , later , units , opts ) {
2021-02-26 12:21:42 +03:00
let [ cursor , results , highWater , lowestOrder ] = highOrderDiffs ( earlier , later , units ) ;
const remainingMillis = later - cursor ;
const lowerOrderUnits = units . filter ( ( u ) = > [
"hours" ,
"minutes" ,
"seconds" ,
"milliseconds"
2022-05-19 10:12:37 +03:00
] . indexOf ( u ) >= 0 ) ;
2021-02-26 12:21:42 +03:00
if ( lowerOrderUnits . length === 0 ) {
if ( highWater < later ) {
highWater = cursor . plus ( {
[ lowestOrder ] : 1
} ) ;
}
if ( highWater !== cursor ) {
results [ lowestOrder ] = ( results [ lowestOrder ] || 0 ) + remainingMillis / ( highWater - cursor ) ;
}
2021-02-22 11:27:40 +03:00
}
2021-09-01 16:11:55 +03:00
const duration = Duration . fromObject ( Object . assign ( results , opts ) ) ;
2021-02-26 12:21:42 +03:00
if ( lowerOrderUnits . length > 0 ) {
2021-09-01 16:11:55 +03:00
return Duration . fromMillis ( remainingMillis , opts ) . shiftTo ( . . . lowerOrderUnits ) . plus ( duration ) ;
2021-02-26 12:21:42 +03:00
} else {
return duration ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
}
2022-05-19 10:12:37 +03:00
function intUnit ( regex , post = ( i ) = > i ) {
2021-02-26 12:21:42 +03:00
return {
regex ,
2022-06-15 17:36:57 +03:00
deser : ( [ s ] ) = > post ( parseDigits ( s ) )
2021-02-26 12:21:42 +03:00
} ;
}
const NBSP = String . fromCharCode ( 160 ) ;
const spaceOrNBSP = ` ( | ${ NBSP } ) ` ;
const spaceOrNBSPRegExp = new RegExp ( spaceOrNBSP , "g" ) ;
2022-06-15 17:36:57 +03:00
function fixListRegex ( s ) {
return s . replace ( /\./g , "\\.?" ) . replace ( spaceOrNBSPRegExp , spaceOrNBSP ) ;
2021-02-26 12:21:42 +03:00
}
2022-06-15 17:36:57 +03:00
function stripInsensitivities ( s ) {
return s . replace ( /\./g , "" ) . replace ( spaceOrNBSPRegExp , " " ) . toLowerCase ( ) ;
2021-02-26 12:21:42 +03:00
}
function oneOf ( strings , startIndex ) {
if ( strings === null ) {
return null ;
} else {
return {
regex : RegExp ( strings . map ( fixListRegex ) . join ( "|" ) ) ,
2022-06-15 17:36:57 +03:00
deser : ( [ s ] ) = > strings . findIndex ( ( i ) = > stripInsensitivities ( s ) === stripInsensitivities ( i ) ) + startIndex
2021-02-26 12:21:42 +03:00
} ;
}
}
2021-12-03 19:55:27 +03:00
function offset ( regex , groups ) {
2021-02-26 12:21:42 +03:00
return {
regex ,
2022-05-19 10:12:37 +03:00
deser : ( [ , h , m ] ) = > signedOffset ( h , m ) ,
2021-02-26 12:21:42 +03:00
groups
} ;
}
function simple ( regex ) {
return {
regex ,
2022-06-15 17:36:57 +03:00
deser : ( [ s ] ) = > s
2021-02-26 12:21:42 +03:00
} ;
}
function escapeToken ( value ) {
return value . replace ( /[\-\[\]{}()*+?.,\\\^$|#\s]/g , "\\$&" ) ;
}
function unitForToken ( token , loc ) {
const one = digitRegex ( loc ) , two = digitRegex ( loc , "{2}" ) , three = digitRegex ( loc , "{3}" ) , four = digitRegex ( loc , "{4}" ) , six = digitRegex ( loc , "{6}" ) , oneOrTwo = digitRegex ( loc , "{1,2}" ) , oneToThree = digitRegex ( loc , "{1,3}" ) , oneToSix = digitRegex ( loc , "{1,6}" ) , oneToNine = digitRegex ( loc , "{1,9}" ) , twoToFour = digitRegex ( loc , "{2,4}" ) , fourToSix = digitRegex ( loc , "{4,6}" ) , literal = ( t ) = > ( {
regex : RegExp ( escapeToken ( t . val ) ) ,
2022-06-15 17:36:57 +03:00
deser : ( [ s ] ) = > s ,
2021-02-26 12:21:42 +03:00
literal : true
2022-05-19 10:12:37 +03:00
} ) , unitate = ( t ) = > {
2021-02-26 12:21:42 +03:00
if ( token . literal ) {
return literal ( t ) ;
}
switch ( t . val ) {
case "G" :
return oneOf ( loc . eras ( "short" , false ) , 0 ) ;
case "GG" :
return oneOf ( loc . eras ( "long" , false ) , 0 ) ;
case "y" :
return intUnit ( oneToSix ) ;
case "yy" :
return intUnit ( twoToFour , untruncateYear ) ;
case "yyyy" :
return intUnit ( four ) ;
case "yyyyy" :
return intUnit ( fourToSix ) ;
case "yyyyyy" :
return intUnit ( six ) ;
case "M" :
return intUnit ( oneOrTwo ) ;
case "MM" :
return intUnit ( two ) ;
case "MMM" :
return oneOf ( loc . months ( "short" , true , false ) , 1 ) ;
case "MMMM" :
return oneOf ( loc . months ( "long" , true , false ) , 1 ) ;
case "L" :
return intUnit ( oneOrTwo ) ;
case "LL" :
return intUnit ( two ) ;
case "LLL" :
return oneOf ( loc . months ( "short" , false , false ) , 1 ) ;
case "LLLL" :
return oneOf ( loc . months ( "long" , false , false ) , 1 ) ;
case "d" :
return intUnit ( oneOrTwo ) ;
case "dd" :
return intUnit ( two ) ;
case "o" :
return intUnit ( oneToThree ) ;
case "ooo" :
return intUnit ( three ) ;
case "HH" :
return intUnit ( two ) ;
case "H" :
return intUnit ( oneOrTwo ) ;
case "hh" :
return intUnit ( two ) ;
case "h" :
return intUnit ( oneOrTwo ) ;
case "mm" :
return intUnit ( two ) ;
case "m" :
return intUnit ( oneOrTwo ) ;
case "q" :
return intUnit ( oneOrTwo ) ;
case "qq" :
return intUnit ( two ) ;
case "s" :
return intUnit ( oneOrTwo ) ;
case "ss" :
return intUnit ( two ) ;
case "S" :
return intUnit ( oneToThree ) ;
case "SSS" :
return intUnit ( three ) ;
case "u" :
return simple ( oneToNine ) ;
case "a" :
return oneOf ( loc . meridiems ( ) , 0 ) ;
case "kkkk" :
return intUnit ( four ) ;
case "kk" :
return intUnit ( twoToFour , untruncateYear ) ;
case "W" :
return intUnit ( oneOrTwo ) ;
case "WW" :
return intUnit ( two ) ;
case "E" :
case "c" :
return intUnit ( one ) ;
case "EEE" :
return oneOf ( loc . weekdays ( "short" , false , false ) , 1 ) ;
case "EEEE" :
return oneOf ( loc . weekdays ( "long" , false , false ) , 1 ) ;
case "ccc" :
return oneOf ( loc . weekdays ( "short" , true , false ) , 1 ) ;
case "cccc" :
return oneOf ( loc . weekdays ( "long" , true , false ) , 1 ) ;
case "Z" :
case "ZZ" :
2021-12-03 19:55:27 +03:00
return offset ( new RegExp ( ` ([+-] ${ oneOrTwo . source } )(?::( ${ two . source } ))? ` ) , 2 ) ;
2021-02-26 12:21:42 +03:00
case "ZZZ" :
2021-12-03 19:55:27 +03:00
return offset ( new RegExp ( ` ([+-] ${ oneOrTwo . source } )( ${ two . source } )? ` ) , 2 ) ;
2021-02-26 12:21:42 +03:00
case "z" :
return simple ( /[a-z_+-/]{1,256}?/i ) ;
default :
return literal ( t ) ;
}
} ;
2021-09-01 16:11:55 +03:00
const unit = unitate ( token ) || {
2021-02-26 12:21:42 +03:00
invalidReason : MISSING_FTP
} ;
2021-09-01 16:11:55 +03:00
unit . token = token ;
return unit ;
2021-02-26 12:21:42 +03:00
}
const partTypeStyleToTokenVal = {
year : {
"2-digit" : "yy" ,
numeric : "yyyyy"
} ,
month : {
numeric : "M" ,
"2-digit" : "MM" ,
short : "MMM" ,
long : "MMMM"
} ,
day : {
numeric : "d" ,
"2-digit" : "dd"
} ,
weekday : {
short : "EEE" ,
long : "EEEE"
} ,
dayperiod : "a" ,
dayPeriod : "a" ,
hour : {
numeric : "h" ,
"2-digit" : "hh"
} ,
minute : {
numeric : "m" ,
"2-digit" : "mm"
} ,
second : {
numeric : "s" ,
"2-digit" : "ss"
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
} ;
2021-09-01 16:11:55 +03:00
function tokenForPart ( part , locale , formatOpts ) {
2023-06-07 08:08:35 +03:00
const { type , value } = part ;
2021-02-26 12:21:42 +03:00
if ( type === "literal" ) {
return {
literal : true ,
val : value
} ;
2021-02-22 11:27:40 +03:00
}
2021-09-01 16:11:55 +03:00
const style = formatOpts [ type ] ;
2021-02-26 12:21:42 +03:00
let val = partTypeStyleToTokenVal [ type ] ;
if ( typeof val === "object" ) {
val = val [ style ] ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
if ( val ) {
return {
literal : false ,
val
} ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
return undefined ;
}
function buildRegex ( units ) {
2022-05-19 10:12:37 +03:00
const re = units . map ( ( u ) = > u . regex ) . reduce ( ( f , r ) = > ` ${ f } ( ${ r . source } ) ` , "" ) ;
2021-02-26 12:21:42 +03:00
return [
` ^ ${ re } $ ` ,
units
] ;
}
2021-12-03 19:55:27 +03:00
function match ( input , regex , handlers ) {
2021-02-26 12:21:42 +03:00
const matches = input . match ( regex ) ;
if ( matches ) {
2022-01-02 23:54:58 +03:00
const all = { } ;
2021-02-26 12:21:42 +03:00
let matchIndex = 1 ;
for ( const i in handlers ) {
if ( hasOwnProperty ( handlers , i ) ) {
const h = handlers [ i ] , groups = h . groups ? h . groups + 1 : 1 ;
if ( ! h . literal && h . token ) {
all [ h . token . val [ 0 ] ] = h . deser ( matches . slice ( matchIndex , matchIndex + groups ) ) ;
}
matchIndex += groups ;
}
}
return [
matches ,
all
] ;
} else {
return [
matches ,
2022-01-02 23:54:58 +03:00
{ }
2021-02-26 12:21:42 +03:00
] ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
}
function dateTimeFromMatches ( matches ) {
const toField = ( token ) = > {
switch ( token ) {
case "S" :
return "millisecond" ;
case "s" :
return "second" ;
case "m" :
return "minute" ;
case "h" :
case "H" :
return "hour" ;
case "d" :
return "day" ;
case "o" :
return "ordinal" ;
case "L" :
case "M" :
return "month" ;
case "y" :
return "year" ;
case "E" :
case "c" :
return "weekday" ;
case "W" :
return "weekNumber" ;
case "k" :
return "weekYear" ;
case "q" :
return "quarter" ;
default :
return null ;
}
} ;
2021-09-01 16:11:55 +03:00
let zone ;
2021-02-26 12:21:42 +03:00
if ( ! isUndefined ( matches . Z ) ) {
2021-09-01 16:11:55 +03:00
zone = new FixedOffsetZone ( matches . Z ) ;
2021-02-26 12:21:42 +03:00
} else if ( ! isUndefined ( matches . z ) ) {
2021-09-01 16:11:55 +03:00
zone = IANAZone . create ( matches . z ) ;
2021-02-26 12:21:42 +03:00
} else {
2021-09-01 16:11:55 +03:00
zone = null ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
if ( ! isUndefined ( matches . q ) ) {
matches . M = ( matches . q - 1 ) * 3 + 1 ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
if ( ! isUndefined ( matches . h ) ) {
if ( matches . h < 12 && matches . a === 1 ) {
matches . h += 12 ;
} else if ( matches . h === 12 && matches . a === 0 ) {
matches . h = 0 ;
}
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
if ( matches . G === 0 && matches . y ) {
matches . y = - matches . y ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
if ( ! isUndefined ( matches . u ) ) {
matches . S = parseMillis ( matches . u ) ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
const vals = Object . keys ( matches ) . reduce ( ( r , k ) = > {
const f = toField ( k ) ;
if ( f ) {
r [ f ] = matches [ k ] ;
}
return r ;
2022-01-02 23:54:58 +03:00
} , { } ) ;
2021-02-26 12:21:42 +03:00
return [
vals ,
2021-09-01 16:11:55 +03:00
zone
2021-02-26 12:21:42 +03:00
] ;
}
let dummyDateTimeCache = null ;
function getDummyDateTime() {
if ( ! dummyDateTimeCache ) {
dummyDateTimeCache = DateTime . fromMillis ( 1555555555555 ) ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
return dummyDateTimeCache ;
}
2021-09-01 16:11:55 +03:00
function maybeExpandMacroToken ( token , locale ) {
2021-02-26 12:21:42 +03:00
if ( token . literal ) {
return token ;
2021-02-22 11:27:40 +03:00
}
2021-09-01 16:11:55 +03:00
const formatOpts = Formatter . macroTokenToFormatOpts ( token . val ) ;
if ( ! formatOpts ) {
2021-02-26 12:21:42 +03:00
return token ;
2021-02-22 11:27:40 +03:00
}
2021-09-01 16:11:55 +03:00
const formatter = Formatter . create ( locale , formatOpts ) ;
2021-02-26 12:21:42 +03:00
const parts = formatter . formatDateTimeParts ( getDummyDateTime ( ) ) ;
2022-05-19 10:12:37 +03:00
const tokens = parts . map ( ( p ) = > tokenForPart ( p , locale , formatOpts ) ) ;
2021-02-26 12:21:42 +03:00
if ( tokens . includes ( undefined ) ) {
return token ;
2021-02-22 11:27:40 +03:00
}
2021-02-26 12:21:42 +03:00
return tokens ;
2021-02-22 11:27:40 +03:00
}
2021-09-01 16:11:55 +03:00
function expandMacroTokens ( tokens , locale ) {
2022-05-19 10:12:37 +03:00
return Array . prototype . concat ( . . . tokens . map ( ( t ) = > maybeExpandMacroToken ( t , locale ) ) ) ;
2021-02-26 12:21:42 +03:00
}
2021-09-01 16:11:55 +03:00
function explainFromTokens ( locale , input , format ) {
2022-05-19 10:12:37 +03:00
const tokens = expandMacroTokens ( Formatter . parseFormat ( format ) , locale ) , units = tokens . map ( ( t ) = > unitForToken ( t , locale ) ) , disqualifyingUnit = units . find ( ( t ) = > t . invalidReason ) ;
2021-02-26 12:21:42 +03:00
if ( disqualifyingUnit ) {
return {
input ,
tokens ,
invalidReason : disqualifyingUnit.invalidReason
} ;
2021-02-22 11:27:40 +03:00
} else {
2021-12-03 19:55:27 +03:00
const [ regexString , handlers ] = buildRegex ( units ) , regex = RegExp ( regexString , "i" ) , [ rawMatches , matches ] = match ( input , regex , handlers ) , [ result , zone ] = matches ? dateTimeFromMatches ( matches ) : [
2021-02-26 12:21:42 +03:00
null ,
null
] ;
if ( hasOwnProperty ( matches , "a" ) && hasOwnProperty ( matches , "H" ) ) {
throw new ConflictingSpecificationError ( "Can't include meridiem when specifying 24-hour format" ) ;
}
return {
input ,
tokens ,
regex ,
rawMatches ,
matches ,
result ,
2021-09-01 16:11:55 +03:00
zone
2021-02-26 12:21:42 +03:00
} ;
2021-02-22 11:27:40 +03:00
}
}
2021-09-01 16:11:55 +03:00
function parseFromTokens ( locale , input , format ) {
2023-06-07 08:08:35 +03:00
const { result , zone , invalidReason } = explainFromTokens ( locale , input , format ) ;
2021-02-26 12:21:42 +03:00
return [
result ,
2021-09-01 16:11:55 +03:00
zone ,
2021-02-26 12:21:42 +03:00
invalidReason
] ;
}
2021-10-08 10:47:01 +03:00
const mod = {
DateTime : DateTime ,
Duration : Duration ,
Interval : Interval ,
Info : Info ,
Zone : Zone ,
FixedOffsetZone : FixedOffsetZone ,
IANAZone : IANAZone ,
InvalidZone : InvalidZone ,
LocalZone : LocalZone ,
Settings : Settings
} ;
2021-12-03 19:55:27 +03:00
const date = new Date ( ) ;
const dt = mod . DateTime . fromJSDate ( date ) ;
console . log ( dt . toISO ( ) ) ;