简体   繁体   English

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

[英]How to ISO 8601 format a Date with Timezone Offset in JavaScript?

Goal: Find the local time and UTC time offset then construct the URL in following format.目标:找到local timeUTC time offset ,然后按以下格式构造 URL。

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

The format is based on the W3C recommendation .该格式基于W3C 推荐 The documentation says:文件说:

For example, 2002-10-10T12:00:00−05:00 (noon on 10 October 2002, Central Daylight Savings Time as well as Eastern Standard Time in the US) is equal to 2002-10-10T17:00:00Z, five hours later than 2002-10-10T12:00:00Z.例如,2002-10-10T12:00:00−05:00(2002 年 10 月 10 日中午,美国中部夏令时和东部标准时间)等于 2002-10-10T17:00:00Z,比 2002-10-10T12:00:00Z 晚了五个小时。

So based on my understanding, I need to find my local time by new Date() then use getTimezoneOffset() function to compute the difference then attach it to the end of string.因此,根据我的理解,我需要通过new Date()找到我的当地时间,然后使用getTimezoneOffset() function 来计算差异,然后将其附加到字符串的末尾。

  1. Get local time with format使用format获取本地时间

    var local = new Date().format("yyyy-MM-ddThh:mm:ss"); // 2013-07-02T09:00:00
  2. Get UTC time offset by hour按小时获取 UTC 时间偏移量

    var offset = local.getTimezoneOffset() / 60; // 7
  3. Construct URL (time part only)构建 URL(仅限时间部分)

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

The above output means my local time is 2013/07/02 9am and difference from UTC is 7 hours (UTC is 7 hours ahead of local time)上面的 output 表示我的本地时间是 2013/07/02 早上 9 点,与 UTC 相差 7 小时(UTC 比本地时间提前 7 小时)

So far it seems to work but what if getTimezoneOffset() returns negative value like -120?到目前为止它似乎工作但是如果getTimezoneOffset()返回像-120 这样的负值怎么办?

I'm wondering how the format should look like in such case because I cannot figure out from W3C documentation.我想知道在这种情况下格式应该是什么样子,因为我无法从 W3C 文档中弄清楚。

Here's a simple helper function that will format JS dates for you.这是一个简单的帮助函数,它将为您格式化 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));

getTimezoneOffset() returns the opposite sign of the format required by the spec that you referenced. getTimezoneOffset()返回您引用的规范所需格式的相反符号。

This format is also known as ISO8601 , or more precisely as RFC3339 .这种格式也称为ISO8601 ,或更准确地说是RFC3339

In this format, UTC is represented with a Z while all other formats are represented by an offset from UTC.在这种格式中,UTC 用Z表示,而所有其他格式都用 UTC 的偏移量表示。 The meaning is the same as JavaScript's, but the order of subtraction is inverted, so the result carries the opposite sign.含义与 JavaScript 相同,但减法的顺序是相反的,因此结果带有相反的符号。

Also, there is no method on the native Date object called format , so your function in #1 will fail unless you are using a library to achieve this.此外,本机Date对象上没有名为format的方法,因此您在 #1 中的函数将失败,除非您使用库来实现此目的。 Refer to this documentation .请参阅本文档

If you are seeking a library that can work with this format directly, I recommend trying moment.js .如果您正在寻找可以直接使用这种格式的库,我建议您尝试moment.js In fact, this is the default format, so you can simply do this:事实上,这是默认格式,所以你可以简单地这样做:

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

This is a well-tested, cross-browser solution, and has many other useful features.这是一个经过充分测试的跨浏览器解决方案,并具有许多其他有用的功能。

I think it is worth considering that you can get the requested info with just a single API call to the standard library...我认为值得考虑的是,您只需对标准库进行一次 API 调用即可获取请求的信息......

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

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

You would have to do text swapping if you want to add the 'T' delimiter, remove the 'GMT-', or append the ':00' to the end.如果要添加“T”分隔符、删除“GMT-”或将“:00”附加到末尾,则必须进行文本交换。

But then you can easily play with the other options if you want to eg.但是,如果您愿意,您可以轻松地使用其他选项 use 12h time or omit the seconds etc.使用 12 小时时间或省略秒等。

