Cara mengabaikan zona waktu pengguna dan memaksa Date () menggunakan zona waktu tertentu

104

Dalam aplikasi JS, saya menerima stempel waktu (persamaan 1270544790922) dari server (Ajax).

Berdasarkan stempel waktu itu saya membuat Dateobjek menggunakan:

var _date = new Date();
_date.setTime(1270544790922);

Sekarang, _datestempel waktu yang didekodekan dalam zona waktu lokal pengguna saat ini. Saya tidak menginginkan itu.

Saya ingin _ datemengonversi stempel waktu ini ke waktu saat ini di kota Helsinki di Eropa (dengan mengabaikan zona waktu pengguna saat ini).

Bagaimana saya bisa melakukan itu?

warpech
sumber
Saya tahu bahwa perbedaan zona waktu di Helsinki adalah +2 di musim dingin dan +3 di DST. Tapi siapa yang tahu kapan DST? Hanya beberapa mekanisme lokal yang tidak tersedia di JS
warpech
Hal ini dimungkinkan, tetapi tidak menggunakan metode asli Javascript, karena javascript tidak memiliki metode untuk menentukan riwayat transisi zona waktu zona waktu selain zona waktu sistem pengguna saat ini (dan dengan cara bergantung browser setidaknya ketika kita pergi ke tanggal 80-an). Tetapi cara ini dimungkinkan: stackoverflow.com/a/12814213/1691517 dan saya pikir jawaban saya memberi Anda hasil yang benar.
Timo Kähkönen

Jawaban:

64

Nilai yang mendasari objek Tanggal sebenarnya dalam UTC. Untuk membuktikan ini, perhatikan bahwa jika Anda mengetik new Date(0)Anda akan melihat sesuatu seperti: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 diperlakukan sebagai 0 dalam GMT, tetapi .toString()metode menunjukkan waktu lokal.

Catatan besar, UTC adalah singkatan dari kode waktu Universal . Waktu saat ini di 2 tempat berbeda adalah UTC yang sama, tetapi keluarannya dapat diformat secara berbeda.

Yang kita butuhkan di sini adalah beberapa pemformatan

var _date = new Date(1270544790922); 
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"

Ini berfungsi tetapi .... Anda tidak dapat benar-benar menggunakan metode tanggal lain untuk tujuan Anda karena metode tersebut menjelaskan zona waktu pengguna. Yang Anda inginkan adalah objek tanggal yang terkait dengan zona waktu Helsinki. Pilihan Anda saat ini adalah menggunakan beberapa pustaka pihak ketiga (saya merekomendasikan ini), atau meretas objek tanggal sehingga Anda dapat menggunakan sebagian besar metodenya.

Opsi 1 - pihak ketiga seperti zona waktu-momen

moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12

Ini terlihat jauh lebih elegan daripada yang akan kita lakukan selanjutnya.

Opsi 2 - Retas objek tanggal

var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)

Ia masih menganggap itu GMT-0700 (PDT), tetapi jika Anda tidak menatap terlalu keras, Anda mungkin dapat salah mengira itu sebagai objek tanggal yang berguna untuk tujuan Anda.

Saya dengan mudah melewatkan satu bagian. Anda harus bisa mendefinisikan currentHelsinkiOffset. Jika Anda dapat menggunakan date.getTimezoneOffset()di sisi server, atau hanya menggunakan beberapa pernyataan if untuk menjelaskan kapan perubahan zona waktu akan terjadi, itu seharusnya menyelesaikan masalah Anda.

Kesimpulan - Saya pikir terutama untuk tujuan ini Anda harus menggunakan perpustakaan tanggal seperti zona waktu-momen .

