Làm cách nào để định dạng ISO 8601 một Ngày có Chênh lệch múi giờ trong JavaScript?


100

Mục tiêu: Tìm local timeUTC time offsetsau đó tạo URL ở định dạng sau.

URL mẫu: / Hành động / Ngủ? Thời lượng = 2002-10-10T12: 00: 00−05: 00

Định dạng dựa trên khuyến nghị của W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

Tài liệu cho biết:

Ví dụ: 2002-10-10T12: 00: 00−05: 00 (trưa ngày 10 tháng 10 năm 2002, Giờ tiết kiệm ánh sáng ban ngày miền Trung cũng như Giờ chuẩn miền Đông ở Hoa Kỳ) bằng 2002-10-10T17: 00: 00Z, muộn hơn năm giờ so với 2002-10-10T12: 00: 00Z.

Vì vậy, dựa trên sự hiểu biết của tôi, tôi cần tìm giờ địa phương của mình theo Date () mới, sau đó sử dụng hàm getTimezoneOffset () để tính toán sự khác biệt sau đó gắn nó vào cuối chuỗi.

1. Nhận giờ địa phương với định dạng

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

đầu ra

2013-07-02T09:00:00

2. Nhận bù đắp thời gian UTC theo giờ

var offset = local.getTimezoneOffset() / 60;

đầu ra

7

3. URL cấu trúc (chỉ phần thời gian)

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

đầu ra:

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

Kết quả trên có nghĩa là giờ địa phương của tôi là 9 giờ sáng, 2013/07/02 và chênh lệch so với UTC là 7 giờ (UTC trước giờ địa phương 7 giờ)

Cho đến nay nó có vẻ hoạt động nhưng điều gì sẽ xảy ra nếu getTimezoneOffset () trả về giá trị âm như -120?

Tôi tự hỏi định dạng sẽ trông như thế nào trong trường hợp như vậy vì tôi không thể tìm ra từ tài liệu W3C. Cảm ơn trước.


Chỉ cần lưu ý nhanh rằng câu trả lời được chấp nhận đã lỗi thời hơn 10 năm và JS đã bao gồm toISOString()từ năm 2009 . Xem câu trả lời của tôi dưới đây.
mikemaccana

Câu trả lời:


174

Bên dưới sẽ hoạt động bình thường và cho tất cả các trình duyệt (cảm ơn @MattJohnson về mẹo)

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
Dấu hiệu cho biết chênh lệch của giờ địa phương từ GMT
Steven Moseley

1
@ masato-san Bạn phải đảo ngược dấu hiệu, xem định nghĩa tại developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto

2
Ở đây hàm pad trả về chuỗi thích hợp cho từng phần của ngày ngoại trừ mili giây. Ví dụ, đối với 5ms như một đầu vào sẽ trở lại 05, mà nghĩa vụ phải được 005. Đây là một liên kết với biến đổi tối thiểu phiên bản của cùng một hàm jsbin
Safique Ahmed Faruque

9
Xin lưu ý: có một lỗi nhỏ trong câu trả lời này. Độ lệch múi giờ sẽ không được tính toán chính xác nếu múi giờ có số phút bù giờ và là số âm (ví dụ: -09: 30), chẳng hạn như một số vị trí nhất định ở Pháp và Canada. Mod trả về một số âm, vì vậy việc thực hiện floor () trước abs () đã vô tình làm cho phần bù âm HƠN. Để sửa lỗi này, nên abs () và THEN floor (). Đã cố gắng chỉnh sửa câu trả lời, nhưng rõ ràng "Bản chỉnh sửa này nhằm đề cập đến tác giả của bài đăng và không có ý nghĩa như một bản chỉnh sửa".
tytk

5
@tytk - Bắt tuyệt vời! Đó là, thực sự, tinh tế. Tôi đã thêm sửa chữa của bạn vào câu trả lời. Cảm ơn đã bình luận!
Steven Moseley

67

getTimezoneOffset() trả về dấu đối diện của định dạng được yêu cầu bởi thông số kỹ thuật mà bạn đã tham chiếu.

Định dạng này còn được gọi là ISO8601 , hoặc chính xác hơn là RFC3339 .

Trong định dạng này, UTC được biểu diễn cùng với Ztất cả các định dạng khác được biểu thị bằng một khoảng lệch từ UTC. Ý nghĩa giống như JavaScript, nhưng thứ tự của phép trừ bị đảo ngược, do đó kết quả mang dấu hiệu ngược lại.

Ngoài ra, không có phương thức nào trên Dateđối tượng gốc được gọi format, vì vậy hàm của bạn trong # 1 sẽ bị lỗi trừ khi bạn đang sử dụng thư viện để đạt được điều này. Tham khảo tài liệu này .

Nếu bạn đang tìm kiếm một thư viện có thể hoạt động trực tiếp với định dạng này, tôi khuyên bạn nên thử dùng moment.js . Trên thực tế, đây là định dạng mặc định, vì vậy bạn có thể chỉ cần thực hiện điều này:

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

Đây là một giải pháp đa trình duyệt, đã được thử nghiệm tốt và có nhiều tính năng hữu ích khác.


