如何正确地在不同时区之间转换时间?

15

假设用户在美国加利福尼亚州选择一个日期、时间和时区:

全球啤酒马拉松从2013年8月15日上午10:00,UTC-08:00开始。

另一个用户在中欧打开了显示此日期和时间的页面。他不想进行时间计算(已经喝了几杯啤酒)。他只想看到这个日期和时间:

2013年8月15日下午7:00

假设浏览器接收到了由加利福尼亚用户输入的日期和时间信息:

有没有一种方法,在Javascript中,不使用外部网络服务,进行正确的转换?也就是说,要检测UTC-08:00的上午10点实际上应该是UTC-07:00,因为这是夏令时。

也许我从一开始就误解了这个问题,但我不想让输入的用户去考虑是否应该选择UTC-08:00(太平洋标准时间)还是UTC-07:00(太平洋夏令时)。我认为,由于加利福尼亚的标准时区是太平洋标准时间,人们在夏季不会转而思考太平洋夏令时。或者他们会吗?!

在中欧,标准日期是UTC+01:00,夏令时日期是UTC+02:00。因此,加州和欧洲之间的时间差应该为9个小时,除了一年中两个时期,当一个地区切换到标准或夏令时模式时,这个时间差会发生变化。

更新:

经过更多思考和阅读评论后,我理想情况下需要的是这样的:

var utcOffset = f('2013-08-15T10:00', 'America/Los_Angeles');
// utcOffset == "-07:00"
var utcOffset = f('2013-11-15T10:00', 'America/Los_Angeles');
// utcOffset == "-08:00"

到目前为止,Guido Preite建议使用的moment.js/timezone插件似乎能够(或多或少地)做到这一点。

还有其他方法吗?可以使用浏览器API吗?


1
请查看Moment.jsMoment Timezone,网址为http://momentjs.com/timezone/。 - Guido Preite
我已经使用了moment.js。Timezone扩展看起来很有前途。生成的时区文件包含夏令时转换数据。经过快速测试,它能够正确地调整输入的时间。因此,这似乎表明浏览器无法告诉“嘿,在这个日期,你必须使用这个UTC偏移量,因为在那个日期,有夏令时”? - rdamborsky
3个回答

24
“在JavaScript中,是否有一种不需要使用外部Web服务的方法来进行正确的转换?也就是说,要检测10am UTC-08:00实际上应该是10am UTC-07:00,因为它是夏令时。”
“10:00-8和10:00-7是两个不同的时间点。它们分别相当于18:00Z和17:00Z(Z = UTC)。当您以偏移量为单位进行测量时,夏令时不会参与进来。”
“我假设由于加利福尼亚的标准时区是PST,所以人们在夏季不会切换到思考PDT。或者他们会吗?!”
“一般来说,人们只会考虑“太平洋时间”,这意味着冬季是PST,夏季是PDT。但计算机更精确。当您看到PST时,它表示UTC-8。当您看到PDT时,它表示UTC-7。如果同时使用一种形式来标记并参考另一种形式的偏移量,则无效。”

时区缩写可能会产生歧义。理想情况下,在编程上引用时区时,您应该使用IANA时区名称,例如America/Los_Angeles。然而,在当前某些JavaScript运行环境中,这并不是所有的都可以实现,需要使用库( 尽管他们正在努力解决此问题 )。

在中欧,标准日期为UTC +01:00,夏令时日期为UTC+02:00。因此,加州和欧洲之间的差异应为9个小时,除了一年中的两个时期,当一个地区切换到标准或夏令时模式时,另一个地区也会相应切换。

正确。它们可能相差8、9或10个小时。不过它们切换的时间完全不同,因此不要试图自己管理。

目前看来,Guido Preite建议使用moment.js/timezone插件来完成此操作(或多或少)。

Moment-timezone 是一个很好的库。然而,根据你所描述的情况,我认为你不需要像你想象的那样担心时区转换问题。看看你是否能够遵循这个逻辑:

  1. The user in California enters a date and time into a textbox.
  2. You read that textbox value into a string, and parse it into a date:

    var dt = new Date("8/15/2013 10:00");
    

    or using moment.js:

    var m = moment("8/15/2013 10:00", "M/D/YYYY HH:mm");
    
  3. Because this is being done on the user's computer, JavaScript will automatically assume that this is a local date and time. You do not need to provide any offset or time zone information.

  4. This does mean that because of the DST transitions that the time entered might be invalid or ambiguous. JavaScript doesn't do such a great job at handling that, in fact - you will get different results on different browsers. If you want to be unambiguous, then you would provide an offset.

    // PST
    var dt = new Date("3/11/2013 1:00 UTC-08:00");
    
    // PDT
    var dt = new Date("3/11/2013 1:00 UTC-07:00");
    
  5. Once you have a Date (or a moment), then you can evaluate its UTC equivalent:

    var s = dt.toISOString();  //  2013-08-15T17:00:00Z
    

    it's the same with moment.js, but you will have better browser support:

    var s = m.toISOString();  //  2013-08-15T17:00:00Z
    
  6. You store that UTC value in your database.

  7. The other user in Central Europe comes along and loads the data.

  8. You feed it in to a Date or moment in JavaScript:

    var dt = new Date("2013-08-15T17:00:00Z");
    

    or with moment.js (again, better browser support)

    var m = moment("2013-08-15T17:00:00Z")
    
  9. Because JavaScript knows the time zone rules of the local computer, you can now display this date and it will be presented with the Central Europe time zone:

    var s = dt.ToString();  //  browser specific output
    // ex: "Thu Aug 15 2013 19:00:00 GMT+0200 (Central Europe Daylight Time)"
    

    or with moment.js, you can control the output format better

    var s = m.format("DD/MM/YYYY HH:mm"); // "15/08/2013 19:00"
    

    you could also let moment.js decide what localized format should be output:

    var s = m.format("llll"); // "Thu, 15 Aug 2013 19:00"
    