Parris
sumber
juga ... tugas serupa dapat dilakukan, hanya dengan menggunakan gmt offset dari satu lokasi. Anda tidak membutuhkan javascript sama sekali dalam kasus itu.
Parris
maaf tapi tidak, maksud saya kebalikannya :) Saya mengedit pertanyaannya, mungkin lebih jelas sekarang
warpech
Ok saya mengubah solusi saya. Saya pikir inilah yang Anda cari.
Parris
Sayangnya itu satu-satunya hal yang juga saya pikirkan. Saya pikir mungkin browser bisa menghasilkan "_helsinkiOffset" untuk saya.
warpech
2
Saya percaya *60*60seharusnya demikian *60000, karena getTime dalam milidetik dan getTimezoneOffset dalam hitungan menit, yang mana ada 60.000 milidetik dalam satu menit, bukan 60 * 60 == 3600
AaronLS
20

Untuk memperhitungkan milidetik dan zona waktu pengguna, gunakan yang berikut ini:

var _userOffset = _date.getTimezoneOffset()*60*1000; // user's offset time
var _centralOffset = 6*60*60*1000; // 6 for central time - use whatever you need
_date = new Date(_date.getTime() - _userOffset + _centralOffset); // redefine variable
Ehren
sumber
Untuk mengganti menggunakan offset tetap untuk pusat, saya menggunakan konsep membuat tanggal menggunakan CST dengan waktu tetap 00:00, lalu getUTCHHours pada tanggal tersebut.
grantwparks
+1 Ini berhasil untuk saya. Tidak yakin bagaimana jawaban 'the' bisa bekerja tanpa berurusan dalam milidetik.
Chris Wallis
2
@Ehren bukankah seharusnya Anda menambahkan timezoneOffset untuk mendapatkan gmt dan kemudian mengurangi offset pusat?
kode
15

Hanya pendekatan lain

function parseTimestamp(timestampStr) {
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000));
};

//Sun Jan 01 2017 12:00:00
var timestamp = 1483272000000;
date = parseTimestamp(timestamp);
document.write(date);

Bersulang!

Mohanraj Balasubramaniam
sumber
2
Ini bisa memiliki hasil yang tidak diinginkan karena Waktu Musim Panas. Jika klien berada dalam zona waktu yang menggunakan DST, hasil penguraian bisa meleset selama satu jam (beberapa zona menggunakan sepersekian jam). Misalnya: Pengguna berada di NY dan hari ini adalah 4 Juli. Itu berarti bahwa pengguna berada dalam GMT -0400 (Zona Waktu Musim Panas Timur sejak DST berlaku); stempel waktu yang diteruskan adalah untuk 30 Januari, yaitu GMT-0500 (Zona Waktu Standar Timur - tidak ada DST waktu itu dalam setahun). Hasilnya akan menjadi satu jam off, karena getTimezoneOffset () memberi Anda offset sekarang, bukan pada bulan Januari.
Dimitar Darazhanski
2
Untuk memperbaiki masalah ini, Anda perlu mengambil offset waktu dari tanggal yang Anda lewati (bukan shift waktu saat ini) : new Date().getTimezoneOffset()harus diubah menjadinew Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski
13

Saya curiga, Jawabannya tidak memberikan hasil yang benar. Dalam pertanyaan, penanya ingin mengubah stempel waktu dari server ke waktu saat ini di Hellsinki dengan mengabaikan zona waktu pengguna saat ini.

Ini adalah fakta bahwa zona waktu pengguna bisa menjadi apa pun sehingga kami tidak dapat mempercayainya.

Jika mis. timestamp adalah 1270544790922 dan kami memiliki fungsi:

var _date = new Date();
_date.setTime(1270544790922);
var _helsenkiOffset = 2*60*60;//maybe 3
var _userOffset = _date.getTimezoneOffset()*60*60; 
var _helsenkiTime = new Date(_date.getTime()+_helsenkiOffset+_userOffset);

Ketika seorang warga New York mengunjungi halaman, peringatan (_helsenkiTime) mencetak:

Tue Apr 06 2010 05:21:02 GMT-0400 (EDT)

Dan saat orang Finlandia mengunjungi halaman tersebut, alert (_helsenkiTime) mencetak:

