Kullanıcının saat dilimi nasıl yok sayılır ve Date () belirli bir saat dilimini kullanmaya zorlanır


104

Bir JS uygulamasında, 1270544790922sunucudan (Ajax) zaman damgası (eq. ) Alıyorum.

Bu zaman damgasına dayanarak şunu Datekullanarak nesne oluşturuyorum:

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

Şimdi, _dategeçerli kullanıcı yerel saat diliminde kodu çözülmüş zaman damgası. Ben bunu istemiyorum

dateBu zaman damgasını Avrupa'daki Helsinki şehrinin şimdiki zamanına dönüştürmek istiyorum (kullanıcının şu anki saat dilimini göz ardı ederek).

Bunu nasıl yapabilirim?


Helsinki'de saat dilimi farkının kışın +2 ve DST'de +3 olduğunu biliyorum. Ama DST'nin ne zaman olduğunu kim bilebilir? Yalnızca JS'de bulunmayan bazı yerel ayar mekanizmaları
warpech

Bu mümkündür, ancak Javascript'in yerel yöntemlerini kullanmamaktır, çünkü javascript, kullanıcının sisteminin geçerli saat diliminden farklı bir zaman dilimi geçiş geçmişini belirleme yöntemine sahip değildir (ve bu arada, en azından 80'lerin tarihlerine gittiğimizde tarayıcıya bağımlıdır). Ancak bu şekilde mümkün: stackoverflow.com/a/12814213/1691517 ve cevabımın size doğru sonucu verdiğini düşünüyorum.
Timo Kähkönen

Yanıtlar:


64

Bir Date nesnesinin temel değeri aslında UTC'dir. Bunu kanıtlamak için yazarsanız fark new Date(0)gibi bir şey göreceksiniz: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 GMT'de 0 olarak kabul edilir, ancak .toString()yöntem yerel saati gösterir.

Büyük not, UTC, Evrensel zaman kodu anlamına gelir . Şu anda 2 farklı yerdeki geçerli saat aynı UTC'dir, ancak çıktı farklı şekilde biçimlendirilebilir.

Burada ihtiyacımız olan şey biraz biçimlendirme

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"

Bu işe yarar ama .... kullanıcının saat dilimini tanımladıkları için diğer tarih yöntemlerinden hiçbirini kendi amaçlarınız için gerçekten kullanamazsınız. İstediğiniz, Helsinki saat dilimiyle ilgili bir tarih nesnesidir. Bu noktada seçenekleriniz, bazı 3. parti kitaplıkları kullanmak (bunu tavsiye ederim) veya tarih nesnesini hacklemek, böylece yöntemlerinin çoğunu kullanabilmenizdir.

Seçenek 1 - üçüncü taraf gibi bir an-saat dilimi

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

Bu, şimdi yapacağımızdan çok daha zarif görünüyor.

Seçenek 2 - Tarih nesnesini hackleyin

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)

Hala GMT-0700 (PDT) olduğunu düşünüyor, ancak çok dikkatli bakmazsanız, bunu amaçlarınız için yararlı olan bir tarih nesnesi olarak yanlış edebilirsiniz.

Bir bölümü rahatça atladım. Tanımlayabilmen gerekiyor currentHelsinkiOffset. Eğer kullanabiliyorsa date.getTimezoneOffset()sunucu tarafında veya ifadeleri, saat dilimi değişiklikleri gerçekleştirilir zaman açıklamak için eğer sadece bazı Kullanıcılara bu sorunu çözmek gerekir.

Sonuç - Özellikle bu amaç için an-saat dilimi gibi bir tarih kitaplığı kullanmanız gerektiğini düşünüyorum .


ayrıca ... benzer bir görev basitçe bir konumdan gmt ofset kullanılarak yapılabilir. Bu durumda javascript'e hiç ihtiyacınız yok.
Parris

üzgünüm ama hayır, tam tersini kastediyorum :) Soruyu düzenledim, belki şimdi daha açık
warpech

