ISO 8601 จัดรูปแบบวันที่ด้วย Timezone Offset ใน JavaScript ได้อย่างไร


105

ประตู:ค้นหาlocal timeและUTC time offsetจากนั้นสร้าง URL ในรูปแบบต่อไป

ตัวอย่าง URL: / Actions / Sleep? duration = 2002-10-10T12: 00: 00−05: 00

รูปแบบเป็นไปตามคำแนะนำของ W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

เอกสารระบุว่า:

ตัวอย่างเช่น 2002-10-10T12: 00: 00−05: 00 (เที่ยงของวันที่ 10 ตุลาคม 2545 เวลาออมแสงกลางและเวลามาตรฐานตะวันออกในสหรัฐอเมริกา) เท่ากับ 2002-10-10T17: 00: 00Z ห้าชั่วโมงช้ากว่า 2002-10-10T12: 00: 00Z

ตามความเข้าใจของฉันฉันต้องหาเวลาท้องถิ่นของฉันด้วย Date () ใหม่จากนั้นใช้ฟังก์ชัน getTimezoneOffset () เพื่อคำนวณความแตกต่างจากนั้นแนบท้ายสตริง

1. รับเวลาท้องถิ่นด้วยรูปแบบ

var local = new Date().format("yyyy-MM-ddThh:mm:ss"); //today (local time)

เอาท์พุท

2013-07-02T09:00:00

2. รับเวลา UTC ชดเชยเป็นรายชั่วโมง

var offset = local.getTimezoneOffset() / 60;

เอาท์พุท

7

3. สร้าง URL (เฉพาะส่วนเวลา)

var duration = local + "-" + offset + ":00";

เอาต์พุต:

2013-07-02T09:00:00-7:00

ผลลัพธ์ข้างต้นหมายถึงเวลาท้องถิ่นของฉันคือ 2013/07/02 9.00 น. และความแตกต่างจาก UTC คือ 7 ชั่วโมง (UTC เร็วกว่าเวลาท้องถิ่น 7 ชั่วโมง)

จนถึงตอนนี้ดูเหมือนจะใช้งานได้ แต่จะเกิดอะไรขึ้นถ้า getTimezoneOffset () ส่งคืนค่าลบเช่น -120?

ฉันสงสัยว่ารูปแบบควรเป็นอย่างไรในกรณีนี้เนื่องจากฉันไม่สามารถหาข้อมูลจากเอกสาร W3C ได้ ขอบคุณล่วงหน้า.

คำตอบ:


185

ด้านล่างควรทำงานได้อย่างถูกต้องและสำหรับเบราว์เซอร์ทั้งหมด (ขอบคุณ @MattJohnson สำหรับเคล็ดลับ)

Date.prototype.toIsoString = function() {
    var tzo = -this.getTimezoneOffset(),
        dif = tzo >= 0 ? '+' : '-',
        pad = function(num) {
            var norm = Math.floor(Math.abs(num));
            return (norm < 10 ? '0' : '') + norm;
        };
    return this.getFullYear() +
        '-' + pad(this.getMonth() + 1) +
        '-' + pad(this.getDate()) +
        'T' + pad(this.getHours()) +
        ':' + pad(this.getMinutes()) +
        ':' + pad(this.getSeconds()) +
        dif + pad(tzo / 60) +
        ':' + pad(tzo % 60);
}

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


2
สัญญาณบ่งบอกถึงการชดเชยเวลาท้องถิ่นจาก GMT
Steven Moseley

1
@ masato-san คุณต้องคว่ำเครื่องหมายดูคำจำกัดความที่developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto

2
ฟังก์ชันแป้นนี้จะส่งคืนสตริงที่เหมาะสมสำหรับแต่ละส่วนของวันที่ยกเว้นมิลลิวินาที ตัวอย่างเช่นสำหรับ 5ms เป็นอินพุตจะส่งกลับ 05 ซึ่งควรจะเป็น 005 นี่คือลิงค์ที่มีเวอร์ชันแก้ไขขั้นต่ำของฟังก์ชันเดียวกันjsbin
Safique Ahmed Faruque

10
โปรดทราบ: มีข้อบกพร่องเล็กน้อยในคำตอบนี้ การชดเชยเขตเวลาจะคำนวณไม่ถูกต้องหากโซนนั้นมีนาทีออฟเซ็ตและเป็นค่าลบ (เช่น -09: 30) เช่นสถานที่บางแห่งในฝรั่งเศสและแคนาดา Mod ส่งคืนจำนวนลบดังนั้นการทำ floor () ก่อน abs () ทำให้ออฟเซ็ต MORE เป็นลบโดยไม่ได้ตั้งใจ เพื่อแก้ไขข้อบกพร่องนี้ดังนั้น abs () และแล้วพื้น () พยายามแก้ไขคำตอบ แต่เห็นได้ชัดว่า "การแก้ไขนี้มีวัตถุประสงค์เพื่อกล่าวถึงผู้เขียนโพสต์และไม่มีเหตุผลว่าเป็นการแก้ไข"
tytk

6
@tytk - สุดยอดมาก! นั่นคือความละเอียดอ่อน ฉันได้เพิ่มการแก้ไขของคุณในคำตอบแล้ว ขอบคุณสำหรับการแสดงความคิดเห็น!
Steven Moseley

68

getTimezoneOffset() ส่งคืนเครื่องหมายตรงข้ามของรูปแบบที่ต้องการโดยข้อมูลจำเพาะที่คุณอ้างถึง

รูปแบบนี้เป็นที่รู้จักกันISO8601หรืออย่างแม่นยำมากขึ้นเป็นRFC3339