Note that I'm using Sweden as locale because it is one of the countries that uses ISO 8601 format.请注意,我使用瑞典作为语言环境,因为它是使用 ISO 8601 格式的国家之一。 I think most of the ISO countries use this 'GMT-4' format for the timezone offset other then Canada which uses the time zone abbreviation eg.我认为大多数 ISO 国家都使用这种“GMT-4”格式作为时区偏移量,而不是使用时区缩写的加拿大,例如。 "EDT" for eastern-daylight-time. “EDT”表示东部夏令时间。

You can get the same thing from the newer standard i18n function "Intl.DateTimeFormat()" but you have to tell it to include the time via the options or it will just give date.您可以从较新的标准 i18n 函数“Intl.DateTimeFormat()”中获得相同的结果,但您必须通过选项告诉它包含时间,否则它只会给出日期。

My answer is a slight variation for those who just want today's date in the local timezone in the YYYY-MM-DD format.对于那些只想在当地时区以 YYYY-MM-DD 格式显示今天日期的人,我的回答是略有不同。

Let me be clear:让我说清楚:

My Goal: get today's date in the user's timezone but formatted as ISO8601 (YYYY-MM-DD)我的目标:用户的时区中获取今天的日期,但格式为 ISO8601 (YYYY-MM-DD)

Here is the code:这是代码:

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

This works because the Sweden locale uses the ISO 8601 format.这是因为瑞典语言环境使用 ISO 8601 格式。

This is my function for the clients timezone, it's lite weight and simple这是我的客户时区功能,重量轻且简单

  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;
  }

Check this:检查这个:

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'

Just my two cents here只有我的两分钱在这里

I was facing this issue with datetimes so what I did is this:我在日期时间方面遇到了这个问题,所以我所做的是:

const moment = require('moment-timezone')

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

Then save date to db to be able to compare it from some query.然后将日期保存到 db 以便能够从某些查询中进行比较。


To install moment-timezone安装moment-timezone

npm i moment-timezone

You can achieve this with a few simple extension methods.您可以通过一些简单的扩展方法来实现这一点。 The following Date extension method returns just the timezone component in ISO format, then you can define another for the date/time part and combine them for a complete date-time-offset string.以下日期扩展方法仅返回 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;
}

Example usage:示例用法:

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

I know it's 2020 and most people are probably using Moment.js by now, but a simple copy & pastable solution is still sometimes handy to have.我知道现在是 2020 年,现在大多数人可能都在使用 Moment.js,但有时仍然可以使用简单的复制和粘贴解决方案。

(The reason I split the date/time and offset methods is because I'm using an old Datejs library which already provides a flexible toString method with custom format specifiers, but just doesn't include the timezone offset. Hence, I added toISOLocaleString for anyone without said library.) (我拆分日期/时间和偏移方法的原因是因为我使用的是旧的Datejs库,它已经提供了一个灵活的toString方法和自定义格式说明符,但不包括时区偏移量。因此,我添加了toISOLocaleString为没有所述库的任何人。)

No moment.js needed: Here's a full round trip answer, from an input type of "datetime-local" which outputs an ISOLocal string to UTCseconds at GMT and back:不需要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
  • date to ISO string,日期到 ISO 字符串,
  • with local(computer) time zone,与本地(计算机)时区,
  • with or without milliseconds有或没有毫秒

ISO ref: https://en.wikipedia.org/wiki/ISO_8601 ISO 参考: https ://en.wikipedia.org/wiki/ISO_8601

