I've reviewed a few questions on similar topics already, but none of them address calculating a destination timezone, taking its DST (daylight savings time) into account. I'm trying to write a simple widget that displays a live local time of a specific timezone to anyone visiting the page. The timezone of interest is BST (what is BST?), but if possible I'd love to see a generic implementation for any locale. Here's my attempt, using vanilla JavaScript:
function getBST() {
var date = new Date(),
utc = date.getTime() + date.getTimezoneOffset() * 60000,
local = new Date(utc), // this is GMT, not BST
day = local.getDate(),
mon = local.getMonth() + 1,
year = local.getFullYear(),
hour = local.getHours(),
minute = ('0' + local.getMinutes()).slice(-2),
second = ('0' + local.getSeconds()).slice(-2),
suffix = hour < 12 ? 'AM' : 'PM';
hour = (hour - 24) % 12 + 12;
return mon + '/' + day + '/' + year + ', ' + hour + ':' + minute + ':' + second + ' ' + suffix;
}
setInterval(function () {
document.body.textContent = getBST();
}, 1000);
I've reviewed a few questions on similar topics already, but none of them address calculating a destination timezone, taking its DST (daylight savings time) into account. I'm trying to write a simple widget that displays a live local time of a specific timezone to anyone visiting the page. The timezone of interest is BST (what is BST?), but if possible I'd love to see a generic implementation for any locale. Here's my attempt, using vanilla JavaScript:
function getBST() {
var date = new Date(),
utc = date.getTime() + date.getTimezoneOffset() * 60000,
local = new Date(utc), // this is GMT, not BST
day = local.getDate(),
mon = local.getMonth() + 1,
year = local.getFullYear(),
hour = local.getHours(),
minute = ('0' + local.getMinutes()).slice(-2),
second = ('0' + local.getSeconds()).slice(-2),
suffix = hour < 12 ? 'AM' : 'PM';
hour = (hour - 24) % 12 + 12;
return mon + '/' + day + '/' + year + ', ' + hour + ':' + minute + ':' + second + ' ' + suffix;
}
setInterval(function () {
document.body.textContent = getBST();
}, 1000);
Based on @RobG's answer, I've written this code:
function resetDay(date) {
date.setUTCMilliseconds(0);
date.setUTCSeconds(0);
date.setUTCMinutes(0);
date.setUTCHours(1);
return date;
}
function lastDay(date, day) {
while (date.getUTCDay() !== day) {
date.setUTCDate(date.getUTCDate() - 1);
}
return date;
}
function adjustDST(date, begin, end) {
if (date >= begin && date < end) {
date.setUTCHours(date.getUTCHours() + 1);
}
return date;
}
function updateBST() {
var date = new Date();
var begin = resetDay(new Date());
begin.setUTCMonth(3, -1);
begin = lastDay(begin, 0);
var end = resetDay(new Date());
end.setUTCMonth(10, -1);
end = lastDay(end, 0);
date = adjustDST(date, begin, end);
var day = date.getUTCDate(),
mon = date.getUTCMonth() + 1,
year = date.getUTCFullYear(),
hour = date.getUTCHours(),
minute = ('0' + date.getUTCMinutes()).slice(-2),
second = ('0' + date.getUTCSeconds()).slice(-2),
suffix = hour < 12 ? 'AM' : 'PM';
hour = (hour - 24) % 12 + 12;
return mon + '/' + day + '/' + year + ', ' + hour + ':' + minute + ':' + second + ' ' + suffix;
}
setInterval(function () {
document.body.textContent = updateBST();
}, 1000);
I tested the BST by pausing in the debugger and changing the month of the current date, and the output was an hour later so it seems to work properly. Thanks for all your help everyone!
Share Improve this question edited Jan 15, 2016 at 23:35 Patrick Roberts asked Jan 15, 2016 at 20:24 Patrick RobertsPatrick Roberts 51.9k10 gold badges117 silver badges162 bronze badges 13 | Show 8 more comments3 Answers
Reset to default 7It's not possible in pure JS, just using the Date
methods, but there's (of course) a lib for that: http://momentjs.com/timezone/
Example:
moment.tz("Europe/London").format(); // 2016-01-15T09:21:08-07:00
It's not difficult to support one timezone provided you know when it transitions in and out of daylight saving.
A javascript Date consists of a time value in UTC and a timezone offset based on the host system settings. So all you need to do is apply the timezone offset that you want to the UTC time and presto, there's your time in any timezone.
There is no standardised system for abbreviating time zones, though there are some defacto standards (e.g. IATA timezone codes and IANA timezones). I guess by BST you mean British Summer Time, also known as British Daylight Time (BDT) and British Daylight Saving Time (BDST). It might also be Bangladesh Standard Time or Bougainville Standard Time which are also known as "BST".
There are various libraries (such as Moment Timezone) that use the IANA codes and can provide the time for any supported time zone for (more or less) any time.
BST starts at 01:00 UTC on the last Sunday in March and ends at 01:00 UTC on the last Sunday in October each year, so the algorithm is:
- Create a new date based on the system's local settings (e.g.
new Date()
) - Create dates for the start and end of BST based on UTC values (for this year, 2016-03-27T01:00:00Z and 2016-03-30T02:00:00Z)
- See if the current UTC time falls in that range
- If so, add 1 hour to the UTC time of the date created in #1
- Output a formatted string based on the date's UTC values
That's it, the only hard part is finding the appropriate Sunday dates. You don't need to consider the local timezone offset at all, since everything is based on UTC and Date objects are too.
Right now I don't have time to provide code, so have a go and I can get back to you in about 10 hrs.
Edit
So here's the code. The first two function are helpers, one gets the last Sunday in a month, the other formats an ISO 8601 string with offset. Most of the work is in those two functions. Hopefully the comments are sufficient, if more explanation is required, just ask.
I haven't included milliseconds in the string, feel free to add them if you want, add + '.' + ('00' + d.getUTCMilliseconds()).slice(-3)
before the offset part of the formatted string.
Note that the function will need to be modified if the dates for starting or stopping daylight saving are changed, but that is infrequent. Historic dates of course will need a small database of when daylight saving starts and stops for particular years and periods.
/* Return a Date for the last Sunday in a month
** @param {number} year - full year number (e.g. 2015)
** @param {number} month - calendar month number (jan=1)
** @returns {Date} date for last Sunday in given month
*/
function getLastSunday(year, month) {
// Create date for last day in month
var d = new Date(year, month, 0);
// Adjust to previous Sunday
d.setDate(d.getDate() - d.getDay());
return d;
}
/* Format a date string as ISO 8601 with supplied offset
** @param {Date} date - date to format
** @param {number} offset - offset in minutes (+east, -west), will be
** converted to +/-00:00
** @returns {string} formatted date and time
**
** Note that javascript Date offsets are opposite: -east, +west but
** this function doesn't use the Date's offset.
*/
function formatDate(d, offset) {
function z(n){return ('0'+n).slice(-2)}
// Default offset to 0
offset = offset || 0;
// Generate offset string
var offSign = offset < 0? '-' : '+';
offset = Math.abs(offset);
var offString = offSign + ('0'+(offset/60|0)).slice(-2) + ':' + ('0'+(offset%60)).slice(-2);
// Generate date string
return d.getUTCFullYear() + '-' + z(d.getUTCMonth()+1) + '-' + z(d.getUTCDate()) +
'T' + z(d.getUTCHours()) + ':' + z(d.getUTCMinutes()) + ':' + z(d.getUTCSeconds()) +
offString;
}
/* Return Date object for current time in London. Assumes
** daylight saving starts at 01:00 UTC on last Sunday in March
** and ends at 01:00 UTC on the last Sunday in October.
** @param {Date} d - date to test. Default to current
** system date and time
** @param {boolean, optional} obj - if true, return a Date object. Otherwise, return
** an ISO 8601 formatted string
*/
function getLondonTime(d, obj) {
// Use provided date or default to current date and time
d = d || new Date();
// Get start and end dates for daylight saving for supplied date's year
// Set UTC date values and time to 01:00
var dstS = getLastSunday(d.getFullYear(), 3);
var dstE = getLastSunday(d.getFullYear(), 10);
dstS = new Date(Date.UTC(dstS.getFullYear(), dstS.getMonth(), dstS.getDate(),1));
dstE = new Date(Date.UTC(dstE.getFullYear(), dstE.getMonth(), dstE.getDate(),1));
// If date is between dstStart and dstEnd, add 1 hour to UTC time
// and format using +60 offset
if (d > dstS && d < dstE) {
d.setUTCHours(d.getUTCHours() +1);
return formatDate(d, 60);
}
// Otherwise, don't adjust and format with 00 offset
return obj? d : formatDate(d);
}
document.write('Current London time: ' + getLondonTime(new Date()));
London Time / BST:
const now = new Date();
console.log(now.toLocaleString('en-GB', { timeZone: 'Europe/London' }));
Europe/London
identifier. The idea of "convert to BST" would make me think you only wanted UTC+1, even if BST wasn't in effect. – Matt Johnson-Pint Commented Jan 15, 2016 at 22:49