在用户的区域设置格式和时间偏移下显示日期/时间

214
我希望服务器在HTML中始终以UTC时间提供日期,在客户端使用JavaScript将其转换为用户本地时区。 如果能够输出用户所在地区的日期格式,则更佳。
18个回答

216

似乎以UTC日期开始最保险的方法是创建一个新的Date对象,并使用setUTC…方法将其设置为所需的日期/时间。

然后各种toLocale…String方法将提供本地化输出。

示例:

// This would come from the server.
// Also, this whole block could probably be made into an mktime function.
// All very bare here for quick grasping.
d = new Date();
d.setUTCFullYear(2004);
d.setUTCMonth(1);
d.setUTCDate(29);
d.setUTCHours(2);
d.setUTCMinutes(45);
d.setUTCSeconds(26);

console.log(d);                        // -> Sat Feb 28 2004 23:45:26 GMT-0300 (BRT)
console.log(d.toLocaleString());       // -> Sat Feb 28 23:45:26 2004
console.log(d.toLocaleDateString());   // -> 02/28/2004
console.log(d.toLocaleTimeString());   // -> 23:45:26

一些参考资料:


1
嗯,是一个相当严重的问题。如果您希望日期在所有浏览器中看起来相同,则使用这些方法将不起作用。有任何其他替代方案吗? - Josh Mc
9
以上初始化的单行版本:var d = new Date(Date.UTC(2004,1,29,2,45,26)); - m1kael
5
值得考虑的是,Chrome(截至v35版本)在toLocaleString()函数中不考虑用户的语言环境。 - benno
10
很抱歉,这种方法在澳大利亚无效。当我运行上面的代码时,日期以美国格式显示。此外,MDN表示,使用的语言环境和返回的字符串形式完全取决于实现。 - tgrrr
2
创建新日期,然后逐个设置其部分可能会导致错误。例如,创建一个1月31日的日期,然后将月份设置为2月,将更改日期为3月。应该一次性完成:new Date(Date.UTC(year,month,...)),这样也要少打很多字。 - RobG
显示剩余3条评论

67

您可以使用moment.js(已于2021年停用)实现

最好从UTC parse日期字符串,如下所示(在服务器上创建一个ISO-8601兼容字符串以在所有浏览器中获得一致的结果):

var m = moment("2013-02-08T09:30:26Z");

现在只需在应用程序中使用m,moment.js默认使用本地时区进行显示操作。有许多方法可以格式化日期和时间值或提取其中的部分。
您甚至可以像这样在用户语言环境中格式化moment对象:
m.format('LLL') // Returns "February 8 2013 8:30 AM" on en-us

要将 moment.js 对象转换为不同的时区(即既不是本地时区也不是 UTC 时区),您需要使用 moment.js 时区扩展。该页面还有一些示例,很容易使用。
注意:Moment JS 推荐更现代的替代方案,因此它可能不是新项目的好选择。

你如何把这些结合起来?m.utcOffset(offset).format('LLL') 似乎无法正常工作。 - bart
现在应该推荐使用 Luxon 还是 Day.js 库? - Homer
1
我建议添加 对于旧项目,只需使用jQuery - Xenos
无法在Chrome上使用 - 即使将时间设置为计算机的本地设置中的24小时制,仍然显示am / pm。 - vir us
1
momentjs已经宣布过时:https://momentjs.com/docs/ 最近,Chrome Dev Tools开始显示建议替换Moment仅仅是因为其大小。我们通常支持这一举措。 - Remco
Moment.js建议使用Day.js作为替代方案。 - Sixteen

25
你可以使用new Date().getTimezoneOffset()/60来获取时区信息。此外,还可以使用toLocaleString()方法以用户的语言环境显示日期。
以下是完整列表:处理日期

