Skip to content

Commit

Permalink
feat: Add a new getUTCOffset method
Browse files Browse the repository at this point in the history
Allows more lightweight time zone computations, if the time object is implemented in a high-level library. Returns only the time zone abbreviation and the difference to UTC to be added to the time value to get the unix time.
  • Loading branch information
prantlf committed Sep 16, 2018
1 parent bbfdc1a commit 60fb64e
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 24 deletions.
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Lightweight time zone listing and date converting. Intended for adding time zone
- [Format a date/time to a custom string](#format-a-datetime-to-a-custom-string)
- [Parse a date/time from a custom string](#parse-a-datetime-from-a-custom-string)
- [Set time zone to a zone-less date](#set-time-zone-to-a-zone-less-date)
- [Get UTC offset for a specific time zone](#get-utc-offset-for-a-specific-time-zone)
- [API Reference](#api-reference)
- [Library Integrations](#library-integrations)
- [Contributing](#contributing)
Expand Down Expand Up @@ -138,7 +139,7 @@ const berlinTime = getZonedTime(unixTime, berlin)

### Convert a date from a specific time zone to UTC

Dates are supposed to be displayed in a time zone chosen by the user, but they are usually stored in UTC. The result of the conversion can be stored safely to prevent the time zone information from being lost.
Dates are usually entered in a time zone chosen by the user, but they are supposed to be stored in UTC. The result of the conversion can be stored safely to prevent the time zone information from being lost.

```js
const { findTimeZone, getUnixTime } = require('timezone-support')
Expand Down Expand Up @@ -214,6 +215,28 @@ const zonedTime = setTimeZone(zonelessLocalDate, berlin, { useUTC: false })

See the function [formatZonedTime](#formatzonedtime) for more information.

### Get UTC offset for a specific date and time zone

Libraries usually provide all what is needed to parse, format, compare or manipulate a date value. They accept the native `Date` object, which offers access to date and time parts in the local time zone and UTC. The UTC offset of an arbitrary time zone can be used to construct a `Date` instance, which returns its date and time parts in the specified time zone. Such instance can be used for formatting, except for its timestamp and time zone offset, which are wrong.

```js
const { findTimeZone, getUTCOffset } = require('timezone-support')
const berlin = findTimeZone('Europe/Berlin')

// Date timestamp in UTC
const unixTime = Date.UTC(2018, 8, 2, 10, 0)
// Request the UTC offset for this day in the "Europe/Berlin" time zone
const { offset } = getUTCOffset(unixTime, berlin)
// Create a new Date instance with date and time parts in the "Europe/Berlin" time zone
const berlinDate = new Date(unixTime - offset)
// Returns "2018-9-2 12:00:00" across the globe
const formattedDate = berlinDate.toLocaleString()
// Only date and time part getters are allowed to be used on this Date instance:
// getFullYear, getMonth, getDate, getHours, getMinutes, getSeconds and getMilliseconds
```

See the function [getUTCOffset](#getutcoffset) for more information.

## API Reference

The API consists of functions only. They are divided to three modules, which you can load depending on your usage scenario.
Expand All @@ -229,9 +252,10 @@ Provides the same functions as the module `timezone-support/dist/index`, but doe
#### populateTimeZones
#### listTimeZones
#### findTimeZone
#### setTimeZone
#### getUTCOffset
#### getZonedTime
#### getUnixTime
#### setTimeZone

### timezone-support/dist/parse-format

Expand All @@ -242,15 +266,16 @@ Offers a minimal date parsing and formatting support, if you want to use this li

## Library Integrations

### Day.js
### date-fns
* [Day.js] - [timeZone plugin] supplies parsing from and formatting to an arbitrary time zone
* [date-fns]

## Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Grunt.

## Release History

* 2018-09-16 v1.1.0 Add a new getUTCOffset method for more lightweight integrations.
* 2018-09-02 v1.0.0 Initial release

## License
Expand All @@ -262,5 +287,6 @@ Licensed under the MIT license.
[Node.js]: http://nodejs.org/
[NPM]: https://www.npmjs.com/
[RequireJS]: https://requirejs.org/
[day.js]: https://github.com/iamkun/dayjs
[Day.js]: https://github.com/iamkun/dayjs
[date-fns]: https://github.com/date-fns/date-fns
[timeZone plugin]: https://github.com/prantlf/dayjs/blob/combined/docs/en/Plugin.md#timezone
2 changes: 1 addition & 1 deletion perf/formatZonedTime.perf.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ function formatZonedTimeUsingDate () {

createSuite('Formatting a string with a local time...')
.add('Date:toString', formatZonedTimeUsingDate)
.add('formatZonedTime', () => formatZonedTime(time, 'D.M.Y H:MM:ss.SSS Z'))
.add('formatZonedTime', () => formatZonedTime(time, 'D.M.Y H:MM:ss.SSS [GMT]Z (z)'))
.start()
4 changes: 2 additions & 2 deletions perf/parseZonedTime.perf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const createSuite = require('./createSuite')
const { parseZonedTime } = require('../dist/parse-format')

const nativeInput = new Date('2018-09-01T18:01:36.386Z').toString()
const localInput = '1.9.2018 18:01:36.386 +02:00'
const localInput = '1.9.2018 18:01:36.386 GMT+02:00 (CET)'

function parseZonedTimeUsingDate () {
const date = new Date(nativeInput)
Expand All @@ -19,5 +19,5 @@ function parseZonedTimeUsingDate () {

createSuite('Parsing a string with a local time...')
.add('Date:constructor', parseZonedTimeUsingDate)
.add('parseZonedTime', () => parseZonedTime(localInput, 'D.M.Y H:MM:ss.SSS Z'))
.add('parseZonedTime', () => parseZonedTime(localInput, 'D.M.Y H:MM:ss.SSS [GMT]Z (z)'))
.start()
30 changes: 18 additions & 12 deletions src/convert/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,10 @@ function attachEpoch (time, unixTime) {
Object.defineProperty(time, 'epoch', { value: unixTime })
}

function setTimeZone (time, timeZone, options) {
if (time instanceof Date) {
time = getDateTime(time, options)
} else {
const { year, month, day, hours, minutes, seconds = 0, milliseconds = 0 } = time
time = { year, month, day, hours, minutes, seconds, milliseconds }
}
const unixTime = getUnixTimeFromUTC(time)
function getUTCOffset (date, timeZone) {
const unixTime = typeof date === 'number' ? date : date.valueOf()
const { abbreviation, offset } = getTransition(unixTime, timeZone)
time.zone = { abbreviation, offset }
attachEpoch(time, unixTime + offset * 60000)
return time
return { abbreviation, offset }
}

function getZonedTime (date, timeZone) {
Expand Down Expand Up @@ -69,4 +61,18 @@ function getUnixTime (time, timeZone) {
return unixTime + zone.offset * 60000
}

export { setTimeZone, getZonedTime, getUnixTime }
function setTimeZone (time, timeZone, options) {
if (time instanceof Date) {
time = getDateTime(time, options)
} else {
const { year, month, day, hours, minutes, seconds = 0, milliseconds = 0 } = time
time = { year, month, day, hours, minutes, seconds, milliseconds }
}
const unixTime = getUnixTimeFromUTC(time)
const { abbreviation, offset } = getTransition(unixTime, timeZone)
time.zone = { abbreviation, offset }
attachEpoch(time, unixTime + offset * 60000)
return time
}

export { getUTCOffset, getZonedTime, getUnixTime, setTimeZone }
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
populateTimeZones, listTimeZones, findTimeZone, setTimeZone, getZonedTime, getUnixTime
populateTimeZones, listTimeZones, findTimeZone, getUTCOffset, getZonedTime, getUnixTime, setTimeZone
} from './lookup-convert'
import data from './lookup/data'

populateTimeZones(data)

export { listTimeZones, findTimeZone, setTimeZone, getZonedTime, getUnixTime }
export { listTimeZones, findTimeZone, getUTCOffset, getZonedTime, getUnixTime, setTimeZone }
4 changes: 2 additions & 2 deletions src/lookup-convert.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { populateTimeZones, listTimeZones, findTimeZone } from './lookup/lookup'
import { setTimeZone, getZonedTime, getUnixTime } from './convert/convert'
import { getUTCOffset, getZonedTime, getUnixTime, setTimeZone } from './convert/convert'

export {
populateTimeZones, listTimeZones, findTimeZone, setTimeZone, getZonedTime, getUnixTime
populateTimeZones, listTimeZones, findTimeZone, getUTCOffset, getZonedTime, getUnixTime, setTimeZone
}
29 changes: 29 additions & 0 deletions test/getUTCOffset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* global beforeAll, it, expect */

const { findTimeZone, getUTCOffset } = require('../dist/index')

let berlin

beforeAll(() => {
berlin = findTimeZone('Europe/Berlin')
})

it('is exported as a function', () => {
expect(typeof getUTCOffset === 'function').toBeTruthy()
})

it('computes the time zone from the UNIX time', () => {
const unixTime = Date.UTC(2018, 0, 2, 9, 30, 15, 234)
const zone = getUTCOffset(unixTime, berlin)
expect(typeof zone === 'object').toBeTruthy()
expect(zone.abbreviation).toEqual('CET')
expect(zone.offset).toEqual(-60)
})

it('accepts a Date object instead of a numeric UNIX time', () => {
const utcDate = new Date(Date.UTC(2018, 6, 2, 9, 30, 15, 234))
const zone = getUTCOffset(utcDate, berlin)
expect(typeof zone === 'object').toBeTruthy()
expect(zone.abbreviation).toEqual('CEST')
expect(zone.offset).toEqual(-120)
})

0 comments on commit 60fb64e

Please sign in to comment.