diff --git a/README.md b/README.md index cbd411b..b73e648 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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') @@ -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. @@ -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 @@ -242,8 +266,8 @@ 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 @@ -251,6 +275,7 @@ In lieu of a formal styleguide, take care to maintain the existing coding style. ## 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 @@ -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 diff --git a/perf/formatZonedTime.perf.js b/perf/formatZonedTime.perf.js index 13998ab..d3bf25e 100644 --- a/perf/formatZonedTime.perf.js +++ b/perf/formatZonedTime.perf.js @@ -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() diff --git a/perf/parseZonedTime.perf.js b/perf/parseZonedTime.perf.js index a50d690..beaf474 100644 --- a/perf/parseZonedTime.perf.js +++ b/perf/parseZonedTime.perf.js @@ -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) @@ -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() diff --git a/src/convert/convert.js b/src/convert/convert.js index 0efce92..695ee2a 100644 --- a/src/convert/convert.js +++ b/src/convert/convert.js @@ -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) { @@ -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 } diff --git a/src/index.js b/src/index.js index 90e2d66..70aae5f 100644 --- a/src/index.js +++ b/src/index.js @@ -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 } diff --git a/src/lookup-convert.js b/src/lookup-convert.js index b968798..189545f 100644 --- a/src/lookup-convert.js +++ b/src/lookup-convert.js @@ -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 } diff --git a/test/getUTCOffset.test.js b/test/getUTCOffset.test.js new file mode 100644 index 0000000..cfcb74b --- /dev/null +++ b/test/getUTCOffset.test.js @@ -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) +})