diff --git a/README.md b/README.md index b6d5f4c..cbd411b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![NPM Downloads](https://nodei.co/npm/timezone-support.png?downloads=true&stars=true)](https://www.npmjs.com/package/timezone-support) -Low-level time zone listing and date converting. Intended for adding time zone support to high-level date libraries, but also for direct application usage. +Lightweight time zone listing and date converting. Intended for adding time zone support to high-level date libraries, but also for direct application usage. * Tiny code base - 3.5 KB minified, 1.5 KB gzipped. Do not pack unnecessary weight in your application. * Packed time zone data - 175 KB minified, 22.5 KB gzipped. Single time zones are unpacked on demand. @@ -251,7 +251,7 @@ In lieu of a formal styleguide, take care to maintain the existing coding style. ## Release History -* 2018-09-03 v1.0.0 Initial release +* 2018-09-02 v1.0.0 Initial release ## License diff --git a/package.json b/package.json index 1b73be3..e9406cd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "timezone-support", "version": "0.0.0-development", - "description": "Low-level time zone listing and date converting. Serves for adding time support to high-level date libraries.", + "description": "Lightweight time zone support for your applications or other date libraries.", "author": { "name": "Ferdinand Prantl", "email": "prantlf@gmail.com", diff --git a/src/convert/convert.js b/src/convert/convert.js index 22013a6..6915275 100644 --- a/src/convert/convert.js +++ b/src/convert/convert.js @@ -16,6 +16,10 @@ function getTransition (unixTime, timeZone) { return { abbreviation, offset } } +function attachEpoch (time, unixTime) { + Object.defineProperty(time, 'epoch', { value: unixTime }) +} + function setTimeZone (time, timeZone, options) { if (time instanceof Date) { const { useUTC } = options || {} @@ -28,12 +32,15 @@ function setTimeZone (time, timeZone, options) { throw new Error('Source of the date parts missing.') } time = extract(time) + } 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) - const zone = { abbreviation, offset } - const { year, month, day, hours, minutes, seconds = 0, milliseconds = 0 } = time - return { year, month, day, hours, minutes, seconds, milliseconds, zone } + time.zone = { abbreviation, offset } + attachEpoch(time, unixTime) + return time } function getZonedTime (date, timeZone) { @@ -45,12 +52,19 @@ function getZonedTime (date, timeZone) { } const time = getUTCTime(date) time.zone = { abbreviation, offset } + attachEpoch(time, unixTime) return time } function getUnixTime (time, timeZone) { + let { zone, epoch } = time + if (epoch) { + if (timeZone) { + throw new Error('Both epoch and other time zone specified.') + } + return epoch + } const unixTime = getUnixTimeFromUTC(time) - let { zone } = time if (zone) { if (timeZone) { throw new Error('Two time zones specified.') diff --git a/src/convert/utc-date.js b/src/convert/utc-date.js index 638ce80..b1f4717 100644 --- a/src/convert/utc-date.js +++ b/src/convert/utc-date.js @@ -20,8 +20,8 @@ function getLocalTime (date) { const day = date.getDate() const hours = date.getHours() const minutes = date.getMinutes() - const seconds = date.getSeconds() - const milliseconds = date.getMilliseconds() + const seconds = date.getSeconds() || 0 + const milliseconds = date.getMilliseconds() || 0 return { year, month, day, hours, minutes, seconds, milliseconds } } diff --git a/test/formatZonedTime.test.js b/test/formatZonedTime.test.js index e16f5e5..34c4027 100644 --- a/test/formatZonedTime.test.js +++ b/test/formatZonedTime.test.js @@ -7,7 +7,7 @@ it('is exported as a function', () => { }) it('formats a time object to a string with no padding needed', () => { - const honoluluDate = { + const honoluluTime = { year: 2017, month: 11, day: 15, @@ -20,12 +20,12 @@ it('formats a time object to a string with no padding needed', () => { offset: 600 } } - const string = formatZonedTime(honoluluDate, 'A S SS SSS s ss m mm h hh H HH D DD M MM Y YY YYYY z Z ZZ') + const string = formatZonedTime(honoluluTime, 'A S SS SSS s ss m mm h hh H HH D DD M MM Y YY YYYY z Z ZZ') expect(string).toEqual('PM 2 23 234 18 18 17 17 11 11 23 23 15 15 11 11 2017 17 2017 HST -10:00 -1000') }) it('pads single digits with zeros', () => { - const berlinDate = { + const berlinTime = { year: 1, month: 1, day: 2, @@ -38,24 +38,24 @@ it('pads single digits with zeros', () => { offset: -60 } } - const string = formatZonedTime(berlinDate, 'A S SS SSS s ss m mm h hh H HH D DD M MM Y YY YYYY z Z ZZ') + const string = formatZonedTime(berlinTime, 'A S SS SSS s ss m mm h hh H HH D DD M MM Y YY YYYY z Z ZZ') expect(string).toEqual('AM 0 00 004 5 05 3 03 9 09 9 09 2 02 1 01 1 01 0001 CET +01:00 +0100') }) it('pads pairs of digits with zeros', () => { - const berlinDate = { + const berlinTime = { year: 67, milliseconds: 34 } - const string = formatZonedTime(berlinDate, 'YYYY SSS') + const string = formatZonedTime(berlinTime, 'YYYY SSS') expect(string).toEqual('0067 034') }) it('pads triplets of digits with zeros', () => { - const berlinDate = { + const berlinTime = { year: 967 } - const string = formatZonedTime(berlinDate, 'YYYY') + const string = formatZonedTime(berlinTime, 'YYYY') expect(string).toEqual('0967') }) diff --git a/test/getUnixTime.test.js b/test/getUnixTime.test.js index 8986bd4..bf33618 100644 --- a/test/getUnixTime.test.js +++ b/test/getUnixTime.test.js @@ -13,7 +13,7 @@ it('is exported as a function', () => { }) it('converts the time object to the correct UNIX time', () => { - const berlinDate = { + const berlinTime = { year: 2018, month: 1, day: 2, @@ -26,14 +26,14 @@ it('converts the time object to the correct UNIX time', () => { offset: -60 } } - const unixTime = getUnixTime(berlinDate) + const unixTime = getUnixTime(berlinTime) expect(typeof unixTime === 'number').toBeTruthy() - const utcDate = new Date(Date.UTC(2018, 0, 2, 9, 30, 15, 234)) - expect(unixTime).toEqual(utcDate.valueOf()) + const epoch = Date.UTC(2018, 0, 2, 9, 30, 15, 234) + expect(unixTime).toEqual(epoch) }) it('accepts an explicit time zone as a parameter', () => { - const berlinDate = { + const berlinTime = { year: 2018, month: 1, day: 2, @@ -42,14 +42,14 @@ it('accepts an explicit time zone as a parameter', () => { seconds: 15, milliseconds: 234 } - const unixTime = getUnixTime(berlinDate, berlin) + const unixTime = getUnixTime(berlinTime, berlin) expect(typeof unixTime === 'number').toBeTruthy() - const utcDate = new Date(Date.UTC(2018, 0, 2, 9, 30, 15, 234)) - expect(unixTime).toEqual(utcDate.valueOf()) + const epoch = Date.UTC(2018, 0, 2, 9, 30, 15, 234) + expect(unixTime).toEqual(epoch) }) it('seconds and milliseconds are optional in the time object', () => { - const berlinDate = { + const berlinTime = { year: 2018, month: 1, day: 2, @@ -60,14 +60,14 @@ it('seconds and milliseconds are optional in the time object', () => { offset: -60 } } - const unixTime = getUnixTime(berlinDate) + const unixTime = getUnixTime(berlinTime) expect(typeof unixTime === 'number').toBeTruthy() - const utcDate = new Date(Date.UTC(2018, 0, 2, 9, 30)) - expect(unixTime).toEqual(utcDate.valueOf()) + const epoch = Date.UTC(2018, 0, 2, 9, 30) + expect(unixTime).toEqual(epoch) }) it('recognizes daylight-saving time', () => { - const berlinDate = { + const berlinTime = { year: 2018, month: 7, day: 2, @@ -78,14 +78,14 @@ it('recognizes daylight-saving time', () => { offset: -120 } } - const unixTime = getUnixTime(berlinDate) + const unixTime = getUnixTime(berlinTime) expect(typeof unixTime === 'number').toBeTruthy() - const utcDate = new Date(Date.UTC(2018, 6, 2, 9, 30)) - expect(unixTime).toEqual(utcDate.valueOf()) + const epoch = Date.UTC(2018, 6, 2, 9, 30) + expect(unixTime).toEqual(epoch) }) it('checks, that only one time zone is requested to convert from', () => { - const berlinDate = { + const berlinTime = { year: 2018, month: 7, day: 2, @@ -96,5 +96,36 @@ it('checks, that only one time zone is requested to convert from', () => { offset: -120 } } - expect(() => getUnixTime(berlinDate, berlin)).toThrow() + expect(() => getUnixTime(berlinTime, berlin)).toThrow() +}) + +it('returns the epoch, that only one time zone is requested to convert from', () => { + const berlinTime = { + year: 2018, + month: 7, + day: 2, + hours: 11, + minutes: 30, + epoch: 1530523800000, + zone: { + abbreviation: 'CEST', + offset: -120 + } + } + const unixTime = getUnixTime(berlinTime) + expect(typeof unixTime === 'number').toBeTruthy() + const epoch = Date.UTC(2018, 6, 2, 9, 30) + expect(unixTime).toEqual(epoch) +}) + +it('checks, that other time zone is not requested if epoch is included', () => { + const berlinTime = { + year: 2018, + month: 7, + day: 2, + hours: 11, + minutes: 30, + epoch: 1530523800000 + } + expect(() => getUnixTime(berlinTime, berlin)).toThrow() }) diff --git a/test/getZonedTime.test.js b/test/getZonedTime.test.js index ffbdd44..d49de8a 100644 --- a/test/getZonedTime.test.js +++ b/test/getZonedTime.test.js @@ -13,10 +13,10 @@ it('is exported as a function', () => { }) it('converts the UNIX time to the correct time object', () => { - const utcDate = new Date(Date.UTC(2018, 0, 2, 9, 30, 15, 234)) - const berlinDate = getZonedTime(utcDate.valueOf(), berlin) - expect(typeof berlinDate === 'object').toBeTruthy() - const { year, month, day, hours, minutes, seconds, milliseconds, zone } = berlinDate + const unixTime = Date.UTC(2018, 0, 2, 9, 30, 15, 234) + const berlinTime = getZonedTime(unixTime, berlin) + expect(typeof berlinTime === 'object').toBeTruthy() + const { year, month, day, hours, minutes, seconds, milliseconds, zone, epoch } = berlinTime expect(year).toEqual(2018) expect(month).toEqual(1) expect(day).toEqual(2) @@ -27,13 +27,14 @@ it('converts the UNIX time to the correct time object', () => { expect(typeof zone === 'object').toBeTruthy() expect(zone.abbreviation).toEqual('CET') expect(zone.offset).toEqual(-60) + expect(epoch).toEqual(1514885415234) }) it('recognizes daylight-saving time', () => { - const utcDate = new Date(Date.UTC(2018, 6, 2, 9, 30, 15, 234)) - const berlinDate = getZonedTime(utcDate.valueOf(), berlin) - expect(typeof berlinDate === 'object').toBeTruthy() - const { year, month, day, hours, minutes, seconds, milliseconds, zone } = berlinDate + const unixTime = Date.UTC(2018, 6, 2, 9, 30, 15, 234) + const berlinTime = getZonedTime(unixTime.valueOf(), berlin) + expect(typeof berlinTime === 'object').toBeTruthy() + const { year, month, day, hours, minutes, seconds, milliseconds, zone, epoch } = berlinTime expect(year).toEqual(2018) expect(month).toEqual(7) expect(day).toEqual(2) @@ -44,13 +45,14 @@ it('recognizes daylight-saving time', () => { expect(typeof zone === 'object').toBeTruthy() expect(zone.abbreviation).toEqual('CEST') expect(zone.offset).toEqual(-120) + expect(epoch).toEqual(1530523815234) }) 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 berlinDate = getZonedTime(utcDate, berlin) - expect(typeof berlinDate === 'object').toBeTruthy() - const { year, month, day, hours, minutes, seconds, milliseconds, zone } = berlinDate + const berlinTime = getZonedTime(utcDate, berlin) + expect(typeof berlinTime === 'object').toBeTruthy() + const { year, month, day, hours, minutes, seconds, milliseconds, zone, epoch } = berlinTime expect(year).toEqual(2018) expect(month).toEqual(7) expect(day).toEqual(2) @@ -61,14 +63,15 @@ it('accepts a Date object instead of a numeric UNIX time', () => { expect(typeof zone === 'object').toBeTruthy() expect(zone.abbreviation).toEqual('CEST') expect(zone.offset).toEqual(-120) + expect(epoch).toEqual(1530523815234) }) it('optimizes conversion to UTC', () => { const utc = findTimeZone('Etc/UTC') const utcDate = new Date(Date.UTC(2018, 6, 2, 9, 30, 15, 234)) - const berlinDate = getZonedTime(utcDate, utc) - expect(typeof berlinDate === 'object').toBeTruthy() - const { year, month, day, hours, minutes, seconds, milliseconds, zone } = berlinDate + const berlinTime = getZonedTime(utcDate, utc) + expect(typeof berlinTime === 'object').toBeTruthy() + const { year, month, day, hours, minutes, seconds, milliseconds, zone, epoch } = berlinTime expect(year).toEqual(2018) expect(month).toEqual(7) expect(day).toEqual(2) @@ -79,4 +82,5 @@ it('optimizes conversion to UTC', () => { expect(typeof zone === 'object').toBeTruthy() expect(zone.abbreviation).toEqual('UTC') expect(zone.offset).toEqual(0) + expect(epoch).toEqual(1530523815234) }) diff --git a/test/setTimeZone.test.js b/test/setTimeZone.test.js index 4d58287..123964a 100644 --- a/test/setTimeZone.test.js +++ b/test/setTimeZone.test.js @@ -4,7 +4,8 @@ const { findTimeZone, setTimeZone } = require('../dist/index') let berlin -function checkTime ({ year, month, day, hours, minutes, seconds, milliseconds, zone }, checkSeconds) { +function checkTime (time, checkSeconds) { + const { year, month, day, hours, minutes, seconds, milliseconds, zone, epoch } = time expect(year).toEqual(2018) expect(month).toEqual(1) expect(day).toEqual(2) @@ -13,9 +14,11 @@ function checkTime ({ year, month, day, hours, minutes, seconds, milliseconds, z if (checkSeconds) { expect(seconds).toEqual(40) expect(milliseconds).toEqual(50) + expect(epoch).toEqual(1514889040050) } else { expect(seconds).toEqual(0) expect(milliseconds).toEqual(0) + expect(epoch).toEqual(1514889000000) } expect(typeof zone === 'object').toBeTruthy() expect(zone.abbreviation).toEqual('CET') @@ -34,26 +37,26 @@ it('sets the right time zone to the time object', () => { const zonelessTime = { year: 2018, month: 1, day: 2, hours: 10, minutes: 30, seconds: 40, milliseconds: 50 } - const berlinTIme = setTimeZone(zonelessTime, berlin) - checkTime(berlinTIme, true) + const berlinTime = setTimeZone(zonelessTime, berlin) + checkTime(berlinTime, true) }) it('supplies zero seconds and milliseconds, if not present in the input', () => { const zonelessTime = { year: 2018, month: 1, day: 2, hours: 10, minutes: 30 } - const berlinTIme = setTimeZone(zonelessTime, berlin) - checkTime(berlinTIme, false) + const berlinTime = setTimeZone(zonelessTime, berlin) + checkTime(berlinTime, false) }) it('extracts the time from date parts in the UTC representation', () => { const zonelessDate = new Date(Date.UTC(2018, 0, 2, 10, 30)) - const berlinTIme = setTimeZone(zonelessDate, berlin, { useUTC: true }) - checkTime(berlinTIme, false) + const berlinTime = setTimeZone(zonelessDate, berlin, { useUTC: true }) + checkTime(berlinTime, false) }) it('extracts the time from date parts in the local time zone representation', () => { const zonelessDate = new Date(2018, 0, 2, 10, 30) - const berlinTIme = setTimeZone(zonelessDate, berlin, { useUTC: false }) - checkTime(berlinTIme, false) + const berlinTime = setTimeZone(zonelessDate, berlin, { useUTC: false }) + checkTime(berlinTime, false) }) it('requests the source of date parts, if a date object uis supplied', () => {