Bagaimana ISO 8601 memformat Tanggal dengan Timezone Offset di JavaScript?

100

Sasaran: Temukan local timedan UTC time offsetkemudian buat URL dalam format berikut.

Contoh URL: / Actions / Sleep? Durasi = 2002-10-10T12: 00: 00−05: 00

Formatnya didasarkan pada rekomendasi W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

Dokumentasinya mengatakan:

Misalnya, 2002-10-10T12: 00: 00−05: 00 (siang pada 10 Oktober 2002, Waktu Musim Panas Tengah serta Waktu Standar Timur di AS) sama dengan 2002-10-10T17: 00: 00Z, lima jam lebih lambat dari 2002-10-10T12: 00: 00Z.

Jadi berdasarkan pemahaman saya, saya perlu menemukan waktu lokal saya dengan Date () baru kemudian menggunakan fungsi getTimezoneOffset () untuk menghitung perbedaannya lalu melampirkannya ke akhir string.

1. Dapatkan waktu lokal dengan format

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

keluaran

2013-07-02T09:00:00

2. Atur waktu UTC per jam

var offset = local.getTimezoneOffset() / 60;

keluaran

7

3. Bangun URL (hanya bagian waktu)

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

keluaran:

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

Output di atas berarti waktu lokal saya adalah 2013/07/02 9 pagi dan perbedaan dari UTC adalah 7 jam (UTC 7 jam lebih awal dari waktu lokal)

Sejauh ini tampaknya berhasil tetapi bagaimana jika getTimezoneOffset () mengembalikan nilai negatif seperti -120?

Saya bertanya-tanya bagaimana format akan terlihat dalam kasus seperti itu karena saya tidak dapat mengetahui dari dokumen W3C. Terima kasih sebelumnya.

meong
sumber

Jawaban:

174

Di bawah ini harus berfungsi dengan baik, dan untuk semua browser (terima kasih kepada @MattJohnson untuk tipnya)

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

Steven Moseley
sumber
2
Tanda tersebut menunjukkan perbedaan waktu lokal dari GMT
Steven Moseley
1
@ masato-san Anda harus membalikkan tandanya, lihat definisi di developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto
2
Di sini fungsi pad mengembalikan string yang tepat untuk setiap bagian tanggal kecuali milidetik. Misalnya, untuk 5ms sebagai masukan akan menghasilkan 05, yang seharusnya 005. Berikut adalah tautan dengan versi modifikasi minimum dari fungsi yang sama jsbin
Safique Ahmed Faruque
9
Harap diperhatikan: ada bug halus dalam jawaban ini. Pengimbangan zona waktu tidak akan dihitung dengan benar jika zona memiliki menit offset dan negatif (mis. -09: 30), seperti lokasi tertentu di Prancis dan Kanada. Mod mengembalikan angka negatif, jadi melakukan floor () sebelum abs () secara tidak sengaja membuat offset LEBIH negatif. Untuk memperbaiki bug ini, jadi abs () dan THEN floor (). Mencoba mengedit jawaban, tetapi tampaknya "Hasil edit ini ditujukan untuk penulis postingan dan tidak masuk akal sebagai edit".
tytk
5
@tytk - Tangkapan yang bagus! Itu memang halus. Saya menambahkan perbaikan Anda ke jawabannya. Terima kasih telah berkomentar!
Steven Moseley
67

getTimezoneOffset() mengembalikan tanda berlawanan dari format yang dibutuhkan oleh spesifikasi yang Anda referensikan.

Format ini juga dikenal sebagai ISO8601 , atau lebih tepatnya RFC3339 .

Dalam format ini, UTC diwakili dengan Zsementara semua format lainnya diwakili oleh offset dari UTC. Artinya sama dengan JavaScript, tetapi urutan pengurangannya dibalik, sehingga hasilnya membawa tanda yang berlawanan.

Selain itu, tidak ada metode pada Dateobjek asli yang dipanggil format, jadi fungsi Anda di # 1 akan gagal kecuali Anda menggunakan pustaka untuk melakukannya. Lihat dokumentasi ini .

Jika Anda mencari perpustakaan yang dapat bekerja dengan format ini secara langsung, saya sarankan untuk mencoba moment.js . Faktanya, ini adalah format default, jadi Anda cukup melakukan ini:

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

Ini adalah solusi lintas-browser yang teruji dengan baik, dan memiliki banyak fitur berguna lainnya.

Matt Johnson-Pint
sumber
Menggunakan toISOString () tidak akan berhasil. Format +01: 00 mengharuskan paruh waktu menjadi waktu lokal. toISOString () akan memberikan string waktu UTC.
Austin Prancis
1
@AustinFrance - Anda benar! Saya terkejut saya melakukan kesalahan itu pada saat itu, karena saya sering mengoreksi orang lain tentang hal ini. Maaf, saya tidak melihat komentar Anda dua tahun lalu! Jawaban diedit.
Matt Johnson-Pint
9

