如何在 JavaScript 中使用 ISO 8601 格式化带有时区偏移的日期?

Posted

技术标签:

【中文标题】如何在 JavaScript 中使用 ISO 8601 格式化带有时区偏移的日期?【英文标题】:How to ISO 8601 format a Date with Timezone Offset in JavaScript? 【发布时间】:2013-06-29 05:31:01 【问题描述】:

目标:找到local timeUTC time offset,然后按以下格式构造网址。

示例网址:/Actions/Sleep?duration=2002-10-10T12:00:00−05:00

格式基于W3C recommendation。文档说:

例如,2002-10-10T12:00:00−05:00(2002 年 10 月 10 日中午, 美国中部夏令时和东部标准时间) 等于 2002-10-10T17:00:00Z,比 2002-10-10T12:00:00Z 晚五个小时。

所以根据我的理解,我需要通过new Date() 找到我的当地时间,然后使用getTimezoneOffset() 函数计算差异,然后将其附加到字符串的末尾。

    通过format获取当地时间

    var local = new Date().format("yyyy-MM-ddThh:mm:ss"); // 2013-07-02T09:00:00
    

    按小时获取 UTC 时间偏移

    var offset = local.getTimezoneOffset() / 60; // 7
    

    构造 URL(仅限时间部分)

    var duration = local + "-" + offset + ":00"; // 2013-07-02T09:00:00-7:00
    

以上输出表示我的当地时间是 2013/07/02 上午 9 点,与 UTC 的差异是 7 小时(UTC 比当地时间早 7 小时)

到目前为止,它似乎有效,但如果 getTimezoneOffset() 返回负值,如 -120?

我想知道在这种情况下格式应该是什么样子,因为我无法从 W3C 文档中弄清楚。

【问题讨论】:

【参考方案1】:

这是一个简单的帮助函数,它将为您格式化 JS 日期。

function toIsoString(date) 
  var tzo = -date.getTimezoneOffset(),
      dif = tzo >= 0 ? '+' : '-',
      pad = function(num) 
          return (num < 10 ? '0' : '') + num;
      ;

  return date.getFullYear() +
      '-' + pad(date.getMonth() + 1) +
      '-' + pad(date.getDate()) +
      'T' + pad(date.getHours()) +
      ':' + pad(date.getMinutes()) +
      ':' + pad(date.getSeconds()) +
      dif + pad(Math.floor(Math.abs(tzo) / 60)) +
      ':' + pad(Math.abs(tzo) % 60);


var dt = new Date();
console.log(toIsoString(dt));

【讨论】:

符号表示当地时间与GMT的偏移量 这里 pad 函数为除毫秒之外的每个日期部分返回正确的字符串。例如,输入 5ms 将返回 05,它应该是 005。这是同一函数的最小修改版本的链接jsbin 请注意:这个答案有一个微妙的错误。如果时区有偏移分钟并且为负数(例如 -09:30),则将无法正确计算时区偏移,例如法国和加拿大的某些位置。 Mod 返回一个负数,因此在 abs() 之前执行 floor() 会无意中使偏移量为负数。为了纠正这个错误,所以 abs() 和 THEN floor()。试图编辑答案,但显然“此编辑旨在解决帖子的作者,作为编辑没有意义”。 @tytk - 很棒的收获!这确实是微妙的。我将您的修复添加到答案中。感谢您的评论! 您可以使用 padStart 更加紧凑:`$Math.floor(Math.abs(num))`.padStart(2, '0')【参考方案2】:

getTimezoneOffset() 返回您引用的规范所需格式的相反符号。

这种格式也称为ISO8601,或更准确地说是RFC3339。

在这种格式中,UTC 用 Z 表示,而所有其他格式都用 UTC 的偏移量表示。和 javascript 的意思一样,只是减法的顺序是颠倒的,所以结果带了相反的符号。

此外,在本机 Date 对象上没有名为 format 的方法,因此您在 #1 中的函数将失败,除非您使用库来实现此目的。参考this documentation。

如果您正在寻找可以直接使用这种格式的库,我建议您尝试moment.js。事实上,这是默认格式,所以你可以这样做:

var m = moment();    // get "now" as a moment
var s = m.format();  // the ISO format is the default so no parameters are needed

