Skip to content

Commit

Permalink
Add getTimezoneOffsetInMilliseconds utility function (#789)
Browse files Browse the repository at this point in the history
  • Loading branch information
leshakoss committed Jun 21, 2018
1 parent ee2eebe commit 7353d74
Show file tree
Hide file tree
Showing 14 changed files with 84 additions and 102 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -639,6 +639,9 @@ for the list of changes made since `v2.0.0-alpha.1`.

- `isDate` now works properly with dates passed across iframes [#754](https://github.com/date-fns/date-fns/pull/754).

- Fix a few bugs that appear in timezones with offsets that include seconds (e.g. GMT+00:57:44).
See PR [#789](https://github.com/date-fns/date-fns/pull/789).

## [1.28.5] - 2017-05-19

### Fixed
Expand Down
15 changes: 0 additions & 15 deletions src/_lib/addUTCMinutes/index.js

This file was deleted.

61 changes: 0 additions & 61 deletions src/_lib/addUTCMinutes/test.js

This file was deleted.

21 changes: 21 additions & 0 deletions src/_lib/getTimezoneOffsetInMilliseconds/index.js
@@ -0,0 +1,21 @@
var MILLISECONDS_IN_MINUTE = 60000

/**
* Google Chrome as of 67.0.3396.87 introduced timezones with offset that includes seconds.
* They usually appear for dates that denote time before the timezones were introduced
* (e.g. for 'Europe/Prague' timezone the offset is GMT+00:57:44 before 1 October 1891
* and GMT+01:00:00 after that date)
*
* Date#getTimezoneOffset returns the offset in minutes and would return 57 for the example above,
* which would lead to incorrect calculations.
*
* This function returns the timezone offset in milliseconds that takes seconds in account.
*/
export default function getTimezoneOffsetInMilliseconds (dirtyDate) {
var date = new Date(dirtyDate.getTime())
var baseTimezoneOffset = date.getTimezoneOffset()
date.setSeconds(0, 0)
var millisecondsPartOfTimezoneOffset = date.getTime() % MILLISECONDS_IN_MINUTE

return baseTimezoneOffset * MILLISECONDS_IN_MINUTE + millisecondsPartOfTimezoneOffset
}
28 changes: 28 additions & 0 deletions src/_lib/getTimezoneOffsetInMilliseconds/test.js
@@ -0,0 +1,28 @@
// @flow
/* eslint-env mocha */

import assert from 'power-assert'
import getTimezoneOffsetInMilliseconds from '.'

describe('getTimezoneOffsetInMilliseconds', function () {
it('works for a modern date', function () {
var date = new Date(2018, 0 /* Jan */, 1, 12, 34, 56, 789)
var result = date.getTime() - getTimezoneOffsetInMilliseconds(date)
var expectedResult = Date.UTC(2018, 0 /* Jan */, 1, 12, 34, 56, 789)
assert(result === expectedResult)
})

it('works for a date before standardized timezones', function () {
var date = new Date(1800, 0 /* Jan */, 1, 12, 34, 56, 789)
var result = date.getTime() - getTimezoneOffsetInMilliseconds(date)
var expectedResult = Date.UTC(1800, 0 /* Jan */, 1, 12, 34, 56, 789)
assert(result === expectedResult)
})

it('works for a date BC', function () {
var date = new Date(-500, 0 /* Jan */, 1, 12, 34, 56, 789)
var result = date.getTime() - getTimezoneOffsetInMilliseconds(date)
var expectedResult = Date.UTC(-500, 0 /* Jan */, 1, 12, 34, 56, 789)
assert(result === expectedResult)
})
})
6 changes: 3 additions & 3 deletions src/differenceInCalendarDays/index.js
@@ -1,6 +1,6 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import startOfDay from '../startOfDay/index.js'

var MILLISECONDS_IN_MINUTE = 60000
var MILLISECONDS_IN_DAY = 86400000

/**
Expand Down Expand Up @@ -45,9 +45,9 @@ export default function differenceInCalendarDays (dirtyDateLeft, dirtyDateRight,
var startOfDayRight = startOfDay(dirtyDateRight, dirtyOptions)

var timestampLeft = startOfDayLeft.getTime() -
startOfDayLeft.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfDayLeft)
var timestampRight = startOfDayRight.getTime() -
startOfDayRight.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfDayRight)

// Round the number of days to the nearest integer
// because the number of milliseconds in a day is not constant
Expand Down
6 changes: 3 additions & 3 deletions src/differenceInCalendarISOWeeks/index.js
@@ -1,6 +1,6 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import startOfISOWeek from '../startOfISOWeek/index.js'

var MILLISECONDS_IN_MINUTE = 60000
var MILLISECONDS_IN_WEEK = 604800000

/**
Expand Down Expand Up @@ -38,9 +38,9 @@ export default function differenceInCalendarISOWeeks (dirtyDateLeft, dirtyDateRi
var startOfISOWeekRight = startOfISOWeek(dirtyDateRight, dirtyOptions)

var timestampLeft = startOfISOWeekLeft.getTime() -
startOfISOWeekLeft.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfISOWeekLeft)
var timestampRight = startOfISOWeekRight.getTime() -
startOfISOWeekRight.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfISOWeekRight)

// Round the number of days to the nearest integer
// because the number of milliseconds in a week is not constant
Expand Down
6 changes: 3 additions & 3 deletions src/differenceInCalendarWeeks/index.js
@@ -1,6 +1,6 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import startOfWeek from '../startOfWeek/index.js'

var MILLISECONDS_IN_MINUTE = 60000
var MILLISECONDS_IN_WEEK = 604800000

/**
Expand Down Expand Up @@ -49,9 +49,9 @@ export default function differenceInCalendarWeeks (dirtyDateLeft, dirtyDateRight
var startOfWeekRight = startOfWeek(dirtyDateRight, dirtyOptions)

var timestampLeft = startOfWeekLeft.getTime() -
startOfWeekLeft.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfWeekLeft)
var timestampRight = startOfWeekRight.getTime() -
startOfWeekRight.getTimezoneOffset() * MILLISECONDS_IN_MINUTE
getTimezoneOffsetInMilliseconds(startOfWeekRight)

// Round the number of days to the nearest integer
// because the number of milliseconds in a week is not constant
Expand Down
7 changes: 4 additions & 3 deletions src/format/index.js
@@ -1,10 +1,11 @@
import toInteger from '../_lib/toInteger/index.js'
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import toDate from '../toDate/index.js'
import isValid from '../isValid/index.js'
import defaultLocale from '../locale/en-US/index.js'
import formatters from './_lib/formatters/index.js'
import longFormatters from './_lib/longFormatters/index.js'
import addUTCMinutes from '../_lib/addUTCMinutes/index.js'
import subMilliseconds from '../subMilliseconds/index.js'

// This RegExp consists of three parts separated by `|`:
// - [yYQqMLwIdDecihHKkms]o matches any available ordinal number token
Expand Down Expand Up @@ -359,8 +360,8 @@ export default function format (dirtyDate, dirtyFormatStr, dirtyOptions) {
// Convert the date in system timezone to the same date in UTC+00:00 timezone.
// This ensures that when UTC functions will be implemented, locales will be compatible with them.
// See an issue about UTC functions: https://github.com/date-fns/date-fns/issues/376
var timezoneOffset = originalDate.getTimezoneOffset()
var utcDate = addUTCMinutes(originalDate, -timezoneOffset, options)
var timezoneOffset = getTimezoneOffsetInMilliseconds(originalDate)
var utcDate = subMilliseconds(originalDate, timezoneOffset, options)

var formatterOptions = {
firstWeekContainsDate: firstWeekContainsDate,
Expand Down
5 changes: 3 additions & 2 deletions src/formatDistance/index.js
@@ -1,3 +1,4 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import compareAsc from '../compareAsc/index.js'
import toDate from '../toDate/index.js'
import differenceInSeconds from '../differenceInSeconds/index.js'
Expand Down Expand Up @@ -130,8 +131,8 @@ export default function formatDistance (dirtyDate, dirtyBaseDate, dirtyOptions)
}

var seconds = differenceInSeconds(dateRight, dateLeft, options)
var offset = dateRight.getTimezoneOffset() - dateLeft.getTimezoneOffset()
var minutes = Math.round(seconds / 60) - offset
var offsetInSeconds = (getTimezoneOffsetInMilliseconds(dateRight) - getTimezoneOffsetInMilliseconds(dateLeft)) / 1000
var minutes = Math.round((seconds - offsetInSeconds) / 60)
var months

// 0 up to 2 mins
Expand Down
5 changes: 3 additions & 2 deletions src/formatDistanceStrict/index.js
@@ -1,3 +1,4 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import compareAsc from '../compareAsc/index.js'
import toDate from '../toDate/index.js'
import differenceInSeconds from '../differenceInSeconds/index.js'
Expand Down Expand Up @@ -145,8 +146,8 @@ export default function formatDistanceStrict (dirtyDate, dirtyBaseDate, dirtyOpt
}

var seconds = differenceInSeconds(dateRight, dateLeft, dirtyOptions)
var offset = dateRight.getTimezoneOffset() - dateLeft.getTimezoneOffset()
var minutes = roundingMethodFn(seconds / 60) - offset
var offsetInSeconds = (getTimezoneOffsetInMilliseconds(dateRight) - getTimezoneOffsetInMilliseconds(dateLeft)) / 1000
var minutes = roundingMethodFn((seconds - offsetInSeconds) / 60)

var unit
if (options.unit == null) {
Expand Down
7 changes: 4 additions & 3 deletions src/formatRelative/index.js
@@ -1,8 +1,9 @@
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import toDate from '../toDate/index.js'
import format from '../format/index.js'
import differenceInCalendarDays from '../differenceInCalendarDays/index.js'
import defaultLocale from '../locale/en-US/index.js'
import subMinutes from '../subMinutes/index.js'
import subMilliseconds from '../subMilliseconds/index.js'

/**
* @name formatRelative
Expand Down Expand Up @@ -78,8 +79,8 @@ export default function formatRelative (dirtyDate, dirtyBaseDate, dirtyOptions)
token = 'other'
}

var utcDate = subMinutes(date, date.getTimezoneOffset(), options)
var utcBaseDate = subMinutes(baseDate, date.getTimezoneOffset(), options)
var utcDate = subMilliseconds(date, getTimezoneOffsetInMilliseconds(date), options)
var utcBaseDate = subMilliseconds(baseDate, getTimezoneOffsetInMilliseconds(baseDate), options)
var formatStr = locale.formatRelative(token, utcDate, utcBaseDate, options)
return format(date, formatStr, options)
}
5 changes: 3 additions & 2 deletions src/parse/index.js
@@ -1,6 +1,7 @@
import toInteger from '../_lib/toInteger/index.js'
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import toDate from '../toDate/index.js'
import subMinutes from '../subMinutes/index.js'
import subMilliseconds from '../subMilliseconds/index.js'
import defaultLocale from '../locale/en-US/index.js'
import parsers from './_lib/parsers/index.js'

Expand Down Expand Up @@ -416,7 +417,7 @@ export default function parse (dirtyDateString, dirtyFormatString, dirtyBaseDate
// Convert the date in system timezone to the same date in UTC+00:00 timezone.
// This ensures that when UTC functions will be implemented, locales will be compatible with them.
// See an issue about UTC functions: https://github.com/date-fns/date-fns/issues/37
var utcDate = subMinutes(date, date.getTimezoneOffset())
var utcDate = subMilliseconds(date, getTimezoneOffsetInMilliseconds(date))

for (i = 0; i < uniquePrioritySetters.length; i++) {
var setter = uniquePrioritySetters[i]
Expand Down
11 changes: 6 additions & 5 deletions src/toDate/index.js
@@ -1,4 +1,5 @@
import toInteger from '../_lib/toInteger/index.js'
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'

var MILLISECONDS_IN_HOUR = 3600000
var MILLISECONDS_IN_MINUTE = 60000
Expand Down Expand Up @@ -128,11 +129,11 @@ export default function toDate (argument, dirtyOptions) {
offset = parseTimezone(dateStrings.timezone)
} else {
// get offset accurate to hour in timezones that change offset
offset = new Date(timestamp + time).getTimezoneOffset()
offset = new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE).getTimezoneOffset()
offset = getTimezoneOffsetInMilliseconds(new Date(timestamp + time))
offset = getTimezoneOffsetInMilliseconds(new Date(timestamp + time + offset))
}

return new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE)
return new Date(timestamp + time + offset)
} else {
return new Date(NaN)
}
Expand Down Expand Up @@ -314,14 +315,14 @@ function parseTimezone (timezoneString) {
// ±hh
token = patterns.timezoneHH.exec(timezoneString)
if (token) {
absoluteOffset = parseInt(token[2], 10) * 60
absoluteOffset = parseInt(token[2], 10) * MILLISECONDS_IN_HOUR
return (token[1] === '+') ? -absoluteOffset : absoluteOffset
}

// ±hh:mm or ±hhmm
token = patterns.timezoneHHMM.exec(timezoneString)
if (token) {
absoluteOffset = parseInt(token[2], 10) * 60 + parseInt(token[3], 10)
absoluteOffset = parseInt(token[2], 10) * MILLISECONDS_IN_HOUR + parseInt(token[3], 10) * MILLISECONDS_IN_MINUTE
return (token[1] === '+') ? -absoluteOffset : absoluteOffset
}

Expand Down

0 comments on commit 7353d74

Please sign in to comment.