toLocaleString 在以用户期望的格式呈现日期方面极不可靠。即使支持 ISO 402,它仍然非常不可靠,并且基于语言而不是区域设置来确定格式。 - RobG
@RobG,除了toLocaleString之外,有什么解决方案吗? - user924
1
@user924 - 你可以使用一个库,有很多可供选择。toLocaleString 的问题在于不同的实现会在不同的语言和地区支持级别下给出不同的结果。例如,我的系统默认语言不是 en-US,但某些浏览器对 toLocaleString 的默认格式是美国格式,而其他浏览器则尊重我的系统语言。时区名称也是如此,只是更糟糕,因为它们根本没有标准化。 :-( - RobG
1
小问题:您应该否定结果以获取时区(以小时为单位):-new Date().getTimezoneOffset()/60 - mvds

16

在JS中,没有简单和跨平台的方法来格式化本地日期时间,除非将每个属性转换为上述提到的方式。

这是我用来获取本地YYYY-MM-DD的快速hack。请注意,这是一个hack,因为最终日期将不再具有正确的时区(所以您必须忽略时区)。如果我需要更多其他内容,我会使用moment.js。

var d = new Date();    
d = new Date(d.getTime() - d.getTimezoneOffset() * 60000)
var yyyymmdd = t.toISOString().slice(0, 10); 
// 2017-05-09T08:24:26.581Z (but this is not UTC)

d.getTimezoneOffset()返回的是相对于UTC时间的时区偏移量,单位为分钟,而d.getTime()返回的是毫秒数,因此需要乘以60,000。


1
@VG,我添加了更多关于这60k来自哪里的信息。希望这有所帮助。 - Jeremy Chone

13

2021年,您可以使用浏览器原生的Intl.DateTimeFormat

const utcDate = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
console.log(new Intl.DateTimeFormat().format(utcDate));
// expected output: "21/04/2021", my locale is Switzerland

以下是直接从文档中获取的内容:

const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary

// Specify default date formatting for language (locale)
console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2020"
    
// Specify default date formatting for language with a fallback language (in this case Indonesian)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// expected output: "20/12/2020"
    
// Specify date and time format using "style" options (i.e. full, long, medium, short)
console.log(new Intl.DateTimeFormat('en-GB', { dateStyle: 'full', timeStyle: 'long' }).format(date));
// Expected output "Sunday, 20 December 2020 at 14:23:16 GMT+11"

8

一旦你构建好日期对象,这里有一个转换的片段:

该函数接受一个UTC格式的日期对象和格式字符串。
您需要一个Date.strftime原型。

function UTCToLocalTimeString(d, format) {
    if (timeOffsetInHours == null) {
        timeOffsetInHours = (new Date().getTimezoneOffset()/60) * (-1);
    }
    d.setHours(d.getHours() + timeOffsetInHours);

    return d.strftime(format);
}

6
你是从哪里学会了strftime的? - Anuj Pandey
1
设置小时对日期对象的日/月/年部分有影响吗?似乎没有。 - ChristoKiwi
2
@ChristoKiwi - setHours 函数期望传入一个整数值,而 getTimezoneOffset()/60 可能会返回一个小数(例如印度时区偏移量为 +05:30,将返回 5.5)。小数部分将被截断。否则,设置小时数确实会更新其他值(将小时数设置为 48 并查看发生了什么)。 - RobG

8

// new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]])
var serverDate = new Date(2018, 5, 30, 19, 13, 15); // just any date that comes from server
var serverDateStr = serverDate.toLocaleString("en-US", {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',
  second: 'numeric'
})
var userDate = new Date(serverDateStr + " UTC");
var locale = window.navigator.userLanguage || window.navigator.language;

var clientDateStr = userDate.toLocaleString(locale, {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric'
});

var clientDateTimeStr = userDate.toLocaleString(locale, {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',
  second: 'numeric'
});

console.log("Server UTC date: " + serverDateStr);
console.log("User's local date: " + clientDateStr);
console.log("User's local date&time: " + clientDateTimeStr);


参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString#Syntax (我需要“options”部分) - Xenos

6

以下是我在过去项目中使用过的内容:

var myDate = new Date();
var tzo = (myDate.getTimezoneOffset()/60)*(-1);
//get server date value here, the parseInvariant is from MS Ajax, you would need to do something similar on your own
myDate = new Date.parseInvariant('<%=DataCurrentDate%>', 'yyyyMMdd hh:mm:ss');
myDate.setHours(myDate.getHours() + tzo);
//here you would have to get a handle to your span / div to set.  again, I'm using MS Ajax's $get
var dateSpn = $get('dataDate');
dateSpn.innerHTML = myDate.localeFormat('F');

4
这涉及到特定于ASP.NET的内容。 - ZiggyTheHamster
1
时区偏移量不一定是小时的整数倍,因此 getTimezoneOffset()/60 经常会返回一个小数值。这在 1900 年之前的日期尤其如此,许多地方的偏移量以分钟和秒为单位。此外,JavaScript 日期现在需要支持历史偏移量。例如,在 1890 年,莫斯科的偏移量为 +2:30:17。请参见 浏览器、时区、Chrome 67 错误 - RobG

4
使用PHP代码获取日期,可以使用以下方式:
function getLocalDate(php_date) {
  var dt = new Date(php_date);
  var minutes = dt.getTimezoneOffset();
  dt = new Date(dt.getTime() + minutes*60000);
  return dt;
}

我们可以这样称呼它。
var localdateObj = getLocalDate('2015-09-25T02:57:46');

1
这个问题是关于 JavaScript 的,不是 PHP。 - pipedreambomb
5
答案仅限于JavaScript。问题中提到服务器日期为UTC,因此服务器可以使用任何语言。因此,我只回答了PHP的问题。 - hriziya

4
我将迄今为止的答案混合并添加,因为我不得不阅读它们并进行额外的调查,以便在用户的本地时区格式中显示来自python/django数据库的日期时间字符串。
日期时间字符串来自python/django数据库,格式为:2016-12-05T15:12:24.215Z。
JavaScript中可靠的浏览器语言检测似乎在所有浏览器中都无法正常工作(请参见JavaScript for detecting browser language preference),因此我从服务器获取浏览器语言。
Python/Django:将请求浏览器语言作为上下文参数发送。
language = request.META.get('HTTP_ACCEPT_LANGUAGE')
return render(request, 'cssexy/index.html', { "language": language })

HTML:将其写入隐藏输入框中:

<input type="hidden" id="browserlanguage" value={{ language }}/>

JavaScript: 获取隐藏输入框的值,例如en-GB,en-US;q=0.8,en;q=0.6/,然后通过替换和正则表达式仅获取列表中的第一种语言

const browserlanguage = document.getElementById("browserlanguage").value;
var defaultlang = browserlanguage.replace(/(\w{2}\-\w{2}),.*/, "$1");

JavaScript:转换为日期时间并格式化:
var options = { hour: "2-digit", minute: "2-digit" };
var dt = (new Date(str)).toLocaleDateString(defaultlang, options);

请看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString 结果为(浏览器语言为en-gb):2016年5月12日,14:58

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