// sample output:   2013-07-01T17:55:13-07:00

这是一个经过充分测试的跨浏览器解决方案,并具有许多其他有用的功能。

【讨论】:

使用 toISOString() 不起作用。 +01:00 格式要求时间部分是本地时间。 toISOString() 将给出一个 UTC 时间字符串。 @AustinFrance - 你是对的!我很惊讶我当时犯了这个错误,因为我经常在这一点上纠正其他人。抱歉两年前没看到你的评论!答案已编辑。【参考方案3】:

我认为值得考虑的是,您只需对标准库进行一次 API 调用即可获取请求的信息...

new Date().toLocaleString( 'sv',  timeZoneName: 'short'  );

// produces "2019-10-30 15:33:47 GMT−4"

如果要添加“T”分隔符、删除“GMT-”或将“:00”附加到末尾,则必须进行文本交换。

但是,如果您愿意,您可以轻松地使用the other options。使用 12 小时时间或省略秒数等。

请注意,我使用瑞典作为语言环境,因为它是使用 ISO 8601 格式的国家之一。我认为大多数 ISO 国家都使用这种“GMT-4”格式作为时区偏移量,而不是使用时区缩写的加拿大,例如。 “EDT”表示东部夏令时间。

您可以从较新的标准 i18n 函数“Intl.DateTimeFormat()”中获得相同的结果 但您必须通过选项告诉它包含时间,否则它只会给出日期。

【讨论】:

嗯,对于 sv,我实际上得到“CET”,而对于夏令时日期,“CEST”......但是感谢输入,标准 API 对我来说已经足够了(需要前缀 log带有日期的消息)。如果我想认真对待时间和时区,我想我会去时刻库...... 加上'sv'格式的toLocaleString 这是红薯! 你确定sv 代表瑞典吗?当我search for "sv" country code 时,所有排名靠前的结果都是关于萨尔瓦多的。当我search sweden country code two letter 时,谷歌说它是se【参考方案4】:

对于那些只希望以 YYYY-MM-DD 格式在当地时区显示今天日期的人来说,我的回答略有不同。

让我说清楚:

我的目标:用户的时区中获取今天的日期,但格式化为 ISO8601 (YYYY-MM-DD)强>

代码如下:

new Date().toLocaleDateString("sv") // "2020-02-23" // 

这是因为瑞典语言环境使用 ISO 8601 格式。

【讨论】:

无效的答案,这确实回答了,但其他问题......这个答案很容易在 SO 上找到。 我没有完整阅读这个问题,所以我不能说这是否回答了这个问题,但这正是我想要的(而且它非常短)。谢谢!【参考方案5】:

这是我针对客户时区的功能,重量轻且简单

  function getCurrentDateTimemysql()         
      var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
      var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, 19).replace('T', ' ');
      var mySqlDT = localISOTime;
      return mySqlDT;
  

【讨论】:

@tsh,你确定吗?您正在获取当前时间偏移量,该偏移量稍后用于模拟本地时间。也许在那一点上偏移量是不同的,但这并不重要,因为你现在只关心。【参考方案6】:

检查一下:

function dateToLocalISO(date) 
    const off    = date.getTimezoneOffset()
    const absoff = Math.abs(off)
    return (new Date(date.getTime() - off*60*1000).toISOString().substr(0,23) +
            (off > 0 ? '-' : '+') + 
            Math.floor(absoff / 60).toFixed(0).padStart(2,'0') + ':' + 
            (absoff % 60).toString().padStart(2,'0'))


// Test it:
d = new Date()

dateToLocalISO(d)
// ==> '2019-06-21T16:07:22.181-03:00'

// Is similar to:

moment = require('moment')
moment(d).format('YYYY-MM-DDTHH:mm:ss.SSSZ') 
// ==> '2019-06-21T16:07:22.181-03:00'

【讨论】:

【参考方案7】:

这里只有我的两分钱

我在日期时间方面遇到了这个问题,所以我所做的是:

const moment = require('moment-timezone')

const date = moment.tz('America/Bogota').format()

然后将日期保存到数据库中,以便能够从一些查询中进行比较。


安装moment-timezone

npm i moment-timezone

【讨论】:

【参考方案8】:

