Skip to content

Commit

Permalink
Fixed issue: Wrong time zone offset near DST boundaries #93
Browse files Browse the repository at this point in the history
Fixed Time Zone offset calculation. I think that this change will fix also a lot of other Time Zone related issues.

(cherry picked from commit eb2bb62)
  • Loading branch information
bsvetlik authored and yharaskrik committed Feb 8, 2021
1 parent 16d0880 commit e97b76c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 25 deletions.
65 changes: 60 additions & 5 deletions src/_lib/tzParseTimezone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var patterns = {
}

// Parse various time zone offset formats to an offset in milliseconds
export default function tzParseTimezone(timezoneString, date) {
export default function tzParseTimezone(timezoneString, date, isUtcDate) {
var token
var absoluteOffset

Expand Down Expand Up @@ -56,15 +56,70 @@ export default function tzParseTimezone(timezoneString, date) {
if (token) {
date = new Date(date || Date.now())
date.setMilliseconds(0)
// var [fYear, fMonth, fDay, fHour, fMinute, fSecond] = tzTokenizeDate(date, timezoneString)
var tokens = tzTokenizeDate(date, timezoneString)
var asUTC = Date.UTC(tokens[0], tokens[1] - 1, tokens[2], tokens[3], tokens[4], tokens[5])
return -(asUTC - date.getTime())
var utcDate = isUtcDate ? date : toUtcDate(date)

var offset = calcOffset(utcDate, timezoneString)

var fixedOffset = isUtcDate ? offset : fixOffset(date, offset, timezoneString)

return -fixedOffset
}

return 0
}

function toUtcDate(date) {
return new Date(
Date.UTC(
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds()
)
)
}

function calcOffset(date, timezoneString) {
var [year, month, day, hour, minute, second] = tzTokenizeDate(date, timezoneString)

This comment has been minimized.

Copy link
@fgordillo

fgordillo Feb 22, 2021

This change breaks IE11 support


var asUTC = Date.UTC(year, month - 1, day, hour % 24, minute, second)

var asTS = date.getTime()
var over = asTS % 1000
asTS -= over >= 0 ? over : 1000 + over
return asUTC - asTS
}

function fixOffset(date, offset, timezoneString) {
var localTS = date.getTime()

// Our UTC time is just a guess because our offset is just a guess
var utcGuess = localTS - offset

// Test whether the zone matches the offset for this ts
var o2 = calcOffset(new Date(utcGuess), timezoneString)

// If so, offset didn't change and we're done
if (offset === o2) {
return offset
}

// If not, change the ts by the difference in the offset
utcGuess -= o2 - offset

// If that gives us the local time we want, we're done
var o3 = calcOffset(new Date(utcGuess), timezoneString)
if (o2 === o3) {
return o2
}

// If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time
return Math.max(o2, o3)
}

function validateTimezone(hours, minutes) {
if (minutes != null && (minutes < 0 || minutes > 59)) {
return false
Expand Down
7 changes: 0 additions & 7 deletions src/toDate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,6 @@ export default function toDate(argument, dirtyOptions) {
if (isNaN(offset)) {
return new Date(NaN)
}
offset = tzParseTimezone(
dateStrings.timezone || options.timeZone,
new Date(timestamp + time + offset)
)
if (isNaN(offset)) {
return new Date(NaN)
}
} else {
// get offset accurate to hour in timezones that change offset
offset = getTimezoneOffsetInMilliseconds(new Date(timestamp + time))
Expand Down
26 changes: 13 additions & 13 deletions src/utcToZonedTime/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import tzParseTimezone from '../_lib/tzParseTimezone'
import subMilliseconds from 'date-fns/subMilliseconds'
import toDate from '../toDate'

/**
Expand Down Expand Up @@ -29,18 +28,19 @@ import toDate from '../toDate'
export default function utcToZonedTime(dirtyDate, timeZone, options) {
var date = toDate(dirtyDate, options)

// This date has the UTC time values of the input date at the system time zone
var utcDate = new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
date.getUTCMilliseconds()
var offsetMilliseconds = tzParseTimezone(timeZone, date, true) || 0

var d = new Date(date.getTime() - offsetMilliseconds)

var zonedTime = new Date(
d.getUTCFullYear(),
d.getUTCMonth(),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds(),
d.getUTCMilliseconds()
)
// We just need to apply the offset indicated by the time zone to this localized date
var offsetMilliseconds = tzParseTimezone(timeZone, utcDate)

return offsetMilliseconds ? subMilliseconds(utcDate, offsetMilliseconds) : utcDate
return zonedTime
}

0 comments on commit e97b76c

Please sign in to comment.