总之,如果您只对将本地时间转换为其他时区以及从其他时区转换为本地时间感兴趣,则可以仅使用Date完成所有操作。 Moment.js可以使解析和格式化变得更加容易,但并非绝对必要。

只有少数情况需要使用时区库(例如moment-timezone或其他库)。

  • 您想要转换到或从不是本地时区或UTC的时区。

  • 您正在处理过去的日期,而自那时起时间区规则或夏令时规则已发生变化,并且您拥有的日期在新规则下将与旧规则下有所不同。这有点技术性,但确实会发生。 请在此处在此处阅读更多信息。


感谢Matt提供详尽的信息、链接和指南。我的情况正是你所指出的第一种情况:转换不是来自本地时区。也就是说,输入日期和时间的用户还要输入与该时间相关的时区。这并不一定是用户所在的时区。我看到我应该在问题中更清楚地说明这一点。 - rdamborsky
1
我要说的是,JavaScript作为“万维网”的编程语言,竟然没有内置的时间转换/时区工具,这真是荒谬。 - Brad Kent

1

我基于其他示例开发了这个解决方案...希望对你有用!可在jsfiddle上获取。

/* 
* Author: Mohammad M. AlBanna
* Website: MBanna.me
* Description: Get the current time in different time zone 
*/

//Check daylight saving time prototype
Date.prototype.stdTimezoneOffset = function() {
    var jan = new Date(this.getFullYear(), 0, 1);
    var jul = new Date(this.getFullYear(), 6, 1);
    return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}

Date.prototype.dst = function() {
    return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

var today = new Date();
var isDST = today.dst() ? true : false;
var pstOffset = isDST ? 7 : 8;
var cstOffset = isDST ? 5 : 6;
var estOffset = isDST ? 4 : 5;
var gmtOffset = 1;

pstOffset = pstOffset * 60 * 60 * 1000;
cstOffset = cstOffset * 60 * 60 * 1000;
estOffset = estOffset * 60 * 60 * 1000;
gmtOffset = gmtOffset * 60 * 60 * 1000;

var todayMillis = today.getTime();
var timeZoneOffset = (today.getTimezoneOffset() * 60 * 1000);

var curretPST = todayMillis - pstOffset; 
var curretCST = todayMillis - cstOffset; 
var curretEST = todayMillis - estOffset;
var curretGMT = todayMillis - gmtOffset;

addP("PST Time : " + new Date(curretPST).toUTCString());
addP("CST Time : " + new Date(curretCST).toUTCString());
addP("EST Time : " + new Date(curretEST).toUTCString());
addP("GMT Time : " + new Date(curretGMT).toUTCString());
addP("Local Time : " + new Date(today.getTime() - timeZoneOffset ).toUTCString());

function addP(value){
    var p = document.createElement("p");
    p.innerHTML = value;
    document.body.appendChild(p);
}

1

默认构造函数创建本地时间实例。

var localDate = new Date(); 

我现在无法测试,但您应该能够将日期时间作为参数提供给构造函数。

var eventDate = [SOMEDATE];
var localDate = new Date(eventDate);

然后您应该能够调用Date对象函数,例如getMonth,在本地时区返回数据。如在此处所述:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

注意1:没有服务器=完全没有服务器|数据库?如果有,日期应以UTC格式保存在数据库中,并为每个用户加载为本地时间。这样您就不必担心转换。

注意2:该问题显示了一些代码,说明如何获取时区差异:如何获取客户端的确切本地时间?


是的,一般情况下这样做是可以的。但问题在于用户输入了用于保存日期的显式时区。想象一下,当您将07/20/2013 10:00 UTC-08:00提交时,您认为它是加利福尼亚州的时间。但这个信息是不正确的,因为在那个特定的日期,应该使用-07:00作为UTC偏移量。这就是我需要检测和解决的问题。存储/检索部分很容易处理一旦日期正确保存了。我想在输入阶段避免标准/夏令时之间的决策。 - rdamborsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接