不需要moment.js:这是一个完整的往返答案,来自“datetime-local”的输入类型,它在格林威治标准时间输出一个ISOLocal字符串到UTC秒数:

<input type="datetime-local" value="2020-02-16T19:30">

isoLocal="2020-02-16T19:30"
utcSeconds=new Date(isoLocal).getTime()/1000

//here you have 1581899400 for utcSeconds

let isoLocal=new Date(utcSeconds*1000-new Date().getTimezoneOffset()*60000).toISOString().substring(0,16)
2020-02-16T19:30

【讨论】:

【参考方案9】:

您可以通过一些简单的扩展方法来实现这一点。以下日期扩展方法仅返回 ISO 格式的时区组件,然后您可以为日期/时间部分定义另一个并将它们组合成一个完整的日期时间偏移字符串。

Date.prototype.getISOTimezoneOffset = function () 
    const offset = this.getTimezoneOffset();
    return (offset < 0 ? "+" : "-") + Math.floor(Math.abs(offset / 60)).leftPad(2) + ":" + (Math.abs(offset % 60)).leftPad(2);


Date.prototype.toISOLocaleString = function () 
    return this.getFullYear() + "-" + (this.getMonth() + 1).leftPad(2) + "-" +
        this.getDate().leftPad(2) + "T" + this.getHours().leftPad(2) + ":" +
        this.getMinutes().leftPad(2) + ":" + this.getSeconds().leftPad(2) + "." +
        this.getMilliseconds().leftPad(3);


Number.prototype.leftPad = function (size) 
    var s = String(this);
    while (s.length < (size || 2)) 
        s = "0" + s;
    
    return s;

示例用法:

var date = new Date();
console.log(date.toISOLocaleString() + date.getISOTimezoneOffset());
// Prints "2020-08-05T16:15:46.525+10:00"

我知道现在是 2020 年,现在可能大多数人都在使用 Moment.js,但有时仍然可以使用简单的复制和粘贴解决方案。

(我拆分日期/时间和偏移方法的原因是因为我使用的是旧的Datejs 库,它已经提供了具有自定义格式说明符的灵活toString 方法,但不包括时区偏移. 因此,我为没有所述库的任何人添加了toISOLocaleString。)

【讨论】:

感谢 Extragrorey 提供这个方便的代码!【参考方案10】:

这是我为此使用的函数:

function localToGMTStingTime(localTime = null) 
    var date = localTime ? new Date(localTime) : new Date();
    return new Date(date.getTime() + (date.getTimezoneOffset() * 60000)).toISOString();
;

function GMTToLocalStingTime(GMTTime = null) 
    var date = GMTTime ? new Date(GMTTime) : new Date();;
    return new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
;

【讨论】:

【参考方案11】:

只使用没有库的 javascript 实际上只有两行:

var dt = new Date();
// Date in UTC and ISO format: "2021-11-30T20:33:32.222Z"
console.log(dt.toISOString());
var dtOffset = new Date(dt.setMinutes(dt.getMinutes() - dt.getTimezoneOffset()));
// Date in EST and ISO format: "2021-11-30T15:33:32.222Z"
console.log(dtOffset.toISOString());

new Date() 将默认为当前语言环境,但如果需要,您也可以直接指定它:

var dt = new Date(new Date().toLocaleString("en-US", timeZone: "America/New_York"));

【讨论】:

【参考方案12】:
function setDate()
    var now = new Date();
    now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
    var timeToSet = now.toISOString().slice(0,16);

    /*
        If you have an element called "eventDate" like the following:

        <input type="datetime-local" name="eventdate" id="eventdate" />

        and you would like to  set the current and minimum time then use the following:
    */

    var elem = document.getElementById("eventDate");
    elem.value = timeToSet;
    elem.min = timeToSet;

【讨论】:

以上是关于如何在 JavaScript 中使用 ISO 8601 格式化带有时区偏移的日期?的主要内容,如果未能解决你的问题,请参考以下文章

使用 rhel-server-7.4-x86_64-boot.iso镜像安装系统

测试CentOS-7-x86_64-Minimal-1708.iso这种光盘安装效果

CentOS-7-x86_64 iso镜像的安装(Linux操作系统)

yum源的挂载

系统安装 Solaris10 For X86 安装

如何安装Android模拟器到VM虚拟机