Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on Moment library in exchange for date-fns-tz #8

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"colors": true
},
"dependencies": {
"moment-timezone": "^0.5.39"
"date-fns-tz": "^1.3.7"
},
"devDependencies": {
"c8": "^7.12.0",
Expand All @@ -68,7 +68,8 @@
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"rollup": "^3.3.0",
"typescript": "^4.9.3"
"typescript": "^4.9.3",
"date-fns":"^2.29.3"
},
"engines": {
"node": ">=12.0.0"
Expand Down
32 changes: 20 additions & 12 deletions src/CalDate.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import moment from 'moment-timezone'
import { toYear, toNumber, isDate, pad0 } from './utils.js'
import utcToZonedTime from 'date-fns-tz/utcToZonedTime'
import zonedTimeToUtc from 'date-fns-tz/zonedTimeToUtc'

const PROPS = ['year', 'month', 'day', 'hour', 'minute', 'second']

Expand Down Expand Up @@ -180,9 +180,17 @@ export class CalDate {
* @param {String} timezone - e.g. 'America/New_York'
* @return {Date}
*/
toTimezone (timezone) {
if (timezone) {
return new Date(moment.tz(this.toString(), timezone).format())
toTimezone (timeZone) {
if (timeZone) {
const returnVar = zonedTimeToUtc(this.toString(), timeZone)
// hack alert - tehran has the only timezone which starts daylight saving at midnight (although stopped DST in 2022)
// once the bug in zoneTimeToUtc is fixed, delete this hack https://github.com/marnusw/date-fns-tz/issues/222
if (timeZone === 'Asia/Tehran') {
const i = new Intl.DateTimeFormat('en', { timeZone, hourCycle: 'h23', hour: 'numeric' })
const f = parseInt(i.format(returnVar), 10)
if (f !== this.hour) returnVar.setHours(returnVar.getHours() + 1)
}
return returnVar
} else {
return this.toDate()
}
Expand All @@ -196,13 +204,13 @@ export class CalDate {
*/
fromTimezone (dateUTC, timezone) {
if (timezone) {
const m = moment.tz(dateUTC, timezone)
this.year = m.year()
this.month = m.month() + 1
this.day = m.date()
this.hour = m.hours()
this.minute = m.minutes()
this.second = m.seconds()
const m = utcToZonedTime(dateUTC, timezone)
this.year = m.getFullYear()
this.month = m.getMonth() + 1
this.day = m.getDate()
this.hour = m.getHours()
this.minute = m.getMinutes()
this.second = m.getSeconds()
} else {
this.set(dateUTC)
}
Expand Down
67 changes: 67 additions & 0 deletions test/CalDate.mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ describe('#CalDate', function () {
assert.strictEqual(res, '2000-01-01T05:00:00.000Z')
})

it('can move date by timezone with daylight saving offset', function () {
const caldate = new CalDate(new Date('2000-07-01 00:00:00'))
const res = caldate.toTimezone('America/New_York').toISOString()
assert.strictEqual(res, '2000-07-01T04:00:00.000Z')
})

it('can return date in current timezone', function () {
const caldate = new CalDate({ year: 2000, month: 1, day: 1 })
const exp = new Date('2000-01-01 00:00:00')
Expand Down Expand Up @@ -199,3 +205,64 @@ describe('#CalDate', function () {
assert.strictEqual(res, exp)
})
})

describe('handles daylight saving jumps', function () {
it('finds first time after clock jumps back: -ve UTC offset', function () {
const caldate = new CalDate(new Date('2023-11-05 02:00:00'))
const res = caldate.toTimezone('America/New_York').toISOString()
// UTC 05:59 is NY 01:59 GMT -4
// UTC 06:00 is NY 01:00 GMT -5
// therefore the first time NY local 02:00 is struck is UTC 07:00
assert.strictEqual(res, '2023-11-05T07:00:00.000Z')
})

it('finds first time after clock jumps back: +ve UTC offset', function () {
const caldate = new CalDate(new Date('2023-04-02 03:00:00'))
const res = caldate.toTimezone('Australia/Sydney').toISOString()
// UTC 15:59 is SYD 02:59 GMT +11
// UTC 16:00 is SYD 02:00 GMT +10
// therefore the first time SYD local 03:00 is struck is UTC 17:00
assert.strictEqual(res, '2023-04-01T17:00:00.000Z')
})

it('handles times that repeat when clock jump back: -ve UTC offset', function () {
// at 02:00 local clock jumps back 1 hour so 01:00 occurs twice
const caldate = new CalDate(new Date('2023-11-05 01:00:00'))
const res = caldate.toTimezone('America/New_York').toISOString()
// UTC 05:00 is NY 01:00 GMT -4
// UTC 06:00 is NY 01:00 GMT -5
// this implementation picks the later occurrence of 01:00 @ UTC 06:00
assert.strictEqual(res, '2023-11-05T06:00:00.000Z')
})

it('handles times that repeat when clock jump back: +ve UTC offset', function () {
// at 03:00 local clock jumps back 1 hour so 02:00 occurs twice
const caldate = new CalDate(new Date('2023-04-02 02:00:00'))
const res = caldate.toTimezone('Australia/Sydney').toISOString()
// UTC 15:00 is SYD 02:00 GMT +11
// UTC 16:00 is SYD 02:00 GMT +10
// this implementation picks the later occurrence of 02:00 @ UTC 16:00
assert.strictEqual(res, '2023-04-01T16:00:00.000Z')
})

// utc: 2023-09-30T16:00:00.000Z SYD: 03:00 GMT+11
it('handles times that dont exist with clock jump forward: -ve UTC offset', function () {
// at 02:00 local clock will immediately jump forward to 03:00
const caldate = new CalDate(new Date('2023-03-12 02:00:00'))
const res = caldate.toTimezone('America/New_York').toISOString()
// UTC 06:59 is NY 01:59 GMT -5
// UTC 07:00 is NY 03:00 GMT -4
// !! this is an error - should either throw error or return UTC 07:00
assert.strictEqual(res, '2023-03-12T06:00:00.000Z')
})

it('handles times that dont exist with clock jump forward: +ve UTC offset', function () {
// at 02:00 local clock will immediately jump forward to 03:00
const caldate = new CalDate(new Date('2023-10-01 02:00:00'))
const res = caldate.toTimezone('Australia/Sydney').toISOString()
// UTC 15:59 is SYD 01:59 GMT +10
// UTC 16:00 is SYD 03:00 GMT +11
// !! this is an error - should either throw error or return UTC 16:00
assert.strictEqual(res, '2023-09-30T15:00:00.000Z')
})
})