Cara menginisialisasi Tanggal JavaScript ke zona waktu tertentu

232

Saya memiliki waktu tanggal dalam zona waktu tertentu sebagai string dan saya ingin mengonversikan ini ke waktu lokal. Tapi, saya tidak tahu cara mengatur zona waktu di objek Date.

Misalnya, saya punya Feb 28 2013 7:00 PM ET,maka saya bisa

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

Sejauh yang saya tahu, saya dapat mengatur waktu UTC atau waktu lokal. Tapi, bagaimana cara mengatur waktu di zona waktu lain?

Saya mencoba menggunakan add / kurangi offset dari UTC tetapi saya tidak tahu bagaimana cara melawan penghematan siang hari. Saya tidak yakin apakah saya menuju ke arah yang benar.

Bagaimana saya bisa mengubah waktu dari zona waktu berbeda ke waktu lokal dalam javascript?

beraspal
sumber
1
Objek tanggal tidak memiliki zona waktu, mereka UTC.
RobG

Jawaban:

352

Latar Belakang

DateObjek JavaScript melacak waktu dalam UTC secara internal, tetapi biasanya menerima input dan menghasilkan output pada waktu lokal komputer itu berjalan. Ini memiliki sedikit fasilitas untuk bekerja dengan waktu di zona waktu lainnya.

Representasi internal suatu Dateobjek adalah angka tunggal, yang mewakili jumlah milidetik yang telah berlalu sejak itu 1970-01-01 00:00:00 UTC, tanpa memperhatikan detik kabisat. Tidak ada zona waktu atau format string yang disimpan di objek Date itu sendiri. Ketika berbagai fungsi Dateobjek digunakan, zona waktu lokal komputer diterapkan ke representasi internal. Jika fungsi menghasilkan string, maka informasi lokal komputer dapat dipertimbangkan untuk menentukan cara menghasilkan string itu. Detailnya bervariasi per fungsi, dan beberapa spesifik implementasi.

Satu-satunya operasi yang Datedapat dilakukan objek dengan zona waktu non-lokal adalah:

  • Itu dapat mengurai string yang berisi offset UTC numerik dari zona waktu mana pun. Ini menggunakan ini untuk menyesuaikan nilai yang diuraikan, dan menyimpan setara UTC. Waktu lokal asli dan offset tidak dipertahankan dalam Dateobjek yang dihasilkan . Sebagai contoh:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
    d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
  • Di lingkungan yang telah menerapkan API Internasionalisasi ECMASCript (alias "Intl"), Dateobjek dapat menghasilkan string spesifik lokal yang disesuaikan dengan pengidentifikasi zona waktu tertentu. Ini dilakukan melalui timeZoneopsi ke toLocaleStringdan variasinya. Sebagian besar implementasi akan mendukung pengidentifikasi zona waktu IANA, seperti 'America/New_York'. Sebagai contoh:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toLocaleString('en-US', { timeZone: 'America/New_York' })
    //=> "4/12/2020, 12:00:00 PM"
    // (midnight in China on Apring 13th is noon in New York on April 12th)

    Sebagian besar lingkungan modern mendukung set lengkap pengidentifikasi zona waktu IANA ( lihat tabel kompatibilitas di sini ). Namun, perlu diingat bahwa satu-satunya pengidentifikasi yang diperlukan untuk didukung oleh Intl adalah 'UTC', oleh karena itu Anda harus memeriksa dengan seksama jika Anda perlu mendukung browser lama atau lingkungan atipikal (misalnya, perangkat IoT ringan).

Perpustakaan

Ada beberapa perpustakaan yang dapat digunakan untuk bekerja dengan zona waktu. Meskipun mereka masih tidak dapat membuat Dateobjek berperilaku berbeda, mereka biasanya mengimplementasikan basis data zona waktu IANA standar dan menyediakan fungsi untuk menggunakannya dalam JavaScript. Perpustakaan modern menggunakan data zona waktu yang disediakan oleh API Intl, tetapi perpustakaan yang lebih tua biasanya memiliki overhead, terutama jika Anda berjalan di browser web, karena basis datanya bisa menjadi agak besar. Beberapa perpustakaan ini juga memungkinkan Anda untuk mengurangi set data secara selektif, baik dengan zona waktu mana yang didukung dan / atau rentang tanggal yang dapat Anda gunakan.