Saya pikir perlu dipertimbangkan bahwa Anda bisa mendapatkan info yang diminta hanya dengan satu panggilan API ke perpustakaan standar ...

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

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

Anda harus melakukan pertukaran teks jika Anda ingin menambahkan pembatas 'T', menghapus 'GMT-', atau menambahkan ': 00' di bagian akhir.

Tetapi kemudian Anda dapat dengan mudah bermain dengan opsi lain jika Anda mau, misalnya. gunakan waktu 12 jam atau hilangkan detik, dll.

Perhatikan bahwa saya menggunakan Swedia sebagai lokal karena merupakan salah satu negara yang menggunakan format ISO 8601. Saya rasa sebagian besar negara ISO menggunakan format 'GMT-4' ini untuk offset zona waktu selain Kanada yang menggunakan singkatan zona waktu misalnya. "EDT" untuk waktu siang hari bagian timur.

Anda bisa mendapatkan hal yang sama dari fungsi i18n standar yang lebih baru "Intl.DateTimeFormat ()" tetapi Anda harus memberitahukannya untuk menyertakan waktu melalui opsi atau itu hanya akan memberikan tanggal.

Tom
sumber
1
Hm, untuk sv, saya benar-benar mendapatkan "CET" dan untuk tanggal waktu musim panas, "CEST" ... Tapi terima kasih atas masukannya, API standar sudah cukup untuk saya (perlu mengawali pesan log dengan tanggal). Jika saya ingin serius tentang waktu dan zona waktu, saya kira saya akan pergi ke perpustakaan saat ini ...
mmey
4

Ini adalah fungsi saya untuk zona waktu klien, ringan dan sederhana

  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;
  }
Bbb
sumber
@tsh, apakah kamu yakin? Anda mendapatkan offset waktu saat ini, yang nantinya digunakan untuk mensimulasikan waktu lokal. Mungkin pada saat itu offsetnya berbeda, tetapi tidak masalah karena Anda hanya peduli sekarang.
Andrew
2

Periksa ini:

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'
Nahuel Greco
sumber
1

Tidak diperlukan moment.js: Berikut adalah jawaban pulang-pergi lengkap, dari jenis input "datetime-local" yang mengeluarkan string ISOLocal ke UTCseconds pada GMT dan sebaliknya:

<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
Kapten Fantastis
sumber
0

Hanya dua saya yang mengirim ke sini

Saya menghadapi masalah ini dengan waktu jadi yang saya lakukan adalah ini:

const moment = require('moment-timezone')

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

Kemudian simpan tanggal ke db agar dapat membandingkannya dari beberapa kueri.


Untuk memasang moment-timezone

npm i moment-timezone
Arnold Gandarillas
sumber
0

Jawaban saya adalah sedikit variasi bagi mereka yang hanya menginginkan tanggal hari ini di zona waktu lokal dalam format YYYY-MM-DD.

Biar saya perjelas:

Tujuan Saya: dapatkan tanggal hari ini dalam zona waktu pengguna tetapi dengan format ISO8601 (YYYY-MM-DD)

Ini kodenya:

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

Ini berfungsi karena lokal Swedia menggunakan format ISO 8601.

Jonathan Steele
sumber
Jawaban tidak valid, ini memang menjawab, tetapi pertanyaan lain ... Jawaban ini dapat dengan mudah ditemukan di SO.
PsychoX
0

Berikut adalah fungsi yang saya gunakan untuk tujuan ini:

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

Anda dapat melakukannya dengan beberapa metode ekstensi sederhana. Metode ekstensi Tanggal berikut hanya mengembalikan komponen zona waktu dalam format ISO, lalu Anda dapat menentukan yang lain untuk bagian tanggal / waktu dan menggabungkannya untuk string offset tanggal-waktu yang lengkap.

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

Contoh penggunaan:

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

Saya tahu ini tahun 2020 dan kebanyakan orang mungkin menggunakan Moment.js sekarang, tetapi solusi salin & tempel sederhana terkadang masih berguna untuk dimiliki.

(Alasan saya membagi tanggal / waktu dan metode offset adalah karena saya menggunakan pustaka Datejs lama yang sudah menyediakan toStringmetode fleksibel dengan penentu format kustom, tetapi tidak menyertakan offset zona waktu. Oleh karena itu, saya menambahkan toISOLocaleStringuntuk siapa pun yang tidak kata perpustakaan.)

Luar biasa
sumber