Sử dụng toISOString () sẽ không hoạt động. Định dạng +01: 00 yêu cầu phần thời gian là giờ địa phương. toISOString () sẽ cung cấp một chuỗi thời gian UTC.
Austin France

1
@AustinFrance - Bạn nói đúng! Tôi ngạc nhiên vì tôi đã mắc sai lầm đó vào thời điểm đó, vì tôi thường xuyên sửa chữa người khác về điểm này. Xin lỗi, tôi không thấy bình luận của bạn hai năm trước! Đã chỉnh sửa câu trả lời.
Matt Johnson-Pint

9

Tôi nghĩ điều đáng xem xét là bạn có thể nhận được thông tin được yêu cầu chỉ với một lệnh gọi API đến thư viện tiêu chuẩn ...

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

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

Bạn sẽ phải thực hiện hoán đổi văn bản nếu bạn muốn thêm dấu phân cách 'T', xóa 'GMT-' hoặc nối ': 00' vào cuối.

Nhưng sau đó bạn có thể dễ dàng chơi với các tùy chọn khác nếu bạn muốn. sử dụng thời gian 12h hoặc bỏ qua giây, v.v.

Lưu ý rằng tôi đang sử dụng Thụy Điển làm ngôn ngữ vì đây là một trong những quốc gia sử dụng định dạng ISO 8601. Tôi nghĩ rằng hầu hết các quốc gia ISO sử dụng định dạng 'GMT-4' này cho độ lệch múi giờ khác sau đó là Canada sử dụng từ viết tắt múi giờ, ví dụ. "EDT" cho giờ ban ngày phía đông.

Bạn có thể nhận được điều tương tự từ hàm i18n tiêu chuẩn mới hơn "Intl.DateTimeFormat ()" nhưng bạn phải yêu cầu nó bao gồm thời gian thông qua các tùy chọn hoặc nó sẽ chỉ cung cấp ngày.


1
Hm, đối với sv, tôi thực sự nhận được "CET" và cho một ngày mùa hè, "CEST" ... Nhưng cảm ơn vì đầu vào, API tiêu chuẩn là đủ cho tôi (cần phải ghi trước thông điệp nhật ký bằng ngày tháng). Nếu tôi muốn nghiêm túc về thời gian và múi giờ, tôi đoán tôi sẽ đến thư viện khoảnh khắc ...
mmey

4

Đây là chức năng của tôi dành cho múi giờ của khách hàng, nó nhẹ và đơn giản

  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, bạn có chắc không? Bạn đang nhận được chênh lệch thời gian hiện tại, sau này được sử dụng để mô phỏng giờ địa phương. Có thể ở thời điểm đó sự bù đắp khác nhau, nhưng điều đó không quan trọng vì bạn chỉ cần quan tâm đến bây giờ.
Andrew

2

Kiểm tra điều này:

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

Không cần moment.js: Đây là câu trả lời đầy đủ cho chuyến đi khứ hồi, từ loại đầu vào là "datetime-local" xuất ra chuỗi ISOLocal thành UTCseconds lúc GMT và ngược lại:

<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

Chỉ cần hai tôi gửi ở đây

Tôi đã phải đối mặt với vấn đề này với lịch ngày, vì vậy những gì tôi đã làm là:

const moment = require('moment-timezone')

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

Sau đó, lưu ngày vào db để có thể so sánh nó từ một số truy vấn.


Để cài đặt moment-timezone

npm i moment-timezone

0

Câu trả lời của tôi là một biến thể nhỏ cho những người chỉ muốn ngày hôm nay theo múi giờ địa phương ở định dạng YYYY-MM-DD.

Hãy để tôi nói rõ:

Mục tiêu của tôi: lấy ngày hôm nay theo múi giờ của người dùng nhưng được định dạng là ISO8601 (YYYY-MM-DD)

Đây là mã:

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

Điều này hoạt động vì ngôn ngữ Thụy Điển sử dụng định dạng ISO 8601.


Câu trả lời không hợp lệ, câu này trả lời, nhưng câu hỏi khác ... Câu trả lời này có thể dễ dàng tìm thấy trên SO.
PsychoX

0

Dưới đây là các chức năng tôi đã sử dụng cho phần cuối này:

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

Bạn có thể đạt được điều này bằng một vài phương pháp mở rộng đơn giản. Phương thức mở rộng ngày sau chỉ trả về thành phần múi giờ ở định dạng ISO, sau đó bạn có thể xác định một thành phần khác cho phần ngày / giờ và kết hợp chúng để có một chuỗi ngày-giờ-chênh lệch hoàn chỉnh.

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

Ví dụ sử dụng:

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

Tôi biết đó là năm 2020 và hầu hết mọi người có lẽ đang sử dụng Moment.js vào lúc này, nhưng một giải pháp sao chép & dán đơn giản đôi khi vẫn hữu ích.

(Lý do tôi tách ngày / giờ và các phương pháp bù đắp là vì tôi đang sử dụng thư viện Datejs cũ đã cung cấp một toStringphương thức linh hoạt với các chỉ định định dạng tùy chỉnh, nhưng không bao gồm chênh lệch múi giờ. Do đó, tôi đã thêm toISOLocaleStringcho bất kỳ ai không có thư viện nói.)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.