Tue Apr 06 2010 11:55:50 GMT+0300 (EEST)

Jadi fungsinya benar hanya jika pengunjung halaman memiliki zona waktu target (Eropa / Helsinki) di komputernya, tetapi gagal di hampir setiap bagian lain dunia. Dan karena stempel waktu server biasanya stempel waktu UNIX, yang menurut definisi dalam UTC, jumlah detik sejak Unix Epoch (1 Januari 1970 00:00:00 GMT), kami tidak dapat menentukan DST atau non-DST dari stempel waktu.

Jadi solusinya adalah untuk MENGHINDARI zona waktu pengguna saat ini dan menerapkan beberapa cara untuk menghitung UTC offset apakah tanggal tersebut dalam DST atau tidak. Javascript tidak memiliki metode asli untuk menentukan riwayat transisi DST zona waktu lain selain zona waktu pengguna saat ini. Kita dapat mencapai ini paling sederhana dengan menggunakan skrip sisi server, karena kita memiliki akses mudah ke basis data zona waktu server dengan seluruh riwayat transisi dari semua zona waktu.

Tetapi jika Anda tidak memiliki akses ke database zona waktu server (atau server lain) DAN stempel waktunya dalam UTC, Anda bisa mendapatkan fungsi serupa dengan melakukan hard coding aturan DST dalam Javascript.

Untuk mencakup tanggal di tahun 1998 - 2099 di Eropa / Helsinki, Anda dapat menggunakan fungsi berikut ( jsfiddled ):

function timestampToHellsinki(server_timestamp) {
    function pad(num) {
        num = num.toString();
        if (num.length == 1) return "0" + num;
        return num;
    }

    var _date = new Date();
    _date.setTime(server_timestamp);

    var _year = _date.getUTCFullYear();

    // Return false, if DST rules have been different than nowadays:
    if (_year<=1998 && _year>2099) return false;

    // Calculate DST start day, it is the last sunday of March
    var start_day = (31 - ((((5 * _year) / 4) + 4) % 7));
    var SUMMER_start = new Date(Date.UTC(_year, 2, start_day, 1, 0, 0));

    // Calculate DST end day, it is the last sunday of October
    var end_day = (31 - ((((5 * _year) / 4) + 1) % 7))
    var SUMMER_end = new Date(Date.UTC(_year, 9, end_day, 1, 0, 0));

    // Check if the time is between SUMMER_start and SUMMER_end
    // If the time is in summer, the offset is 2 hours
    // else offset is 3 hours
    var hellsinkiOffset = 2 * 60 * 60 * 1000;
    if (_date > SUMMER_start && _date < SUMMER_end) hellsinkiOffset = 
    3 * 60 * 60 * 1000;

    // Add server timestamp to midnight January 1, 1970
    // Add Hellsinki offset to that
    _date.setTime(server_timestamp + hellsinkiOffset);
    var hellsinkiTime = pad(_date.getUTCDate()) + "." + 
    pad(_date.getUTCMonth()) + "." + _date.getUTCFullYear() + 
    " " + pad(_date.getUTCHours()) + ":" +
    pad(_date.getUTCMinutes()) + ":" + pad(_date.getUTCSeconds());

    return hellsinkiTime;
}

Contoh penggunaan:

var server_timestamp = 1270544790922;
document.getElementById("time").innerHTML = "The timestamp " + 
server_timestamp + " is in Hellsinki " + 
timestampToHellsinki(server_timestamp);

server_timestamp = 1349841923 * 1000;
document.getElementById("time").innerHTML += "<br><br>The timestamp " + 
server_timestamp + " is in Hellsinki " + timestampToHellsinki(server_timestamp);

var now = new Date();
server_timestamp = now.getTime();
document.getElementById("time").innerHTML += "<br><br>The timestamp is now " +
server_timestamp + " and the current local time in Hellsinki is " +
timestampToHellsinki(server_timestamp);​

