最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Calculating BST time from Date object? - Stack Overflow

programmeradmin1浏览0评论

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
  • 1 I would suggest looking at a library like Moment and Moment Timezone. Dates and time zones in JavaScript are notoriously hard, and writing your own is liable to be error-prone. – Heretic Monkey Commented Jan 15, 2016 at 20:39
  • @MikeMcCaughan—"Dates and time zones in JavaScript are notoriously hard…". No they aren't, it's just that there is a lot of poor information and people don't take the time to understand them. Dates are really very simple objects. – RobG Commented Jan 15, 2016 at 20:48
  • So, if they're not that hard, answer the question :P. In pure JavaScript, mind you, without reference to an external library. – Heretic Monkey Commented Jan 15, 2016 at 20:50
  • @MikeMcCaughan—I don't have time at the moment to write the code, but I've answers. I'll be back in about 10 hrs. – RobG Commented Jan 15, 2016 at 21:19
  • To be pedantic, let me say that "BST" is specifically UTC+1. The United Kingdom is sometimes on BST, and sometimes on GMT. If you're attempting to reconcile the transitions for a single time zone, you would refer to it as UK Time or London Time. IANA uses the 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
 |  Show 8 more comments

3 Answers 3

Reset to default 7

It'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:

  1. Create a new date based on the system's local settings (e.g. new Date())
  2. 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)
  3. See if the current UTC time falls in that range
  4. If so, add 1 hour to the UTC time of the date created in #1
  5. 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' })); 

发布评论

评论列表(0)

  1. 暂无评论