This presentation bases on moment
and moment-timezone
packages.
Both are monolithic, a little bloated, and definitely not very performant, but have the best timezone handling.
— xkcd
2019-10-11
10:14
2019-10-11 10:14
Europe/Warsaw
1570781640000
Central European Summer Time
Europe/Warsaw
GMT+2
CEST
1 day = 24 hours?
Practice of advancing clocks during warmer months so that darkness falls later each day according to the clock. [...] In other words, there is one 23-hour day in late winter or early spring and one 25-hour day in the autumn.
dateA = moment.tz('2020-03-29', 'Europe/Warsaw')
dateB = dateA.clone().add(1, 'day')
dateB.diff(dateA, 'hours')
// 23
dateA = moment.tz('2020-10-25', 'Europe/Warsaw')
dateB = dateA.clone().add(1, 'day')
dateB.diff(dateA, 'hours')
// 25
Can you skip a day?
[...] At the end of Thursday, 29 December 2011, Samoa continued directly to Saturday, 31 December 2011, skipping the entire calendar day of Friday, 30 December 2011 and effectively re-drawing the International Date Line.
(yes, this acronym doesn't make any sense)
dateA = moment.utc('2020-03-29')
dateB = dateA.clone().add(1, 'day')
dateB.diff(dateA, 'hours')
// 24
dateA = moment.utc('2020-10-25')
dateB = dateA.clone().add(1, 'day')
dateB.diff(dateA, 'hours')
// 24
Lib X uses Date
objects. How to use it without breaking timezones?
Restaurant time
Current local restaurant time
It is possible to "look" into another timezone
Mind the gap DST!
There's no 02:00!
23 hours!
// Calculate non-local date timezone offset.
function getTotalDateOffset(date: Moment) {
const afterLeapOffset = date.clone().add(+3601, 'second').utcOffset();
const afterLeapPointOffset = date.clone().endOf('day').utcOffset();
const afterLeapDiff = afterLeapOffset - afterLeapPointOffset;
const aheadLeapOffset = date.clone().add(-3601, 'second').utcOffset();
const aheadLeapPointOffset = date.clone().startOf('day').utcOffset();
const aheadLeapDiff = aheadLeapOffset - aheadLeapPointOffset;
const dateOffset = date.clone().utcOffset();
const weekOffset = date.clone().startOf('week').utcOffset();
const dateWeekDiff = dateOffset - weekOffset;
return afterLeapDiff + aheadLeapDiff + dateWeekDiff;
}
// Translate local timestamp to local timezoned date.
function toLocalDate(date: number, timezone: string) {
const datePrev = moment.tz(date, timezone);
const dateNext = moment(datePrev.toObject());
return dateNext.add(getTotalDateOffset(dateNext), 'minute').toDate();
}
moment(+new Date('2019-10-11T10:14')).tz('Europe/Warsaw').toDate()
moment.tz(+new Date('2019-10-11T10:14'), 'Europe/Warsaw').toDate()
moment.utc(+new Date('2019-10-11T10:14')).tz('Europe/Warsaw').toDate()
// Fri Oct 11 2019 10:14:00 GMT+0200 (Central European Summer Time)
moment(+new Date('2019-10-11T10:14')).tz('Pacific/Samoa').toDate()
moment.tz(+new Date('2019-10-11T10:14'), 'Pacific/Samoa').toDate()
moment.utc(+new Date('2019-10-11T10:14')).tz('Pacific/Samoa').toDate()
// Fri Oct 11 2019 10:14:00 GMT+0200 (Central European Summer Time)
toLocalDate(+new Date('2019-10-11T10:14'), 'Europe/Warsaw')
// Fri Oct 11 2019 10:14:00 GMT+0200 (Central European Summer Time)
toLocalDate(+new Date('2019-10-11T10:14'), 'Pacific/Samoa')
// Thu Oct 10 2019 21:14:00 GMT+0200 (Central European Summer Time)
My clients are in different timezones. What now!?
Restaurant time
// Client
api.getAvailableBookings({ datetime: moment.utc().format() });
// Server
function getAvailableBookings({ datetime }: { datetime: string }) {
const tz = ...;
const datetimeLocal = moment(datetime, 'YYYY-MM-DDTHH:mm:ssZ').toDate();
const datetimeTz = moment(datetimeLocal).tz(tz);
// Timezoned start of the day: 2020-04-30T14:12 -> 2020-04-30T00:00.
const dateTz = datetimeTz.clone().startOf('day');
const offset = (dateTz.utcOffset() - datetimeTz.utcOffset()) * 1000;
// Timezoned time: 2020-04-30T14:12 -> 14:12 (in milliseconds).
const timeTz = datetimeTz - dateTz - offset;
// Remember to return UTC dates, not local nor timezoned ones!
}
// Client
function formatTime(datetime: string) {
return moment
.utc(datetime, 'YYYY-MM-DDTHH:mm:ssZ')
.tz(timezone)
.format('HH:mm');
}
How about repeating events and timezones?
On 8 February 2018, the European Parliament voted to ask the European Commission to re-evaluate DST in Europe. After a web survey, in which 4.6 million European citizens participated, showed high support for not switching clocks twice annually [...] On 4 March 2019, the European Parliament Transport and Tourism Committee approved the Commission's proposal by 23 votes to 11. The start date will however be postponed until 2021 at the earliest, to ensure a smooth transition [...] Under the draft directive, member states would be able to choose whether to remain on their current summer time [...] or their current winter time [...].