Berikut adalah perpustakaan yang perlu dipertimbangkan:

Perpustakaan berbasis internasional

Pengembangan baru harus memilih dari salah satu implementasi ini, yang mengandalkan API Intl untuk data zona waktu mereka:

Perpustakaan Non-Internasional

Perpustakaan-perpustakaan ini dikelola, tetapi membawa beban mengemas data zona waktu mereka sendiri, yang bisa sangat besar.

* Sementara Moment dan Moment-Timezone sebelumnya direkomendasikan, tim Moment sekarang lebih suka pengguna memilih Luxon untuk pengembangan baru.

Perpustakaan Dihentikan

Perpustakaan-perpustakaan ini telah secara resmi dihentikan dan seharusnya tidak lagi digunakan.

Proposal Masa Depan

The TC39 Temporal Proposal bertujuan untuk menyediakan satu set baru objek standar untuk bekerja dengan tanggal dan waktu dalam bahasa JavaScript itu sendiri. Ini akan mencakup dukungan untuk objek yang sadar zona waktu.

Matt Johnson-Pint
sumber
1
Silakan ubah "represent" menjadi "output / parse", karena cap waktu yang diwakili adalah zona waktu
Bergi
1
@Bergi - Saya sudah memikirkan kembali ini, dan setuju dengan Anda. Memperbarui jawaban saya sesuai dengan itu.
Matt Johnson-Pint
1
Ketika Anda melakukan ini di konsol Firebug:, var date_time = new Date()nilai date_timeis (untuk saya di AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}dan karena itu sepertinya ia telah menyimpan zona waktu. Bagaimana saya memastikan bahwa date_timeini murni waktu UTC, tanpa ada zona waktu?
user1063287
Hmm, menurut jawaban ini ( stackoverflow.com/a/8047885/1063287 ), ini adalah ini: new Date().getTime();
user1063287
1
@ user1063287 — metode toString menggunakan offset zona waktu host untuk menghasilkan tanggal dan waktu di zona waktu "lokal". Objek tanggal itu sendiri memiliki nilai waktu yang merupakan offset dari 1970-01-01T00: 00: 00Z, sehingga secara efektif UTC. Untuk melihat tanggal dan waktu UTC, gunakan toISOString .
RobG
69

Seperti yang dikatakan Matt Johnson

Jika Anda dapat membatasi penggunaan Anda untuk browser web modern, Anda sekarang dapat melakukan hal berikut tanpa perpustakaan khusus:

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

Ini bukan solusi yang komprehensif, tetapi bekerja untuk banyak skenario yang hanya memerlukan konversi output (dari UTC atau waktu lokal ke zona waktu tertentu, tetapi tidak ke arah lain).

Jadi, meskipun peramban tidak dapat membaca zona waktu IANA saat membuat tanggal, atau memiliki metode apa pun untuk mengubah zona waktu pada objek Tanggal yang ada, tampaknya ada peretasan di sekitarnya:

function changeTimezone(date, ianatz) {

  // suppose the date is 12:00 UTC
  var invdate = new Date(date.toLocaleString('en-US', {
    timeZone: ianatz
  }));

  // then invdate will be 07:00 in Toronto
  // and the diff is 5 hours
  var diff = date.getTime() - invdate.getTime();

  // so 12:00 in Toronto is 17:00 UTC
  return new Date(date.getTime() + diff);

}

// E.g.
var there = new Date();
var here = changeTimezone(there, "America/Toronto");

console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);