how to use: toIsoLocalTime(new Date())使用方法: toIsoLocalTime(new Date())

 function toIsoLocalTime(value) { if (value instanceof Date === false) value = new Date(); const off = value.getTimezoneOffset() * -1; const del = value.getMilliseconds() ? 'Z' : '.'; // have milliseconds ? value = new Date(value.getTime() + off * 60000); // add or subtract time zone return value .toISOString() .split(del)[0] + (off < 0 ? '-' : '+') + ('0' + Math.abs(Math.floor(off / 60))).substr(-2) + ':' + ('0' + Math.abs(off % 60)).substr(-2); } function test(value) { const event = new Date(value); console.info(value + ' -> ' + toIsoLocalTime(event) + ', test = ' + (event.getTime() === (new Date(toIsoLocalTime(event))).getTime() )); } test('2017-06-14T10:00:00+03:00'); // test with timezone test('2017-06-14T10:00:00'); // test with local timezone test('2017-06-14T10:00:00Z'); // test with UTC format test('2099-12-31T23:59:59.999Z'); // date with milliseconds test((new Date()).toString()); // now

Use Temporal .使用时间

Temporal.Now.zonedDateTimeISO().toString()
// '2022-08-09T14:16:47.762797591-07:00[America/Los_Angeles]'

To omit the fractional seconds and IANA time zone:要省略小数秒和 IANA 时区:

Temporal.Now.zonedDateTimeISO().toString({
  timeZoneName: "never",
  fractionalSecondDigits: 0
})
// '2022-08-09T14:18:34-07:00'

Note: Temporal is currently (2022) available as a polyfill , but will soon be available in major browsers.注意:Temporal 目前 (2022) 可作为polyfill使用,但很快将在主要浏览器中可用。

Here are the functions I used for this end:以下是我为此目的使用的功能:

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();
};
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;
}

consider using moment (like Matt's answer ).考虑使用时刻(如马特的回答)。

From version 2.20.0 , you may call .toISOString(true) to prevent UTC conversion:2.20.0版本开始,您可以调用 .toISOString(true) 来阻止 UTC 转换:

console.log(moment().toISOString(true));

// sample output:   2022-04-06T16:26:36.758+03:00
let myDate = new Date(dateToBeFormatted * 1000); // depends if you have milliseconds, or seconds, then the * 1000 might be not, or required.
timeOffset = myDate.getTimezoneOffset();
myDate = new Date(myDate.getTime() - (timeOffset * 60 * 1000));

console.log(myDate.toISOString().split('T')[0]);

Inspired by https://stackoverflow.com/a/29774197/11127383 , including timezone offset comment .灵感来自https://stackoverflow.com/a/29774197/11127383 ,包括时区偏移注释

luxon

DateTime.now().toISODate() // 2022-05-23

a simple way to get:一个简单的获取方法:

//using a sample date
let iso_str = '2022-06-11T01:51:59.618Z';
let d = new Date(iso_str);

let tz = 'America/Santiago'
let options = {
    timeZone:tz ,
    timeZoneName:'longOffset',
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
    fractionalSecondDigits: 3
}


str_locale = d.toLocaleString("sv-SE",options);
iso_str_tz = str_locale.replace(/(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2}),(\d+)\s+/,'$1-$2-$3T$4:$5:$6.$7').replace('GMT−', '-' ).replace('GMT+','+')


console.log('iso_str               : ',iso_str);
console.log('str_locale            : ',str_locale);
console.log('iso_str_tz            : ',iso_str_tz);
console.log('iso_str_tz --> date   : ',new Date(iso_str_tz));
console.log('iso_str_tz --> iso_str: ',new Date(iso_str_tz).toISOString());

Using moment.js , you can use keepOffset parameter of toISOString :使用moment.js ,您可以使用toISOStringkeepOffset参数:

toISOString(keepOffset?: boolean): string;

moment().toISOString(true)

Alternative approach with dayjs dayjs的替代方法

import dayjs from "dayjs"

const formattedDateTime = dayjs(new Date()).format()

console.log(formattedDateTime) // Prints 2022-11-09T07:49:29+03:00

Using just javascript with no libraries is really just two lines:只使用没有库的 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() will default to the current locale but you can also specify it directly if needed: new Date()将默认为当前语言环境,但如果需要,您也可以直接指定它:

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

I found another more easy solution:我找到了另一个更简单的解决方案:

let now = new Date();
// correct time zone offset for generating iso string
now.setMinutes(now.getMinutes() - now.getTimezoneOffset())
now = now.toISOString();

I undo the timezone offset by substracting it from the current date object. The UTC time from the date object is now pointing to the local time.我通过从当前日期 object 中减去它来撤消时区偏移量。从日期 object 开始的 UTC 时间现在指向本地时间。 That gives you the possibility to get the iso date for the local time.这使您有可能获得当地时间的 iso 日期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM