Skip to content

Commit

Permalink
feat: Add epoch time as a read-only non-enumerable property to the ti…
Browse files Browse the repository at this point in the history
…me object

Improve performance of getting the UNIX time for a time object created from the UNIX time earlier.
  • Loading branch information
prantlf committed Sep 3, 2018
1 parent cb78760 commit bea4262
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 58 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
22 changes: 18 additions & 4 deletions src/convert/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 || {}
Expand All @@ -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) {
Expand All @@ -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.')
Expand Down
4 changes: 2 additions & 2 deletions src/convert/utc-date.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}

Expand Down
16 changes: 8 additions & 8 deletions test/formatZonedTime.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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')
})

Expand Down
67 changes: 49 additions & 18 deletions test/getUnixTime.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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()
})
32 changes: 18 additions & 14 deletions test/getZonedTime.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
})
21 changes: 12 additions & 9 deletions test/setTimeZone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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')
Expand All @@ -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', () => {
Expand Down

0 comments on commit bea4262

Please sign in to comment.