Tamam, çözümümü değiştirdim. Sanırım aradığınız şey bu.
Parris

Maalesef aklıma gelen tek şey bu. Tarayıcının benim için "_helsinkiOffset" oluşturabileceğini düşündüm.
warpech

2
Bunun *60*60yerine *60000, getTime milisaniye cinsinden ve getTimezoneOffset dakikalar içinde olduğundan, bunun 60 * 60 == 3600 değil, dakikada 60000 milisaniye olduğu için olması gerektiğine inanıyorum
AaronLS

20

Milisaniyeleri ve kullanıcının saat dilimini hesaba katmak için aşağıdakileri kullanın:

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

Merkez için sabit bir ofset kullanarak değiştirmek için, CST kullanarak sabit bir 00:00 saatiyle bir tarih oluşturma kavramını kullandım, ardından o tarihin getUTCHHours'unu kullandım.
grantwparks

+1 Bu benim için çalıştı. Milisaniyelerle uğraşmadan 'cevabın' nasıl çalışacağından emin değilim.
Chris Wallis

2
@Ehren, gmt'ye ulaşmak için timezoneOffset'i eklemeniz ve ardından merkezi ofseti çıkarmanız gerekmez mi?
kodlayıcı

15

Sadece başka bir yaklaşım

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

Şerefe!


2
Yaz Saati Uygulaması nedeniyle bu istenmeyen sonuçlara neden olabilir. İstemci DST kullanan bir saat dilimindeyse, ayrıştırmanın sonucu bir saat gecikebilir (bazı bölgeler bir saatin kesirlerini kullanır). Örneğin: Kullanıcı New York'ta ve bugün 4 Temmuz. Bu, kullanıcının GMT -0400'de olduğu anlamına gelir (DST başladığından bu yana Doğu Yaz Saati Dilimi); geçen zaman damgası GMT-0500 olan 30 Ocak içindir (Doğu Standart Saat Dilimi - yılın bu zamanında DST yoktur). Sonuç bir saat kapalı olacaktır çünkü getTimezoneOffset () size Ocak ayındaki konumu değil, şimdi ofseti verir.
Dimitar Darazhanski

2
Bu sorunu çözmek için , geçmekte olduğunuz tarihin saat farkını almanız gerekir (şimdiki zaman kaymasını değil) : new Date().getTimezoneOffset()şu şekilde değiştirilmelidirnew Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski

13

O, bir şüphe var Cevap doğru sonucu vermez. Soruda soruyu soran, kullanıcının geçerli saat dilimini göz ardı ederek zaman damgasını sunucudan Hellsinki'deki geçerli saate dönüştürmek ister.

Gerçek şu ki, kullanıcının zaman dilimi her ne olursa olsun olabilir, bu yüzden ona güvenemeyiz.

Örneğin. zaman damgası 1270544790922 ve bizim bir fonksiyonumuz var:

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

Bir New Yorklu sayfayı ziyaret ettiğinde, uyarı (_helsenkiTime) yazdırılır:

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

Ve bir Finlandiyalı sayfayı ziyaret ettiğinde, uyarı (_helsenkiTime) yazdırır:

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

Bu nedenle, işlev yalnızca sayfa ziyaretçisinin bilgisayarında hedef saat dilimini (Avrupa / Helsinki) bulundurması, ancak dünyanın hemen hemen her yerinde başarısız olması durumunda doğrudur. Ve sunucu zaman damgası genellikle UNIX zaman damgası olduğundan, tanım gereği UTC'de olduğu için, Unix Epoch'tan (1 Ocak 1970 00:00:00 GMT) bu yana geçen saniye sayısı, zaman damgasından DST veya DST olmayanları belirleyemiyoruz.

