我正在开发一个基于云的应用程序,该应用程序为世界各地的用户广泛处理日期和时间值。
考虑一个使用JavaScript的场景,我的机器在印度(格林尼治标准时间+05:30),并且我必须显示一个在加利福尼亚时区(GMT-08:00)运行的时钟。
在这种情况下,我必须获得一个新的日期对象,
let india_date = new Date()
添加时区偏移值,
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
添加加利福尼亚的时区偏移值,
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
最后是日期对象。
let california_date: Date = new Date(california_ms)
有什么方法可以直接处理这些时区,而不必一次又一次地转换值?
答案 0 :(得分:3)
好吧,您确实确实需要在任何时间更改显示,但都没有您想像的那么糟。
首先,将所有时间存储为UTC。可能使用milliseconds format,例如Date.UTC()
。
第二,使用存储的信息进行所有操作/比较。
第三,如果您的基于云的应用程序具有一个API,那么该API也应该只使用UTC进行通信,尽管您可以提供ISO字符串,而不是MS,或者希望客户更好地处理该字符串。 。
第四,最后,只有在用户界面中,才应使用您描述的方法或使用momentjs
之类的库将最终转换为本地日期/时间字符串。答案 1 :(得分:2)
JavaScript Date objects以UTC存储日期和时间,但是当日期表示为文本值时会自动调用toString()
方法,该文本值在浏览器的本地时区中显示日期和时间。因此,当您要将日期时间转换为本地时间以外的其他时区时,您实际上是在从UTC转换到该时区(而不是从本地时区转换到另一个时区)。
如果您的用例仅限于特定的浏览器并且格式化灵活(由于浏览器显示日期字符串格式的方式可能有所不同),则可以使用toLocaleString()
,但是Edge等浏览器, Android Webview等不完全支持locales
和options
参数。
下面的示例将区域设置和时区设置为以本地格式输出日期,该格式可能因浏览器而异。
let dt = new Date();
let kolkata = dt.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });
let la = dt.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' });
console.log('Kolkata:', kolkata);
// Kolkata: 19/3/2019, 7:36:26 pm
console.log('Los Angeles:', la);
// Los Angeles: 3/19/2019, 7:06:26 AM
您还可以使用Moment.js和Moment Timezone将日期和时间转换为本地时区以外的时区。例如:
let dt = moment();
let kolkata = dt.tz('Asia/Kolkata').format()
let la = dt.tz('America/Los_Angeles').format()
console.log(kolkata);
// 2019-03-19T19:37:11+05:30
console.log(la);
// 2019-03-19T07:07:11-07:00
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data.min.js"></script>
答案 2 :(得分:2)
首先,让我们谈谈您的问题中的代码。
let india_date = new Date()
您已将此变量命名为india_date
,但是如果代码在设置为印度时区的计算机上运行,则Date
对象将仅反映印度。如果它在具有不同时区的计算机上运行,它将反映该时区。请记住,在内部,Date
对象仅跟踪基于UTC的时间戳。本地时区是在调用需要本地时间的函数和属性时应用的,而不是在创建Date
对象时应用。
添加时区偏移值
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
此方法不正确。 getTime()
已返回基于UTC的时间戳。您无需添加本地偏移量。 (而且,缩写是UTC,而不是UTS。)
现在添加加利福尼亚的时区偏移值
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
同样,添加偏移量是不正确的。此外,与印度不同,加利福尼亚州实行夏令时,因此一年中的偏移量为480
(UTC-8),一年中的偏移量为420
(UTC-7) 。任何功能(例如getCaliforniatimezoneOffsetMS
)都需要将时间戳记作为参数传递才能生效。
最后是日期对象
let california_date: Date = new Date(california_ms)
向Date
构造函数传递数字时间戳记时,必须使用UTC。传递此california_ms
时间戳实际上只是在选择其他时间点。您不能仅通过添加或减去偏移量来更改Date
对象的行为以使其使用不同的时区。对于需要本地时间的任何功能,例如.toString()
和其他功能,它仍将使用其运行时所在的本地时区。
在只有一种情况下,这种调整才有意义,这是一种称为“时代转移”的技术。调整时间戳以使基本纪元偏离正常的1970-01-01T00:00:00Z
,从而允许一个人利用Date
对象的UTC函数(例如getUTCHours
等)。问题是:一旦转移,您将永远无法在该Date
对象上使用任何本地时间函数,或将其传递给其他任何希望Date
对象为正常对象的地方。正确完成时移是Moment.js之类的库的强大力量。 Here是正确完成纪元移位的另一个示例。
但是在您的示例中,您正在移动(错误两次),然后使用Date
对象,就好像它是正常的并且没有移动一样。这只会导致错误,在toString
输出中显示的时区很明显,并且在数学上会在本地时区和预期目标时区的任何DST转换附近出现。通常,您不想采用这种方法。
相反,请阅读我对How to initialize a JavaScript Date to a particular time zone.的回答,那里列出了您的选择。谢谢。
答案 3 :(得分:0)
新日期创建一个Date对象,其时间值为UTC。如果可以保证支持 toLocaleString 的 timeZone 选项(例如,具有受控SOE的公司环境),则可以使用它来构造任何时区和任何格式的时间戳,但可能有点乏味。通用网络上可能缺乏支持。在这种情况下,如果需要可靠地工作,则首选库。
例如要获取加利福尼亚的值,可以对 timeZone 选项使用 toLocaleString 和“ America / Los_Angeles”:
var d = new Date();
// Use the default implementation format
console.log(d.toLocaleString(undefined, {timeZone:'America/Los_Angeles'}));
// Customised format
var weekday = d.toLocaleString(undefined, {weekday:'long', timeZone:'America/Los_Angeles'});
var day = d.toLocaleString(undefined, {day:'numeric', timeZone:'America/Los_Angeles'});
var month = d.toLocaleString(undefined, {month:'long', timeZone:'America/Los_Angeles'});
var year = d.toLocaleString(undefined, {year:'numeric', timeZone:'America/Los_Angeles'});
var hour = d.toLocaleString(undefined, {hour:'numeric',hour12: false, timeZone:'America/Los_Angeles'});
var minute = d.toLocaleString(undefined, {minute:'2-digit', timeZone:'America/Los_Angeles'});
var ap = hour > 11? 'pm' : 'am';
hour = ('0' + (hour % 12 || 12)).slice(-2);
console.log(`The time in Los Angeles is ${hour}:${minute} ${ap} on ${weekday}, ${day} ${month}, ${year}`);
获取时区名称要困难一些,没有其他信息就很难获取。