ในรูปแบบนี้ UTC จะแสดงZในขณะที่รูปแบบอื่น ๆ ทั้งหมดจะแสดงด้วยค่าชดเชยจาก UTC ความหมายเหมือนกับของ JavaScript แต่ลำดับการลบจะกลับด้านดังนั้นผลลัพธ์จึงมีเครื่องหมายตรงกันข้าม

นอกจากนี้ยังไม่มีเมธอดบนDateอ็อบเจ็กต์ดั้งเดิมที่เรียกformatดังนั้นฟังก์ชันของคุณใน # 1 จะล้มเหลวเว้นแต่คุณจะใช้ไลบรารีเพื่อบรรลุสิ่งนี้ อ้างถึงเอกสารนี้

หากคุณกำลังมองหาห้องสมุดที่สามารถทำงานกับรูปแบบนี้โดยตรงผมขอแนะนำให้พยายาม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
Austin France

1
@AustinFrance - คุณพูดถูก! ฉันแปลกใจที่ฉันทำผิดพลาดครั้งนั้นในขณะที่ฉันแก้ไขคนอื่นในประเด็นนี้บ่อยๆ ขออภัยฉันไม่เห็นความคิดเห็นของคุณเมื่อสองปีก่อน! แก้ไขคำตอบแล้ว
Matt Johnson-Pint

13

ฉันคิดว่ามันคุ้มค่าที่จะพิจารณาว่าคุณสามารถรับข้อมูลที่ร้องขอได้ด้วยการเรียก API เพียงครั้งเดียวไปยังไลบรารีมาตรฐาน ...

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

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

คุณจะต้องทำการสลับข้อความหากคุณต้องการเพิ่มตัวคั่น 'T' ลบ 'GMT-' หรือต่อท้าย ': 00' ต่อท้าย

แต่คุณสามารถเล่นกับตัวเลือกอื่น ๆได้อย่างง่ายดายหากคุณต้องการเช่น ใช้เวลา 12 ชม. หรือเว้นวินาทีเป็นต้น

โปรดทราบว่าฉันใช้สวีเดนเป็นภาษาเพราะเป็นหนึ่งในประเทศที่ใช้รูปแบบ ISO 8601 ฉันคิดว่าประเทศ ISO ส่วนใหญ่ใช้รูปแบบ 'GMT-4' นี้สำหรับเขตเวลาออฟเซ็ตอื่น ๆ แล้วแคนาดาซึ่งใช้ตัวย่อเขตเวลาเช่น "EDT" สำหรับเวลาตะวันออก - กลางวัน

คุณจะได้รับสิ่งเดียวกันจากฟังก์ชัน i18n มาตรฐานที่ใหม่กว่า "Intl.DateTimeFormat ()" แต่คุณต้องบอกให้รวมเวลาผ่านตัวเลือกไม่เช่นนั้นจะให้วันที่


1
อืมสำหรับ sv จริง ๆ แล้วฉันได้รับ "CET" และสำหรับวันที่เวลาฤดูร้อน "CEST" ... แต่ขอบคุณสำหรับอินพุต API มาตรฐานก็เพียงพอสำหรับฉัน (ต้องนำหน้าข้อความบันทึกพร้อมวันที่) ถ้าฉันอยากจะจริงจังกับเวลาและเขตเวลาฉันเดาว่าฉันจะไปห้องสมุดสักครู่ ...
mmey

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 แน่ใจเหรอ? คุณกำลังได้รับการชดเชยเวลาปัจจุบันซึ่งจะใช้ในการจำลองเวลาท้องถิ่นในภายหลัง บางทีเมื่อถึงจุดนั้นค่าชดเชยอาจแตกต่างกัน แต่ก็ไม่สำคัญเพราะตอนนี้คุณสนใจ
Andrew

3

ตรวจสอบสิ่งนี้:

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 ? '-' : '+') + 
            (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'

1

แค่สองคนของฉันส่งมาที่นี่

ฉันประสบปัญหานี้กับเวลาดังนั้นสิ่งที่ฉันทำคือ:

const moment = require('moment-timezone')

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

จากนั้นบันทึกวันที่ลงใน db เพื่อให้สามารถเปรียบเทียบได้จากแบบสอบถามบางรายการ


ติดตั้ง moment-timezone

npm i moment-timezone

1

ไม่จำเป็นต้องใช้ moment.js: นี่คือคำตอบแบบไปกลับแบบเต็มจากประเภทอินพุต "datetime-local" ซึ่งส่งเอาต์พุตสตริง ISOLocal เป็น UTC วินาทีที่ GMT และย้อนกลับ:

<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

0

นี่คือฟังก์ชั่นที่ฉันใช้สำหรับการสิ้นสุดนี้:

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

0

คุณสามารถบรรลุเป้าหมายนี้ได้ด้วยวิธีการขยายง่ายๆสองสามวิธี วิธีการขยายวันที่ต่อไปนี้จะส่งคืนเฉพาะองค์ประกอบของเขตเวลาในรูปแบบ 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สำหรับใครก็ตามที่ไม่มี ห้องสมุดดังกล่าว)


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

-1

คำตอบของฉันคือการเปลี่ยนแปลงเล็กน้อยสำหรับผู้ที่ต้องการวันที่ของวันนี้ในเขตเวลาท้องถิ่นในรูปแบบปปปป - ดด - วว

ให้ฉันชัดเจน:

เป้าหมายของฉัน:รับวันที่ของวันนี้ในเขตเวลาของผู้ใช้แต่จัดรูปแบบเป็น ISO8601 (YYYY-MM-DD)

นี่คือรหัส:

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

ซึ่งได้ผลเนื่องจากภาษาสวีเดนใช้รูปแบบ ISO 8601


คำตอบไม่ถูกต้องตอบ แต่คำถามอื่น ๆ ... คำตอบนี้สามารถพบได้ง่ายใน SO
PsychoX
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.