Dolayısıyla çözüm, kullanıcının geçerli saat dilimini İPTAL ETMEK ve tarihin DST'de olup olmadığına bakılmaksızın UTC farkını hesaplamanın bir yolunu uygulamaktır. Javascript, kullanıcının geçerli saat dilimi dışında başka bir saat diliminin DST geçiş geçmişini belirlemek için yerel bir yönteme sahip değildir. Bunu en basit şekilde sunucu tarafı komut dosyasını kullanarak başarabiliriz, çünkü sunucunun zaman dilimi veritabanına tüm saat dilimlerinin tüm geçiş geçmişiyle kolayca erişebiliriz.

Ancak sunucunun (veya başka herhangi bir sunucunun) saat dilimi veritabanına erişiminiz yoksa VE zaman damgası UTC'deyse, Javascript'te DST kurallarını sabit kodlayarak benzer işlevselliği elde edebilirsiniz.

Avrupa / Helsinki'de 1998 - 2099 yıllarını kapsamak için aşağıdaki işlevi kullanabilirsiniz ( 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;
}

Kullanım örnekleri:

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

Ve bu, kullanıcının saat dilimine bakılmaksızın aşağıdakileri yazdırır:

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

Elbette, zaman damgasını (DST veya DST olmayan) sunucudaki zaman damgasına önceden eklenmiş bir biçimde döndürebilirseniz, bunu istemci tarafında hesaplamanız gerekmez ve işlevi çok basitleştirebilirsiniz. ANCAK timezoneOffset () 'i KULLANMAYINIZ, çünkü o zaman kullanıcının saat dilimi ile uğraşmanız gerekir ve bu istenen davranış değildir.


2
nb. Helsinki'de yalnızca bir 'l' vardır. Bu hata, bu yanıttan gerçekten uzaklaşır.
Ben McIntyre

@BenMcIntyre Küçük bir şaka. Ya da böyle olması gerekiyordu. :)
Timo Kähkönen

ah, asla kendi zamanınızı / zaman dilimi uygulamanızı kodlamayın ... ama -1'e çok tembelim
mb21

3

Helsinki saatinde zaman damgasını aldığınızı varsayarsak, 1 Ocak 1970 UTC gece yarısına ayarlanmış bir tarih nesnesi oluştururum (tarayıcının yerel saat dilimi ayarlarını göz ardı etmek için). Ardından gerekli milisaniye sayısını ekleyin.

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

Tarih nesnesinden her zaman UTC değerlerini sormak için daha sonra dikkatli olun. Bu şekilde kullanıcılar, yerel ayarlardan bağımsız olarak aynı tarih değerlerini göreceklerdir. Aksi takdirde tarih değerleri yerel saat ayarlarına göre kaydırılacaktır.


0

Kullanabilirsin setUTCMilliseconds()

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

Bu cevap yanlış. setUTCMilliseconds, belirtilen milisaniye sayısını Tarihe ekler.
Tibor

@Tibor: MozDev'den: setUTCMilliseconds()Yöntem, belirli bir tarih için milisaniyeleri evrensel saate göre ayarlar. [...] Belirttiğiniz bir parametre beklenen aralığın dışındaysa setUTCMilliseconds(), Date nesnesindeki tarih bilgilerini buna göre güncellemeye çalışır. Başka bir deyişle, DateUTC'de verilen Unix zaman damgasına sahip bir nesne oluşturursunuz .
jimasun

W3schools'tan: setUTCMilliseconds () yöntemi, evrensel saate göre milisaniyeleri (0'dan 999'a) ayarlar. Lütfen yukarıdaki cevabı deneyin ve kendiniz görün.
Tibor

MDN'den: Parametreler milisaniyeDeğer: 0 ile 999 arasında milisaniyeleri temsil eden bir sayı ...
Tibor

Örneğiniz beklendiği gibi çalışmıyor, çünkü new Date () geçerli yerel tarih ile bir tarih nesnesi oluşturuyor. Ve bundan sonra kendisine belirtilen milisaniye sayısını ekler. Dolayısıyla, örneğinizde mevcut yerel tarih 2017-05-04 ise, ortaya çıkan tarih 4027 yılından sonra bir yerde olacaktır ...
Tibor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.