|
|
@@ -1,56 +1,63 @@
|
|
|
-import { DateMarker, arrayToUtcDate, dateToUtcArray, arrayToLocalDate, dateToLocalArray, startOfHour, startOfMinute, startOfSecond, addMs } from './util'
|
|
|
+import {
|
|
|
+ DateMarker,
|
|
|
+ addDays, addMs,
|
|
|
+ diffHours, diffMinutes, diffSeconds, diffWholeWeeks, diffWholeDays,
|
|
|
+ startOfDay, startOfHour, startOfMinute, startOfSecond,
|
|
|
+ weekOfYear, arrayToUtcDate, dateToUtcArray, dateToLocalArray, arrayToLocalDate, timeAsMs
|
|
|
+} from './marker'
|
|
|
import { CalendarSystem, createCalendarSystem } from './calendar-system'
|
|
|
-import { namedTimeZoneOffsetGenerator, getNamedTimeZoneOffsetGenerator } from './timezone'
|
|
|
-import { getLocale } from './locale'
|
|
|
+import { Locale } from './locale'
|
|
|
+import { NamedTimeZoneImpl, createNamedTimeZoneImpl } from './timezone'
|
|
|
import { Duration } from './duration'
|
|
|
import { DateFormatter, buildIsoString } from './formatting'
|
|
|
import { parse } from './parsing'
|
|
|
-import { assignTo } from '../util/object'
|
|
|
+import { isInt } from '../util/misc'
|
|
|
|
|
|
export interface DateEnvSettings {
|
|
|
timeZone: string
|
|
|
timeZoneImpl?: string
|
|
|
calendarSystem: string
|
|
|
- locale: string // TODO: accept a list
|
|
|
+ locale: Locale
|
|
|
weekNumberCalculation?: any
|
|
|
firstDay?: any
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-export type DateInput = Date | number[] | number | string
|
|
|
-
|
|
|
-
|
|
|
-const MS_IN_DAY = 864e5
|
|
|
-
|
|
|
-// TODO: locale: 'auto'
|
|
|
-// TODO: separate locale object from dateenv. but keep separate from locale name
|
|
|
+export type DateInput = Date | string | number | number[]
|
|
|
|
|
|
|
|
|
export class DateEnv {
|
|
|
|
|
|
timeZone: string
|
|
|
- namedTimeZoneOffsetGenerator: namedTimeZoneOffsetGenerator
|
|
|
+ namedTimeZoneImpl: NamedTimeZoneImpl
|
|
|
+ canComputeOffset: boolean
|
|
|
+
|
|
|
calendarSystem: CalendarSystem
|
|
|
- locale: string
|
|
|
- weekMeta: any
|
|
|
+ locale: Locale
|
|
|
+ weekDow: number
|
|
|
+ weekDoy: number
|
|
|
weekNumberFunc: any
|
|
|
- simpleNumberFormat: Intl.NumberFormat
|
|
|
+
|
|
|
|
|
|
constructor(settings: DateEnvSettings) {
|
|
|
- this.timeZone = settings.timeZone
|
|
|
- this.namedTimeZoneOffsetGenerator = getNamedTimeZoneOffsetGenerator(settings.timeZoneImpl)
|
|
|
+ let timeZone = this.timeZone = settings.timeZone
|
|
|
+ let isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC'
|
|
|
+
|
|
|
+ if (settings.timeZoneImpl && isNamedTimeZone) {
|
|
|
+ this.namedTimeZoneImpl = createNamedTimeZoneImpl(settings.timeZoneImpl, timeZone)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl)
|
|
|
+
|
|
|
this.calendarSystem = createCalendarSystem(settings.calendarSystem)
|
|
|
this.locale = settings.locale
|
|
|
- this.weekMeta = assignTo({}, getLocale(settings.locale).week)
|
|
|
-
|
|
|
- this.simpleNumberFormat = new Intl.NumberFormat(settings.locale)
|
|
|
+ this.weekDow = settings.locale.week.dow
|
|
|
+ this.weekDoy = settings.locale.week.dow
|
|
|
|
|
|
if (settings.weekNumberCalculation === 'ISO') {
|
|
|
- this.weekMeta.dow = 1
|
|
|
- this.weekMeta.doy = 4
|
|
|
+ this.weekDow = 1
|
|
|
+ this.weekDoy = 4
|
|
|
} else if (typeof settings.firstDay === 'number') {
|
|
|
- this.weekMeta.dow = settings.firstDay
|
|
|
+ this.weekDow = settings.firstDay
|
|
|
}
|
|
|
|
|
|
if (typeof settings.weekNumberCalculation === 'function') {
|
|
|
@@ -58,249 +65,137 @@ export class DateEnv {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- add(marker: DateMarker, dur: Duration): DateMarker {
|
|
|
- let { calendarSystem } = this
|
|
|
|
|
|
- return calendarSystem.arrayToMarker([
|
|
|
- calendarSystem.getMarkerYear(marker) + dur.year,
|
|
|
- calendarSystem.getMarkerMonth(marker) + dur.month,
|
|
|
- calendarSystem.getMarkerDay(marker) + dur.day,
|
|
|
- marker.getUTCHours(),
|
|
|
- marker.getUTCMinutes(),
|
|
|
- marker.getUTCSeconds(),
|
|
|
- marker.getUTCMilliseconds() + dur.time
|
|
|
- ])
|
|
|
- }
|
|
|
+ // Creating / Parsing
|
|
|
|
|
|
- getMonth(marker: DateMarker): number {
|
|
|
- return this.calendarSystem.getMarkerMonth(marker)
|
|
|
- }
|
|
|
-
|
|
|
- subtract(marker: DateMarker, dur: Duration): DateMarker {
|
|
|
- let { calendarSystem } = this
|
|
|
-
|
|
|
- return calendarSystem.arrayToMarker([
|
|
|
- calendarSystem.getMarkerYear(marker) - dur.year,
|
|
|
- calendarSystem.getMarkerMonth(marker) - dur.month,
|
|
|
- calendarSystem.getMarkerDay(marker) - dur.day,
|
|
|
- marker.getUTCHours(),
|
|
|
- marker.getUTCMinutes(),
|
|
|
- marker.getUTCSeconds(),
|
|
|
- marker.getUTCMilliseconds() - dur.time
|
|
|
- ])
|
|
|
- }
|
|
|
-
|
|
|
- addYears(marker: DateMarker, n: number): DateMarker {
|
|
|
- let { calendarSystem } = this
|
|
|
-
|
|
|
- return calendarSystem.arrayToMarker([
|
|
|
- calendarSystem.getMarkerYear(marker) + n,
|
|
|
- calendarSystem.getMarkerMonth(marker),
|
|
|
- calendarSystem.getMarkerDay(marker),
|
|
|
- marker.getUTCHours(),
|
|
|
- marker.getUTCMinutes(),
|
|
|
- marker.getUTCSeconds(),
|
|
|
- marker.getUTCMilliseconds()
|
|
|
- ])
|
|
|
- }
|
|
|
-
|
|
|
- startOf(marker: DateMarker, unit: string) {
|
|
|
- if (unit === 'year') {
|
|
|
- return this.startOfYear(marker)
|
|
|
- } else if (unit === 'month') {
|
|
|
- return this.startOfMonth(marker)
|
|
|
- } else if (unit === 'week') {
|
|
|
- return this.startOfWeek(marker)
|
|
|
- } else if (unit === 'day') {
|
|
|
- return this.startOfDay(marker)
|
|
|
- } else if (unit === 'hour') {
|
|
|
- return startOfHour(marker)
|
|
|
- } else if (unit === 'minute') {
|
|
|
- return startOfMinute(marker)
|
|
|
- } else if (unit === 'second') {
|
|
|
- return startOfSecond(marker)
|
|
|
+ createMarker(input: DateInput): DateMarker {
|
|
|
+ let meta = this.createMarkerMeta(input)
|
|
|
+ if (meta === null) {
|
|
|
+ return null
|
|
|
}
|
|
|
+ return meta.marker
|
|
|
}
|
|
|
|
|
|
- startOfYear(marker: DateMarker): DateMarker {
|
|
|
- let { calendarSystem } = this
|
|
|
-
|
|
|
- return calendarSystem.arrayToMarker([
|
|
|
- calendarSystem.getMarkerYear(marker) // might not work, might go to ms
|
|
|
- ])
|
|
|
+ createNowMarker(): DateMarker {
|
|
|
+ return this.timestampToMarker(new Date().valueOf())
|
|
|
}
|
|
|
|
|
|
- startOfMonth(marker: DateMarker): DateMarker {
|
|
|
- let { calendarSystem } = this
|
|
|
+ createMarkerMeta(input: DateInput) {
|
|
|
|
|
|
- return calendarSystem.arrayToMarker([
|
|
|
- calendarSystem.getMarkerYear(marker),
|
|
|
- calendarSystem.getMarkerMonth(marker)
|
|
|
- ])
|
|
|
- }
|
|
|
+ if (typeof input === 'string') {
|
|
|
+ return this.parse(input)
|
|
|
+ }
|
|
|
|
|
|
- startOfWeek(marker: DateMarker): DateMarker {
|
|
|
- return arrayToUtcDate([
|
|
|
- marker.getUTCFullYear(),
|
|
|
- marker.getUTCMonth(),
|
|
|
- marker.getUTCDate() - (marker.getUTCDay() - this.weekMeta.dow + 7) % 7
|
|
|
- ])
|
|
|
- }
|
|
|
+ let marker = null
|
|
|
|
|
|
- computeWeekNumber(marker: DateMarker): number {
|
|
|
- if (this.weekNumberFunc) {
|
|
|
- return this.weekNumberFunc(this.toDate(marker))
|
|
|
- } else {
|
|
|
- let { dow, doy } = this.weekMeta
|
|
|
- return weekOfYear(marker, dow, doy)
|
|
|
+ if (typeof input === 'number') {
|
|
|
+ marker = this.timestampToMarker(input)
|
|
|
+ } else if (input instanceof Date) {
|
|
|
+ marker = this.timestampToMarker(input.valueOf())
|
|
|
+ } else if (Array.isArray(input)) {
|
|
|
+ marker = arrayToUtcDate(input)
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // TODO: make i18n friendly
|
|
|
- // use weekNumberTitle?
|
|
|
- formatWeek(marker: DateMarker, includeLabel: boolean = false): string {
|
|
|
- let w = this.computeWeekNumber(marker)
|
|
|
- let s = this.simpleNumberFormat.format(w)
|
|
|
-
|
|
|
- if (includeLabel) {
|
|
|
- s = 'Wk ' + s
|
|
|
+ if (marker === null) {
|
|
|
+ return null
|
|
|
}
|
|
|
|
|
|
- return s
|
|
|
+ return { marker, isTimeUnspecified: false, forcedTimeZoneOffset: null }
|
|
|
}
|
|
|
|
|
|
- toDate(marker: DateMarker): Date {
|
|
|
- if (this.timeZone === 'UTC' || !this.canComputeTimeZoneOffset()) {
|
|
|
- return new Date(marker.valueOf())
|
|
|
- } else {
|
|
|
- let arr = dateToUtcArray(marker)
|
|
|
+ parse(s: string) {
|
|
|
+ let parts = parse(s)
|
|
|
+ let marker = parts.marker
|
|
|
+ let forcedTimeZoneOffset = null
|
|
|
|
|
|
- if (this.timeZone === 'local') {
|
|
|
- return arrayToLocalDate(arr)
|
|
|
+ if (parts.timeZoneOffset !== null) {
|
|
|
+ if (this.canComputeOffset) {
|
|
|
+ marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000)
|
|
|
} else {
|
|
|
- return new Date(
|
|
|
- marker.valueOf() -
|
|
|
- this.namedTimeZoneOffsetGenerator(this.timeZone, arr)
|
|
|
- )
|
|
|
+ forcedTimeZoneOffset = parts.timeZoneOffset
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- canComputeTimeZoneOffset() {
|
|
|
- return this.timeZone === 'UTC' || this.timeZone === 'local' || this.namedTimeZoneOffsetGenerator
|
|
|
+ return { marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTimeZoneOffset }
|
|
|
}
|
|
|
|
|
|
- // will return undefined if cant compute it
|
|
|
- computeTimeZoneOffset(marker: DateMarker) {
|
|
|
- if (this.timeZone === 'UTC') {
|
|
|
- return 0
|
|
|
- } else {
|
|
|
- let arr = dateToUtcArray(marker)
|
|
|
|
|
|
- if (this.timeZone === 'local') {
|
|
|
- return arrayToLocalDate(arr).getTimezoneOffset()
|
|
|
- } else if (this.namedTimeZoneOffsetGenerator) {
|
|
|
- return this.namedTimeZoneOffsetGenerator(this.timeZone, arr)
|
|
|
- }
|
|
|
- }
|
|
|
+ // Accessors
|
|
|
+
|
|
|
+ getYear(marker: DateMarker): number {
|
|
|
+ return this.calendarSystem.getMarkerYear(marker)
|
|
|
}
|
|
|
|
|
|
- toRangeFormat(start: DateMarker, end: DateMarker, formatter: DateFormatter, dateOptions: any = {}) {
|
|
|
+ getMonth(marker: DateMarker): number {
|
|
|
+ return this.calendarSystem.getMarkerMonth(marker)
|
|
|
+ }
|
|
|
|
|
|
- // yuck
|
|
|
- if (dateOptions.isExclusive) {
|
|
|
- end = addMs(end, -1)
|
|
|
- }
|
|
|
|
|
|
- return formatter.format(
|
|
|
- {
|
|
|
- marker: start,
|
|
|
- timeZoneOffset: dateOptions.forcedStartTimeZoneOffset != null ?
|
|
|
- dateOptions.forcedStartTimeZoneOffset :
|
|
|
- this.computeTimeZoneOffset(start)
|
|
|
- },
|
|
|
- {
|
|
|
- marker: end,
|
|
|
- timeZoneOffset: dateOptions.forcedEndTimeZoneOffset != null ?
|
|
|
- dateOptions.forcedEndTimeZoneOffset :
|
|
|
- this.computeTimeZoneOffset(end)
|
|
|
- },
|
|
|
- this // yuck
|
|
|
- )
|
|
|
+ // Adding / Subtracting
|
|
|
+
|
|
|
+ add(marker: DateMarker, dur: Duration): DateMarker {
|
|
|
+ let a = this.calendarSystem.markerToArray(marker)
|
|
|
+ a[0] += dur.year
|
|
|
+ a[1] += dur.month
|
|
|
+ a[2] += dur.day
|
|
|
+ a[6] += dur.time
|
|
|
+ return this.calendarSystem.arrayToMarker(a)
|
|
|
}
|
|
|
|
|
|
- toFormat(marker: DateMarker, formatter: DateFormatter, extraOptions: any = {}) {
|
|
|
- return formatter.format(
|
|
|
- {
|
|
|
- marker: marker,
|
|
|
- timeZoneOffset: extraOptions.forcedTimeZoneOffset != null ?
|
|
|
- extraOptions.forcedTimeZoneOffset :
|
|
|
- this.computeTimeZoneOffset(marker)
|
|
|
- },
|
|
|
- null,
|
|
|
- this
|
|
|
- )
|
|
|
+ subtract(marker: DateMarker, dur: Duration): DateMarker {
|
|
|
+ let a = this.calendarSystem.markerToArray(marker)
|
|
|
+ a[0] -= dur.year
|
|
|
+ a[1] -= dur.month
|
|
|
+ a[2] -= dur.day
|
|
|
+ a[6] -= dur.time
|
|
|
+ return this.calendarSystem.arrayToMarker(a)
|
|
|
}
|
|
|
|
|
|
- toIso(marker: DateMarker, extraOptions: any = {}) {
|
|
|
- return buildIsoString(
|
|
|
- marker,
|
|
|
- extraOptions.forcedTimeZoneOffset != null ?
|
|
|
- extraOptions.forcedTimeZoneOffset :
|
|
|
- this.computeTimeZoneOffset(marker),
|
|
|
- extraOptions.omitTime
|
|
|
- )
|
|
|
+ addYears(marker: DateMarker, n: number) {
|
|
|
+ let a = this.calendarSystem.markerToArray(marker)
|
|
|
+ a[0] += n
|
|
|
+ return this.calendarSystem.arrayToMarker(a)
|
|
|
}
|
|
|
|
|
|
- createMarker(input: DateInput) {
|
|
|
- return this.createMarkerMeta(input).marker
|
|
|
+ addMonths(marker: DateMarker, n: number) {
|
|
|
+ let a = this.calendarSystem.markerToArray(marker)
|
|
|
+ a[1] += n
|
|
|
+ return this.calendarSystem.arrayToMarker(a)
|
|
|
}
|
|
|
|
|
|
- // returns an object that wraps the marker!
|
|
|
- createMarkerMeta(input: DateInput) {
|
|
|
- if (typeof input === 'string') {
|
|
|
- return this.parse(input)
|
|
|
- } else if (typeof input === 'number') {
|
|
|
- return { marker: this.timestampToMarker(input), isTimeUnspecified: false, forcedTimeZoneOffset: null }
|
|
|
- } else if (isNativeDate(input)) {
|
|
|
- return { marker: this.dateToMarker(input as Date), isTimeUnspecified: false, forcedTimeZoneOffset: null }
|
|
|
- } else if (Array.isArray(input)) {
|
|
|
- return { marker: arrayToUtcDate(input), isTimeUnspecified: false, forcedTimeZoneOffset: null }
|
|
|
+
|
|
|
+ // Diffing Whole Units
|
|
|
+
|
|
|
+ diffWholeYears(m0: DateMarker, m1: DateMarker): number {
|
|
|
+ let { calendarSystem } = this
|
|
|
+
|
|
|
+ if (
|
|
|
+ timeAsMs(m0) === timeAsMs(m1) &&
|
|
|
+ calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) &&
|
|
|
+ calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)
|
|
|
+ ) {
|
|
|
+ return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)
|
|
|
}
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- parse(str: string) {
|
|
|
- let parts = parse(str)
|
|
|
- let marker = parts.marker
|
|
|
- let forcedTimeZoneOffset = null
|
|
|
+ diffWholeMonths(m0: DateMarker, m1: DateMarker): number {
|
|
|
+ let { calendarSystem } = this
|
|
|
|
|
|
- if (parts.timeZoneOffset != null) {
|
|
|
- if (this.canComputeTimeZoneOffset()) { // can get rid of this now?
|
|
|
- marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000)
|
|
|
- } else {
|
|
|
- forcedTimeZoneOffset = parts.timeZoneOffset
|
|
|
- }
|
|
|
+ if (
|
|
|
+ timeAsMs(m0) === timeAsMs(m1) &&
|
|
|
+ calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)
|
|
|
+ ) {
|
|
|
+ return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) +
|
|
|
+ (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12
|
|
|
}
|
|
|
-
|
|
|
- return { marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTimeZoneOffset }
|
|
|
+ return null
|
|
|
}
|
|
|
|
|
|
- dateToMarker(date: Date) {
|
|
|
- return this.timestampToMarker(date.valueOf())
|
|
|
- }
|
|
|
|
|
|
- timestampToMarker(ms: number) {
|
|
|
- if (this.timeZone === 'UTC') {
|
|
|
- return new Date(ms)
|
|
|
- } else if (this.timeZone === 'local') {
|
|
|
- return arrayToUtcDate(dateToLocalArray(new Date(ms)))
|
|
|
- } else {
|
|
|
- throw 'need tz system!!!'
|
|
|
- }
|
|
|
- }
|
|
|
+ // Range / Duration
|
|
|
|
|
|
- computeGreatestDenominator(m0: DateMarker, m1: DateMarker) {
|
|
|
+ greatestWholeUnit(m0: DateMarker, m1: DateMarker) {
|
|
|
let n = this.diffWholeYears(m0, m1)
|
|
|
|
|
|
if (n !== null) {
|
|
|
@@ -313,13 +208,13 @@ export class DateEnv {
|
|
|
return { unit: 'month', value: n }
|
|
|
}
|
|
|
|
|
|
- n = this.diffWholeWeeks(m0, m1)
|
|
|
+ n = diffWholeWeeks(m0, m1)
|
|
|
|
|
|
if (n !== null) {
|
|
|
- return { unit: 'week', value: n / 7 }
|
|
|
+ return { unit: 'week', value: n }
|
|
|
}
|
|
|
|
|
|
- n = this.diffWholeDays(m0, m1)
|
|
|
+ n = diffWholeDays(m0, m1)
|
|
|
|
|
|
if (n !== null) {
|
|
|
return { unit: 'day', value: n }
|
|
|
@@ -327,29 +222,29 @@ export class DateEnv {
|
|
|
|
|
|
n = diffHours(m0, m1)
|
|
|
|
|
|
- if (n !== null) {
|
|
|
+ if (isInt(n)) {
|
|
|
return { unit: 'hour', value: n }
|
|
|
}
|
|
|
|
|
|
n = diffMinutes(m0, m1)
|
|
|
|
|
|
- if (n !== null) {
|
|
|
+ if (isInt(n)) {
|
|
|
return { unit: 'minute', value: n }
|
|
|
}
|
|
|
|
|
|
n = diffSeconds(m0, m1)
|
|
|
|
|
|
- if (n !== null) {
|
|
|
+ if (isInt(n)) {
|
|
|
return { unit: 'second', value: n }
|
|
|
}
|
|
|
|
|
|
return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() }
|
|
|
}
|
|
|
|
|
|
- divideRangeByWholeDuration(m0: DateMarker, m1: DateMarker, d: Duration) {
|
|
|
+ countDurationsBetween(m0: DateMarker, m1: DateMarker, d: Duration) {
|
|
|
let cnt = 0
|
|
|
|
|
|
- while (m0 < m1) { // not optimal
|
|
|
+ while (m0 < m1) { // better way to do this without iterating?
|
|
|
m0 = this.add(m0, d)
|
|
|
cnt++
|
|
|
}
|
|
|
@@ -357,171 +252,158 @@ export class DateEnv {
|
|
|
return cnt
|
|
|
}
|
|
|
|
|
|
- diffWholeYears(m0: DateMarker, m1: DateMarker): number {
|
|
|
- let { calendarSystem } = this
|
|
|
|
|
|
- if (
|
|
|
- m0.getUTCMilliseconds() === m1.getUTCMilliseconds() && // TODO: util for time
|
|
|
- m0.getUTCSeconds() === m1.getUTCSeconds() &&
|
|
|
- m0.getUTCMinutes() === m1.getUTCMinutes() &&
|
|
|
- m0.getUTCHours() === m1.getUTCHours() &&
|
|
|
- calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) &&
|
|
|
- calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)
|
|
|
- ) {
|
|
|
- return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)
|
|
|
- }
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- diffWholeMonths(m0: DateMarker, m1: DateMarker): number {
|
|
|
- let { calendarSystem } = this
|
|
|
+ // Start-Of
|
|
|
|
|
|
- if (
|
|
|
- m0.getUTCMilliseconds() === m1.getUTCMilliseconds() &&
|
|
|
- m0.getUTCSeconds() === m1.getUTCSeconds() &&
|
|
|
- m0.getUTCMinutes() === m1.getUTCMinutes() &&
|
|
|
- m0.getUTCHours() === m1.getUTCHours() &&
|
|
|
- calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)
|
|
|
- ) {
|
|
|
- return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) +
|
|
|
- (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12
|
|
|
+ startOf(m: DateMarker, unit: string) {
|
|
|
+ if (unit === 'year') {
|
|
|
+ return this.startOfYear(m)
|
|
|
+ } else if (unit === 'month') {
|
|
|
+ return this.startOfMonth(m)
|
|
|
+ } else if (unit === 'week') {
|
|
|
+ return this.startOfWeek(m)
|
|
|
+ } else if (unit === 'day') {
|
|
|
+ return startOfDay(m)
|
|
|
+ } else if (unit === 'hour') {
|
|
|
+ return startOfHour(m)
|
|
|
+ } else if (unit === 'minute') {
|
|
|
+ return startOfMinute(m)
|
|
|
+ } else if (unit === 'second') {
|
|
|
+ return startOfSecond(m)
|
|
|
}
|
|
|
- return null
|
|
|
}
|
|
|
|
|
|
- diffWholeWeeks(m0: DateMarker, m1: DateMarker): number {
|
|
|
- let d = this.diffWholeDays(m0, m1)
|
|
|
-
|
|
|
- if (d !== null && d % 7 === 0) {
|
|
|
- return d / 7
|
|
|
- }
|
|
|
-
|
|
|
- return null
|
|
|
+ startOfYear(m: DateMarker): DateMarker {
|
|
|
+ return this.calendarSystem.arrayToMarker([
|
|
|
+ this.calendarSystem.getMarkerYear(m)
|
|
|
+ ])
|
|
|
}
|
|
|
|
|
|
- diffWholeDays(m0: DateMarker, m1: DateMarker): number {
|
|
|
- if (
|
|
|
- m0.getUTCMilliseconds() === m1.getUTCMilliseconds() &&
|
|
|
- m0.getUTCSeconds() === m1.getUTCSeconds() &&
|
|
|
- m0.getUTCMinutes() === m1.getUTCMinutes() &&
|
|
|
- m0.getUTCHours() === m1.getUTCHours()
|
|
|
- ) {
|
|
|
- return Math.round(diffDays(m0, m1))
|
|
|
- }
|
|
|
- return null
|
|
|
+ startOfMonth(m: DateMarker): DateMarker {
|
|
|
+ return this.calendarSystem.arrayToMarker([
|
|
|
+ this.calendarSystem.getMarkerYear(m),
|
|
|
+ this.calendarSystem.getMarkerMonth(m)
|
|
|
+ ])
|
|
|
}
|
|
|
|
|
|
- diffDayAndTime(m0: DateMarker, m1: DateMarker): Duration {
|
|
|
- let m0day = this.startOfDay(m0)
|
|
|
- let m1day = this.startOfDay(m1)
|
|
|
-
|
|
|
- return {
|
|
|
- year: 0,
|
|
|
- month: 0,
|
|
|
- day: Math.round((m1day.valueOf() - m0day.valueOf()) / MS_IN_DAY),
|
|
|
- time: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf())
|
|
|
- }
|
|
|
+ startOfWeek(m: DateMarker): DateMarker {
|
|
|
+ return addDays(m, -((m.getUTCDay() - this.weekDow + 7) % 7))
|
|
|
}
|
|
|
|
|
|
- startOfDay: (marker: DateMarker) => DateMarker
|
|
|
|
|
|
-}
|
|
|
+ // Week Number
|
|
|
|
|
|
-DateEnv.prototype.startOfDay = startOfDay
|
|
|
-
|
|
|
-
|
|
|
-function weekOfYear(marker, dow, doy) {
|
|
|
- let y = marker.getUTCFullYear()
|
|
|
- let w = weekOfGivenYear(marker, y, dow, doy)
|
|
|
-
|
|
|
- if (w < 1) {
|
|
|
- return weekOfGivenYear(marker, y - 1, dow, doy)
|
|
|
+ computeWeekNumber(marker: DateMarker): number {
|
|
|
+ if (this.weekNumberFunc) {
|
|
|
+ return this.weekNumberFunc(this.toDate(marker))
|
|
|
+ } else {
|
|
|
+ return weekOfYear(marker, this.weekDow, this.weekDoy)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- let nextW = weekOfGivenYear(marker, y + 1, dow, doy)
|
|
|
- if (nextW >= 1) {
|
|
|
- return Math.min(w, nextW)
|
|
|
+ format(marker: DateMarker, formatter: DateFormatter, dateOptions: any = {}) {
|
|
|
+ return formatter.format(
|
|
|
+ {
|
|
|
+ marker: marker,
|
|
|
+ timeZoneOffset: dateOptions.forcedTimeZoneOffset != null ?
|
|
|
+ dateOptions.forcedTimeZoneOffset :
|
|
|
+ this.offsetForMarker(marker)
|
|
|
+ },
|
|
|
+ this
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
- return w
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-function weekOfGivenYear(marker, year, dow, doy) {
|
|
|
- let firstWeekStart = arrayToUtcDate([ year, 0, 1 + firstWeekOffset(year, dow, doy) ])
|
|
|
- let dayStart = startOfDay(marker)
|
|
|
- let days = Math.round(diffDays(firstWeekStart, dayStart))
|
|
|
-
|
|
|
- return Math.floor(days / 7) + 1 // zero-indexed
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-function startOfDay(marker: DateMarker): DateMarker {
|
|
|
- return arrayToUtcDate([
|
|
|
- marker.getUTCFullYear(),
|
|
|
- marker.getUTCMonth(),
|
|
|
- marker.getUTCDate()
|
|
|
- ])
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-export function diffDays(m0, m1) { // will give float
|
|
|
- return (m1.valueOf() - m0.valueOf()) / MS_IN_DAY
|
|
|
-}
|
|
|
-
|
|
|
-export function diffWeeks(m0, m1) { // will give float
|
|
|
- return Math.round(diffDays(m0, m1)) / 7
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// start-of-first-week - start-of-year
|
|
|
-function firstWeekOffset(year, dow, doy) {
|
|
|
- var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
|
|
|
- fwd = 7 + dow - doy,
|
|
|
- // first-week day local weekday -- which local weekday is fwd
|
|
|
- fwdlw = (7 + arrayToUtcDate([ year, 0, fwd ]).getUTCDay() - dow) % 7;
|
|
|
- return -fwdlw + fwd - 1;
|
|
|
-}
|
|
|
+ formatRange(start: DateMarker, end: DateMarker, formatter: DateFormatter, dateOptions: any = {}) {
|
|
|
|
|
|
+ if (dateOptions.isEndExclusive) {
|
|
|
+ end = addMs(end, -1)
|
|
|
+ }
|
|
|
|
|
|
-function isNativeDate(input) {
|
|
|
- return Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date
|
|
|
-}
|
|
|
+ return formatter.formatRange(
|
|
|
+ {
|
|
|
+ marker: start,
|
|
|
+ timeZoneOffset: dateOptions.forcedStartTimeZoneOffset != null ?
|
|
|
+ dateOptions.forcedStartTimeZoneOffset :
|
|
|
+ this.offsetForMarker(start)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ marker: end,
|
|
|
+ timeZoneOffset: dateOptions.forcedEndTimeZoneOffset != null ?
|
|
|
+ dateOptions.forcedEndTimeZoneOffset :
|
|
|
+ this.offsetForMarker(end)
|
|
|
+ },
|
|
|
+ this
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
+ formatIso(marker: DateMarker, extraOptions: any = {}) {
|
|
|
+ return buildIsoString(
|
|
|
+ marker,
|
|
|
+ extraOptions.forcedTimeZoneOffset != null ?
|
|
|
+ extraOptions.forcedTimeZoneOffset :
|
|
|
+ this.offsetForMarker(marker),
|
|
|
+ extraOptions.omitTime
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
+ formatWeek(marker: DateMarker, fit?: 'numeric' | 'narrow' | 'short'): string {
|
|
|
+ let { locale } = this
|
|
|
+ let parts = []
|
|
|
|
|
|
-const MS_IN_HOUR = 1000 * 60 * 60
|
|
|
-const MS_IN_MINUTE = 1000 * 60
|
|
|
+ if (fit === 'narrow') {
|
|
|
+ parts.push(locale.options.weekHeader)
|
|
|
+ } else if (fit === 'short') {
|
|
|
+ parts.push(locale.options.weekHeader, ' ')
|
|
|
+ }
|
|
|
+ // otherwise, considered 'numeric'
|
|
|
|
|
|
+ parts.push(this.computeWeekNumber(marker))
|
|
|
|
|
|
-function diffHours(m0, m1) {
|
|
|
- let ms = m1.valueOf() - m0.valueOf()
|
|
|
+ if (locale.options.isRTL) {
|
|
|
+ parts.reverse()
|
|
|
+ }
|
|
|
|
|
|
- if (ms % MS_IN_HOUR === 0) {
|
|
|
- return ms / MS_IN_HOUR
|
|
|
+ return parts.join('')
|
|
|
}
|
|
|
|
|
|
- return null
|
|
|
-}
|
|
|
-
|
|
|
|
|
|
-function diffMinutes(m0, m1) {
|
|
|
- let ms = m1.valueOf() - m0.valueOf()
|
|
|
+ // TimeZone
|
|
|
|
|
|
- if (ms % MS_IN_MINUTE === 0) {
|
|
|
- return ms / MS_IN_MINUTE
|
|
|
+ timestampToMarker(ms: number) {
|
|
|
+ if (this.timeZone === 'local') {
|
|
|
+ return arrayToUtcDate(dateToLocalArray(new Date(ms)))
|
|
|
+ } else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) {
|
|
|
+ return new Date(ms)
|
|
|
+ } else {
|
|
|
+ return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return null
|
|
|
-}
|
|
|
+ offsetForMarker(m: DateMarker) {
|
|
|
+ if (this.timeZone === 'local') {
|
|
|
+ return arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset()
|
|
|
+ } else if (this.timeZone === 'UTC') {
|
|
|
+ return m.getTimezoneOffset()
|
|
|
+ } else if (this.namedTimeZoneImpl) {
|
|
|
+ return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m))
|
|
|
+ }
|
|
|
+ return null
|
|
|
+ }
|
|
|
|
|
|
|
|
|
-function diffSeconds(m0, m1) {
|
|
|
- let ms = m1.valueOf() - m0.valueOf()
|
|
|
+ // Conversion
|
|
|
|
|
|
- if (ms % 1000 === 0) {
|
|
|
- return ms / 1000
|
|
|
+ toDate(m: DateMarker): Date {
|
|
|
+ if (this.timeZone === 'local') {
|
|
|
+ return arrayToLocalDate(dateToUtcArray(m))
|
|
|
+ } else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) {
|
|
|
+ return new Date(m.valueOf()) // make sure it's a copy
|
|
|
+ } else {
|
|
|
+ return new Date(
|
|
|
+ m.valueOf() -
|
|
|
+ this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m))
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return null
|
|
|
}
|