Dan ini mencetak yang berikut terlepas dari zona waktu pengguna:

The timestamp 1270544790922 is in Hellsinki 06.03.2010 12:06:30

The timestamp 1349841923000 is in Hellsinki 10.09.2012 07:05:23

The timestamp is now 1349853751034 and the current local time in Hellsinki is 10.09.2012 10:22:31

Tentu saja jika Anda dapat mengembalikan stempel waktu dalam bentuk yang offset (DST atau non-DST) sudah ditambahkan ke stempel waktu di server, Anda tidak perlu menghitungnya di sisi klien dan Anda dapat banyak menyederhanakan fungsinya. TAPI ingat untuk TIDAK menggunakan timezoneOffset (), karena Anda harus berurusan dengan zona waktu pengguna dan ini bukan perilaku yang diinginkan.

Timo Kähkönen
sumber
2
nb. Helsinki hanya memiliki satu 'l'. Kesalahan ini benar-benar mengurangi jawaban ini.
Ben McIntyre
@BenIntyIni sedikit lelucon. Atau seharusnya seperti itu. :)
Timo Kähkönen
ah, jangan pernah membuat kode penerapan zona waktu / waktu Anda sendiri ... tapi saya terlalu malas untuk -1
mb21
3

Anggap Anda mendapatkan stempel waktu dalam waktu Helsinki, saya akan membuat objek tanggal yang disetel ke tengah malam 1 Januari 1970 UTC (untuk mengabaikan setelan zona waktu lokal browser). Kemudian tambahkan saja jumlah milidetik yang dibutuhkan.

var _date	= new Date( Date.UTC(1970, 0, 1, 0, 0, 0, 0) );
_date.setUTCMilliseconds(1270544790922);

alert(_date); //date shown shifted corresponding to local time settings
alert(_date.getUTCFullYear());    //the UTC year value
alert(_date.getUTCMonth());       //the UTC month value
alert(_date.getUTCDate());        //the UTC day of month value
alert(_date.getUTCHours());       //the UTC hour value
alert(_date.getUTCMinutes());     //the UTC minutes value

Hati-hati nanti, untuk selalu menanyakan nilai UTC dari objek tanggal. Dengan cara ini pengguna akan melihat nilai tanggal yang sama terlepas dari pengaturan lokal. Jika tidak, nilai tanggal akan bergeser sesuai dengan pengaturan waktu lokal.

Tibor
sumber
0

Anda bisa menggunakan setUTCMilliseconds()

var _date = new Date();
_date.setUTCMilliseconds(1270544790922);
jimasun
sumber
Jawaban ini salah. setUTCMilliseconds menambahkan jumlah milidetik yang ditentukan ke Tanggal.
Tibor
@Tibor: Dari MozDev: setUTCMilliseconds()Metode ini menyetel milidetik untuk tanggal tertentu sesuai dengan waktu universal. [...] Jika parameter yang Anda tentukan berada di luar kisaran yang diharapkan, setUTCMilliseconds()mencoba memperbarui informasi tanggal di objek Tanggal sesuai dengan itu. Dengan kata lain, Anda membuat Dateobjek yang memiliki stempel waktu Unix yang diberikan, pada UTC.
jimasun
Dari w3schools: Metode setUTCMilliseconds () menyetel milidetik (dari 0 hingga 999), menurut waktu universal. Silahkan coba jawaban diatas dan buktikan sendiri.
Tibor
Dari MDN: Parameter milidetik Nilai: Angka antara 0 dan 999, mewakili milidetik ...
Tibor
Contoh Anda tidak berfungsi seperti yang diharapkan, karena new Date () membuat objek tanggal dengan tanggal lokal saat ini. Dan setelah itu menambahkan jumlah milidetik yang ditentukan untuk itu. Jadi dalam contoh Anda, jika tanggal lokal saat ini adalah 2017-05-04, tanggal yang dihasilkan akan berada di suatu tempat setelah tahun 4027 ...
Tibor