Commonpike
sumber
6
Saya bertanya-tanya mengapa jawaban ini tidak mendapatkan lebih banyak cinta. Untuk tujuan saya itu benar-benar solusi terbaik. Di aplikasi saya, semua tanggal adalah UTC, tetapi mereka harus input atau output dalam zona waktu IANA eksplisit di UI. Untuk tujuan ini saya menggunakan solusi ini dan berfungsi dengan baik. Sederhana, efektif, standar.
logidelic
2
@logidelic, ini adalah posting yang cukup baru. sejujurnya, saya kagum pada diri sendiri ketika saya tersadar bahwa Anda bisa menggunakan toLocaleStringuntuk mencapai efek sebaliknya, dan ya, ini harus menjadi pengetahuan umum! pergi menulis artikel tentang hal itu dan menyebutkan saya :-)
commonpike
1
Untuk lingkungan simpul yang belum dalam GMT, kode di atas perlu berubah dari var invdate = Date baru (date.toLocaleString ('en-US', {timeZone: ianatz})); ke dalam var invdate = Tanggal baru ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.
5
Browser tidak diharuskan untuk menguraikan output dari toLocaleString , jadi itu didasarkan pada premis yang salah.
RobG
3
"... kenapa jawaban ini tidak mendapatkan lebih banyak cinta?" - karena Dateobjek yang dikembalikan adalah dusta. Bahkan contoh yang ditunjukkan, output string termasuk zona waktu mesin lokal. Di komputer saya, kedua hasil mengatakan "GMT-0700 (Waktu Siang Pasifik)", di mana itu hanya benar untuk yang pertama (Toronto sebenarnya di Waktu Timur.) Di luar hanya output string, cap waktu yang disimpan dalam Dateobjek telah bergeser ke titik waktu yang berbeda. Ini bisa menjadi teknik yang berguna, tetapi dilengkapi dengan banyak peringatan - terutama bahwa Datefungsi objek tidak akan memiliki output normal.
Matt Johnson-Pint
30

Anda dapat menentukan offset zona waktu aktif new Date(), misalnya:

new Date('Feb 28 2013 19:00:00 EST')

atau

new Date('Feb 28 2013 19:00:00 GMT-0500')

Sejak Datemenyimpan waktu UTC (yaitu getTimemengembalikan dalam UTC), javascript akan mereka mengubah waktu menjadi UTC, dan ketika Anda memanggil hal-hal seperti toStringjavascript akan mengubah waktu UTC menjadi zona waktu lokal browser dan mengembalikan string dalam zona waktu lokal, yaitu Jika saya menggunakan UTC+8:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

Anda juga dapat menggunakan getHours/Minute/Secondmetode normal :

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(Ini 8berarti setelah waktu dikonversi ke waktu lokal saya - UTC+8, nomor jamnya adalah 8.)

maowtm
sumber
16
Parsing format apa pun selain format diperpanjang ISO 8601 tergantung pada implementasi dan tidak boleh diandalkan. Tidak ada standar untuk singkatan zona waktu, misalnya "EST" mungkin mewakili salah satu dari 3 zona berbeda.
RobG
1
Contoh-contoh dalam komentar ini sering tidak berfungsi di internet explorer. Seperti disebutkan dalam komentar sebelumnya untuk posting ini, ISO 8601 penting. Saya mengonfirmasi dengan membaca Spesifikasi bahasa edisi ECMA-262 (Javascript 5).
Dakusan
12

Ini harus menyelesaikan masalah Anda, jangan ragu untuk menawarkan perbaikan. Metode ini juga akan memperhitungkan waktu musim panas untuk tanggal yang ditentukan.

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

Cara menggunakan dengan zona waktu dan waktu lokal:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
ayam
sumber
Solusi ini cocok untuk kebutuhan saya. Saya tidak yakin bagaimana tepatnya saya bisa menjelaskan penghematan siang hari.
Hossein Amin
Kenapa tidak tzDatelangsung saja kembali ?
Retribusi
8

Saya menemukan cara yang paling didukung untuk melakukan ini, tanpa khawatir tentang perpustakaan pihak ketiga, adalah dengan menggunakan getTimezoneOffsetuntuk menghitung cap waktu yang sesuai, atau memperbarui waktu kemudian menggunakan metode normal untuk mendapatkan tanggal dan waktu yang diperlukan.

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

EDIT

Saya sebelumnya menggunakan UTCmetode ketika melakukan transformasi tanggal, yang tidak benar. Dengan menambahkan offset ke waktu, menggunakan getfungsi lokal akan mengembalikan hasil yang diinginkan.

Shaun Cockerill
sumber
Di mana Anda menghitung timezone_offset?
Anand Somani
1
Ups, saya salah memberi label pada salah satu variabel saya. timezone_offsetseharusnya offset, atau sebaliknya visa. Saya telah mengedit jawaban saya untuk menunjukkan nama variabel yang benar.
Shaun Cockerill
3

Saya mengalami masalah yang sama dengan tes unit (khususnya bercanda ketika tes unit berjalan secara lokal untuk membuat snapshot dan kemudian server CI berjalan di (berpotensi) zona waktu yang berbeda menyebabkan perbandingan snapshot gagal). Saya mengejek kami Datedan beberapa metode pendukung seperti:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});
JRulle
sumber
2

