summaryrefslogtreecommitdiff
path: root/date-fns/test/dst
diff options
context:
space:
mode:
Diffstat (limited to 'date-fns/test/dst')
-rw-r--r--date-fns/test/dst/addBusinessDays/basic.js18
-rw-r--r--date-fns/test/dst/eachDayOfInterval/basic.js22
-rw-r--r--date-fns/test/dst/formatDistanceStrict/cairo.ts15
-rw-r--r--date-fns/test/dst/formatDistanceStrict/melbourne.ts16
-rw-r--r--date-fns/test/dst/parseISO/basic.js30
-rw-r--r--date-fns/test/dst/parseISO/samoa.js16
-rw-r--r--date-fns/test/dst/parseISO/sydney.js58
-rw-r--r--date-fns/test/dst/tzOffsetTransitions.ts143
8 files changed, 318 insertions, 0 deletions
diff --git a/date-fns/test/dst/addBusinessDays/basic.js b/date-fns/test/dst/addBusinessDays/basic.js
new file mode 100644
index 0000000..c55a686
--- /dev/null
+++ b/date-fns/test/dst/addBusinessDays/basic.js
@@ -0,0 +1,18 @@
+// This is basic DST test for addBusinessDays
+
+import assert from 'assert'
+import addBusinessDays from '../../../src/addBusinessDays'
+
+if (process.env.TZ !== 'America/Santiago')
+ throw new Error('The test must be run with TZ=America/Santiago')
+
+if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10)
+ throw new Error('The test must be run on Node.js version >= 10')
+
+console.log(addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10).toString())
+
+assert.deepEqual(
+ // new Date(2014, 8, 7) is the DST day
+ addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10).toString(),
+ 'Mon Sep 15 2014 00:00:00 GMT-0300 (Chile Summer Time)'
+)
diff --git a/date-fns/test/dst/eachDayOfInterval/basic.js b/date-fns/test/dst/eachDayOfInterval/basic.js
new file mode 100644
index 0000000..8525a2b
--- /dev/null
+++ b/date-fns/test/dst/eachDayOfInterval/basic.js
@@ -0,0 +1,22 @@
+// This is basic DST test for eachDayOfInterval
+
+import eachDayOfInterval from '../../../src/eachDayOfInterval'
+import assert from 'assert'
+
+if (process.env.TZ !== 'Asia/Damascus')
+ throw new Error('The test must be run with TZ=Asia/Damascus')
+
+if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10)
+ throw new Error('The test must be run on Node.js version >= 10')
+
+assert.deepEqual(
+ eachDayOfInterval({
+ start: new Date(2020, 2, 26),
+ end: new Date(2020, 2, 28)
+ }).map(d => d.toString()),
+ [
+ 'Thu Mar 26 2020 00:00:00 GMT+0200 (Eastern European Standard Time)',
+ 'Fri Mar 27 2020 01:00:00 GMT+0300 (Eastern European Summer Time)',
+ 'Sat Mar 28 2020 00:00:00 GMT+0300 (Eastern European Summer Time)'
+ ]
+)
diff --git a/date-fns/test/dst/formatDistanceStrict/cairo.ts b/date-fns/test/dst/formatDistanceStrict/cairo.ts
new file mode 100644
index 0000000..c9bd691
--- /dev/null
+++ b/date-fns/test/dst/formatDistanceStrict/cairo.ts
@@ -0,0 +1,15 @@
+// This is DST test for formatDistanceStrict in the Cairo timezone
+
+import formatDistanceStrict from '../../../src/formatDistanceStrict'
+import assert from 'assert'
+
+if (process.env.TZ !== 'Africa/Cairo')
+ throw new Error('The test must be run with TZ=Africa/Cairo')
+
+assert.equal(
+ formatDistanceStrict(
+ new Date(1986, 3, 4, 10, 32, 0),
+ new Date(1986, 4, 4, 10, 32, 0)
+ ),
+ '1 month'
+)
diff --git a/date-fns/test/dst/formatDistanceStrict/melbourne.ts b/date-fns/test/dst/formatDistanceStrict/melbourne.ts
new file mode 100644
index 0000000..aee04da
--- /dev/null
+++ b/date-fns/test/dst/formatDistanceStrict/melbourne.ts
@@ -0,0 +1,16 @@
+// This is DST test for formatDistanceStrict in the Melbourne timezone
+
+import formatDistanceStrict from '../../../src/formatDistanceStrict'
+import parseISO from '../../../src/parseISO'
+import assert from 'assert'
+
+if (process.env.TZ !== 'Australia/Melbourne')
+ throw new Error('The test must be run with TZ=Australia/Melbourne')
+
+assert.equal(
+ formatDistanceStrict(
+ parseISO('2020-04-05T01:00:00+11:00'),
+ parseISO('2020-04-05T03:00:00+10:00')
+ ),
+ '3 hours'
+)
diff --git a/date-fns/test/dst/parseISO/basic.js b/date-fns/test/dst/parseISO/basic.js
new file mode 100644
index 0000000..665af35
--- /dev/null
+++ b/date-fns/test/dst/parseISO/basic.js
@@ -0,0 +1,30 @@
+// This is basic DST test for parseISO
+
+import parseISO from '../../../src/parseISO'
+import assert from 'assert'
+
+if (process.env.TZ !== 'America/Sao_Paulo')
+ throw new Error('The test must be run with TZ=America/Sao_Paulo')
+
+if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10)
+ throw new Error('The test must be run on Node.js version >= 10')
+
+// Test DST start edge
+assert.equal(parseISO('2018-11-03').getDate(), 3)
+assert.equal(parseISO('2018-11-04').getDate(), 4) // DST start
+assert.equal(parseISO('2018-11-05').getDate(), 5)
+
+// Test DST end edge
+assert.equal(parseISO('2019-02-15').getDate(), 15)
+assert.equal(parseISO('2019-02-16').getDate(), 16) // DST end
+assert.equal(parseISO('2019-02-17').getDate(), 17)
+
+// Test creation of nonexistent time
+assert.equal(
+ parseISO('2018-11-04T00:00').toString(),
+ 'Sun Nov 04 2018 01:00:00 GMT-0200 (Brasilia Summer Time)'
+)
+assert.equal(
+ parseISO('2018-11-04T00:30').toString(),
+ 'Sun Nov 04 2018 01:30:00 GMT-0200 (Brasilia Summer Time)'
+)
diff --git a/date-fns/test/dst/parseISO/samoa.js b/date-fns/test/dst/parseISO/samoa.js
new file mode 100644
index 0000000..40d224f
--- /dev/null
+++ b/date-fns/test/dst/parseISO/samoa.js
@@ -0,0 +1,16 @@
+// This is an edge case DST test for parseISO
+
+import parseISO from '../../../src/parseISO'
+import assert from 'assert'
+
+if (process.env.TZ !== 'Pacific/Apia')
+ throw new Error('The test must be run with TZ=Pacific/Apia')
+
+if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10)
+ throw new Error('The test must be run on Node.js version >= 10')
+
+assert.equal(parseISO('2011-12-30').getDate(), 31)
+assert.equal(
+ parseISO('2011-12-30T03:30').toString(),
+ 'Sat Dec 31 2011 03:30:00 GMT+1400 (Apia Daylight Time)'
+)
diff --git a/date-fns/test/dst/parseISO/sydney.js b/date-fns/test/dst/parseISO/sydney.js
new file mode 100644
index 0000000..3b733c0
--- /dev/null
+++ b/date-fns/test/dst/parseISO/sydney.js
@@ -0,0 +1,58 @@
+// This is basic DST test for parseISO
+
+import parseISO from '../../../src/parseISO'
+import assert from 'assert'
+
+if (process.env.TZ !== 'Australia/Sydney')
+ throw new Error('The test must be run with TZ=Australia/Sydney')
+
+if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10)
+ throw new Error('The test must be run on Node.js version >= 10')
+
+// Test DST start edge
+assert.equal(parseISO('2019-10-06').getDate(), 6) // DST start
+assert.equal(parseISO('2019-10-07').getDate(), 7)
+assert.equal(
+ parseISO('2019-10-06T01:00:00').toString(),
+ 'Sun Oct 06 2019 01:00:00 GMT+1000 (Australian Eastern Standard Time)'
+)
+assert.equal(
+ parseISO('2019-10-06T02:00:00').toString(),
+ 'Sun Oct 06 2019 03:00:00 GMT+1100 (Australian Eastern Daylight Time)'
+)
+
+assert.equal(
+ parseISO('2019-10-06T05:00:00').toString(),
+ 'Sun Oct 06 2019 05:00:00 GMT+1100 (Australian Eastern Daylight Time)'
+)
+
+// Test DST end edge
+assert.equal(parseISO('2019-04-06').getDate(), 6)
+assert.equal(parseISO('2019-04-07').getDate(), 7) // DST end
+assert.equal(
+ parseISO('2019-04-06T11:00:00').toString(),
+ 'Sat Apr 06 2019 11:00:00 GMT+1100 (Australian Eastern Daylight Time)'
+)
+assert.equal(
+ parseISO('2019-04-07T11:00:00').toString(),
+ 'Sun Apr 07 2019 11:00:00 GMT+1000 (Australian Eastern Standard Time)'
+)
+
+assert.equal(
+ parseISO('2019-04-07T00:00:00').toString(),
+ 'Sun Apr 07 2019 00:00:00 GMT+1100 (Australian Eastern Daylight Time)'
+)
+
+// test edge cases for months, years
+assert.equal(
+ parseISO('2020-01-01T00:00:00').toString(),
+ 'Wed Jan 01 2020 00:00:00 GMT+1100 (Australian Eastern Daylight Time)'
+)
+assert.equal(
+ parseISO('2019-12-31T23:59:59').toString(),
+ 'Tue Dec 31 2019 23:59:59 GMT+1100 (Australian Eastern Daylight Time)'
+)
+assert.equal(
+ parseISO('2020-02-29T23:59:59').toString(),
+ 'Sat Feb 29 2020 23:59:59 GMT+1100 (Australian Eastern Daylight Time)'
+)
diff --git a/date-fns/test/dst/tzOffsetTransitions.ts b/date-fns/test/dst/tzOffsetTransitions.ts
new file mode 100644
index 0000000..3e5521b
--- /dev/null
+++ b/date-fns/test/dst/tzOffsetTransitions.ts
@@ -0,0 +1,143 @@
+type PartialInterval = {
+ start: Date | undefined
+ end: Date | undefined
+}
+
+/**
+ * Fetch the start and end of DST for the local time
+ * zone in a given year.
+ * We'll assume that DST start & end are the first
+ * forward and the last back transitions in the year,
+ * except transitions in Jan or Dec which are likely
+ * to be permanent TZ changes rather than DST changes.
+ * @param {number} year
+ * @returns object with two Date-valued properties:
+ * - `start` is the first instant of DST in the Spring,
+ * or undefined if there's no DST in this year.
+ * - `end` is the first instant of standard time
+ * in the Fall, or undefined if there's no DST in
+ * this year.
+ */
+export function getDstTransitions(year: number): PartialInterval {
+ const result: PartialInterval = {
+ start: undefined,
+ end: undefined
+ }
+ const transitions = getTzOffsetTransitions(year)
+ for (let i = 0; i < transitions.length; i++) {
+ const t = transitions[i]
+ const month = t.date.getMonth()
+ if (month > 0 && month < 11) {
+ if (t.type === 'forward') result.start = t.date
+ if (t.type === 'back' && !result.end) result.end = t.date
+ }
+ }
+ return result
+}
+
+function isValidDate(date: unknown): date is Date {
+ return date instanceof Date && !isNaN(date.getTime())
+}
+
+const MINUTE = 1000 * 60
+
+function firstTickInLocalDay(date: Date): Date {
+ const dateNumber = date.getDate()
+ let prev = date
+ let d = date
+ do {
+ prev = d
+ d = new Date(d.getTime() - MINUTE)
+ } while (dateNumber === d.getDate())
+ return prev
+}
+
+function fiveMinutesLater(date: Date): Date {
+ return new Date(date.getTime() + 5 * MINUTE)
+}
+
+function oneDayLater(date: Date): Date {
+ const d = new Date(date)
+ d.setDate(d.getDate() + 1)
+ return firstTickInLocalDay(d)
+}
+
+function previousTickTimezoneOffset(date: Date): number {
+ const d = new Date(date.getTime() - 1)
+ return d.getTimezoneOffset()
+}
+
+/**
+ * Fetch all timezone-offset transitions in a given
+ * year. These are almost always DST transitions,
+ * but sometimes there are non-DST changes, e.g.
+ * when a country changes its time zone
+ * @param {number} year
+ * @returns array of objects, each with the following
+ * propeerties:
+ * - `date` - a `Date` representing the first instant
+ * when the new timezone offset is effective.
+ * - `type` - either `forward` for skippnig time like
+ * the Spring transition to DST.
+ * - `before` - the timezone offset before the tranition.
+ * For example, the UTC-0400 offset will return -240.
+ * To match how times are displayed in ISO 8601 format,
+ * the sign of this value is reversed from the return
+ * value of `Date.getTimezoneOffset`.
+ * - `after` - the timezone offset after the tranition.
+ * Examples and caveats are the same as `before`.
+
+ */
+export function getTzOffsetTransitions(year: number) {
+ // start at the end of the previous day
+ let date = firstTickInLocalDay(new Date(year, 0, 1))
+ if (!isValidDate(date)) {
+ throw new Error('Invalid Date')
+ }
+ let baseTzOffset = previousTickTimezoneOffset(date)
+ const transitions = []
+ do {
+ let tzOffset = date.getTimezoneOffset()
+ if (baseTzOffset !== tzOffset) {
+ if (tzOffset !== previousTickTimezoneOffset(date)) {
+ // Transition is the first tick of a local day.
+ transitions.push({
+ date: date,
+ type: tzOffset < baseTzOffset ? 'forward' : 'back',
+ before: -baseTzOffset,
+ after: -tzOffset
+ })
+ baseTzOffset = tzOffset
+ } else {
+ // transition was not at the start of the day, so it must have happened
+ // yesterday. Back up one day and find the minute where it happened.
+ let transitionDate = new Date(date.getTime())
+ transitionDate.setDate(transitionDate.getDate() - 1)
+
+ // Iterate through each 5 mins of the day until we find a transition.
+ // TODO: this could be optimized to search hours then minutes or by or
+ // by using a binary search.
+ const dayNumber = transitionDate.getDate()
+ while (
+ isValidDate(transitionDate) &&
+ transitionDate.getDate() === dayNumber
+ ) {
+ tzOffset = transitionDate.getTimezoneOffset()
+ if (baseTzOffset !== tzOffset) {
+ transitions.push({
+ date: transitionDate,
+ type: tzOffset < baseTzOffset ? 'forward' : 'back',
+ before: -baseTzOffset,
+ after: -tzOffset
+ })
+ baseTzOffset = tzOffset
+ break // assuming only 1 transition per day
+ }
+ transitionDate = fiveMinutesLater(transitionDate)
+ }
+ }
+ }
+ date = oneDayLater(date)
+ } while (date.getFullYear() === year)
+ return transitions
+}