Coba gunakan ctoc dari npm. https://www.npmjs.com/package/ctoc_timezone

Ini memiliki fungsi sederhana untuk mengubah zona waktu (sebagian besar zona waktu sekitar 400) dan semua format khusus yang Anda inginkan ditampilkan.

Sandeep Chowdary
sumber
2

Berdasarkan jawaban di atas, saya menggunakan liner asli ini untuk mengubah string zona waktu lama menjadi string tiga huruf:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

Ini akan memberikan PDT atau PST tergantung pada tanggal yang disediakan. Dalam kasus penggunaan khusus saya, berkembang di Salesforce (Aura / Lightning), kami bisa mendapatkan zona waktu pengguna dalam format panjang dari backend.

Shane
sumber
Hanya karena saya suka bermain pedant dari waktu ke waktu, 'America / Los_Angeles' bukan zona waktu, itu adalah "lokasi yang representatif" untuk aturan penyeimbang dan penghematan siang hari tertentu dan sejarah perubahan tersebut (per basis data zona waktu IANA ) . ;-)
RobG
Haha, Anda harus memposting komentar itu pada beberapa jawaban di sini: P
Shane
2

Saya tahu ini terlambat 3 tahun, tetapi mungkin bisa membantu orang lain karena saya belum menemukan yang seperti itu kecuali perpustakaan time-timezone, yang tidak persis sama dengan apa yang dia minta di sini.

Saya telah melakukan sesuatu yang mirip dengan zona waktu Jerman, ini sedikit rumit karena waktu musim panas dan tahun kabisat di mana Anda memiliki 366 hari.

mungkin perlu sedikit kerja dengan fungsi "isDaylightSavingTimeInGermany" sementara zona waktu yang berbeda berubah pada waktu yang berbeda dengan waktu musim panas.

bagaimanapun, lihat halaman ini: https://github.com/zerkotin/german-timezone-converter/wiki

metode utamanya adalah: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Saya sudah berusaha mendokumentasikannya, jadi tidak akan membingungkan.

zerkotin
sumber
Ini harus menjadi komentar, itu bukan jawaban.
RobG
1

Coba: date-from-timezone , ia menyelesaikan tanggal yang diharapkan dengan bantuan yang tersedia secara asli Intl.DateTimeFormat.

Saya menggunakan metode itu di salah satu proyek saya selama beberapa tahun, tetapi sekarang saya memutuskan untuk mempublikasikannya sebagai proyek OS kecil :)

Mariusz Nowak
sumber
1

Mungkin ini akan membantu Anda

/**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
);

Juan04
sumber
0

Untuk pengguna ionik, saya tidak suka dengan ini karena .toISOString()harus digunakan dengan template html.

Ini akan mengambil tanggal saat ini, tetapi tentu saja dapat ditambahkan ke jawaban sebelumnya untuk tanggal yang dipilih.

Saya memperbaikinya menggunakan ini:

date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();

* 60000 menunjukkan UTC -6 yang merupakan CST sehingga TimeZone apa pun yang diperlukan, angka dan perbedaan dapat diubah.

Stephen Romero
sumber
Ini bukan jawaban yang dicari si penanya. Dia mencari untuk mendapatkan tanggal ke zona waktu tertentu.
Richard Vergis
@RichardVergis Pertanyaannya dengan jelas menyatakan bahwa pengguna ingin mengubah ke waktu lokal, tetapi tidak menyatakan di zona waktu mana mereka berada. Saya menyatakan zona waktu saya sebagai contoh dan pengguna dapat dengan jelas membaca berdasarkan contoh saya, mereka dapat memasukkan offset zona waktu mereka.
Stephen Romero
-2

Sedang menghadapi masalah yang sama, gunakan yang ini

Console.log (Date.parse ("13 Jun 2018 10:50:39 GMT + 1"));

Ini akan mengembalikan milidetik ke mana Anda dapat memeriksa apakah +100 timzone menginternalisasi waktu Inggris Semoga bermanfaat !!

sandeep kumar
sumber
3
(Posting ini sepertinya tidak memberikan jawaban yang berkualitas untuk pertanyaan. Harap edit jawaban Anda, atau cukup kirimkan sebagai komentar untuk pertanyaan).
sɐunıɔ ןɐ qɐp