Bir JavaScript Tarihini belirli bir saat dilimine başlatma


232

Bir dize olarak belirli bir saat diliminde tarih saat var ve bunu yerel saate dönüştürmek istiyorum. Ancak, Date nesnesinde saat dilimini nasıl ayarlayacağımı bilmiyorum.

Örneğin, Feb 28 2013 7:00 PM ET,o zaman yapabilirim

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

Bildiğim kadarıyla, UTC saatini veya yerel saati ayarlayabilirim. Ancak, başka bir saat diliminde nasıl zaman ayarlayabilirim?

UTC'den ofseti ekleme / çıkarma kullanmaya çalıştım ama gün ışığından nasıl tasarruf edeceğimi bilmiyorum. Doğru yöne gidip gitmediğimden emin değilim.

Javascript'te saati farklı bir saat diliminden yerel saate dönüştürmeye nasıl gidebilirim?



1
Tarih nesnelerinin bir saat dilimi yoktur, bunlar UTC'dir.
RobG

Yanıtlar:


352

Arka fon

JavaScript'in Datenesnesi UTC'de saati dahili olarak izler, ancak genellikle girdiyi kabul eder ve üzerinde çalıştığı bilgisayarın yerel saatinde çıktı üretir. Diğer zaman dilimlerinde zamanla çalışmak için çok az imkan vardır.

Bir Datenesnenin dahili temsili, 1970-01-01 00:00:00 UTCartık saniye dikkate alınmadan bu yana geçen milisaniye sayısını temsil eden tek bir sayıdır . Date nesnesinin kendisinde depolanan bir saat dilimi veya dize biçimi yoktur. DateNesnenin çeşitli işlevleri kullanıldığında, bilgisayarın yerel saat dilimi dahili gösterime uygulanır. İşlev bir dize üretirse, o dizenin nasıl üretileceğini belirlemek için bilgisayarın yerel bilgileri dikkate alınabilir. Ayrıntılar işleve göre değişir ve bazıları uygulamaya özeldir.

DateNesnenin yerel olmayan saat dilimleriyle yapabileceği tek işlemler şunlardır:

  • Herhangi bir saat diliminden sayısal UTC ofseti içeren bir dizeyi ayrıştırabilir. Ayrıştırılan değeri ayarlamak için bunu kullanır ve UTC eşdeğerini saklar. Orijinal yerel saat ve ofset, elde edilen Datenesnede tutulmaz . Örneğin:

    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)
  • ECMASCript Uluslararasılaştırma API'sını ("Intl" olarak da bilinir) uygulayan ortamlarda, Datenesne belirli bir saat dilimi tanımlayıcısına ayarlanmış yerel ayara özgü bir dize üretebilir. Bu, timeZoneseçenek toLocaleStringve varyasyonları ile gerçekleştirilir. Çoğu uygulama, IANA zaman dilimi tanımlayıcılarını destekleyecektir 'America/New_York'. Örneğin:

    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)

    Modern ortamların çoğu, IANA zaman dilimi tanımlayıcılarının tamamını destekler ( buradaki uyumluluk tablosuna bakın ). Bununla birlikte, Intl tarafından desteklenmesi gereken tek tanımlayıcının olduğunu unutmayın 'UTC', bu nedenle eski tarayıcıları veya atipik ortamları (örneğin, hafif IoT cihazları) desteklemeniz gerekip gerekmediğini dikkatlice kontrol etmeniz gerekir.

Kütüphaneler

Saat dilimleriyle çalışmak için kullanılabilecek birkaç kütüphane vardır. DateNesnenin hala farklı davranmasını sağlayamasalar da, genellikle standart IANA saat dilimi veritabanını uygular ve JavaScript'te kullanmak için işlevler sağlarlar. Modern kütüphaneler, Intl API tarafından sağlanan saat dilimi verilerini kullanır, ancak veritabanı biraz daha büyük olabileceğinden, özellikle bir web tarayıcısında çalışıyorsanız eski kütüphanelerin genel olarak ek yükü vardır. Bu kitaplıklardan bazıları, hangi zaman dilimlerinin desteklendiği ve / veya çalışabileceğiniz tarih aralığına göre veri kümesini seçici olarak azaltmanıza izin verir.

Dikkate alınması gereken kütüphaneler şunlardır:

Uluslararası Tabanlı Kütüphaneler

Yeni geliştirme, saat dilimi verileri için Intl API'sine dayanan bu uygulamalardan birini seçmelidir:

Uluslararası Olmayan Kütüphaneler

Bu kütüphaneler korunur, ancak kendi zaman dilimi verilerini paketleme yükünü taşırlar ki bu oldukça büyük olabilir.

* Moment ve Moment-Timezone daha önce önerilmiş olsa da, Moment ekibi artık kullanıcıların yeni geliştirme için Luxon'u seçmesini tercih ediyor.

Durdurulan Kütüphaneler

Bu kütüphaneler resmi olarak sonlandırılmıştır ve artık kullanılmamalıdır.

Gelecek Öneriler

TC39 Zamansal Önerisi amaçları JavaScript dili kendisinde tarihleri ve saatleri ile çalışmak için standart nesneler yeni bir dizi sağlamaktır. Bu, zaman dilimine duyarlı bir nesne için destek içerir.


1
Temsil edilen zaman damgaları saat diliminden bağımsız olduğu için lütfen "temsil" i "çıktı / ayrıştırma" olarak değiştirin
Bergi

1
@Bergi - Bunu tekrar düşündüm ve sana katılıyorum. Cevabımı buna göre güncelledi.
Matt Johnson-Pint

1
Firebug'un konsolunda Bunu yaptığınızda: var date_time = new Date(), değeri date_time(AEST benim için) 'dir Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}ve bu nedenle göründüğünden olan bir zaman dilimini saklanan. date_timeHerhangi bir saat dilimi olmadan yalnızca UTC saatinin olmasını nasıl sağlayabilirim ?
user1063287

Hmm, bu cevaba göre ( stackoverflow.com/a/8047885/1063287 ), bu: new Date().getTime();
user1063287

1
@ user1063287 — toString yöntemi, "yerel" saat diliminde bir tarih ve saat oluşturmak için ana makine saat dilimi farkını kullanır. Tarih nesnesinin kendisinin 1970-01-01T00: 00: 00Z den farkı olan bir zaman değeri vardır. UTC tarih ve saatini görmek için toISOString işlevini kullanın .
RobG

69

Matt Johnson'ın dediği gibi

Kullanımınızı modern web tarayıcılarıyla sınırlandırabiliyorsanız, artık özel bir kitaplık olmadan aşağıdakileri yapabilirsiniz:

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

Bu kapsamlı bir çözüm değildir, ancak yalnızca çıktı dönüşümü gerektiren birçok senaryo için çalışır (UTC veya yerel saatten belirli bir saat dilimine, ancak diğer yöne değil).

Dolayısıyla, tarayıcı bir tarih oluştururken IANA zaman dilimlerini okuyamıyorsa veya mevcut bir Date nesnesindeki zaman dilimlerini değiştirmek için herhangi bir yöntemi olsa da, etrafında bir kesmek var gibi görünüyor:

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


6
Bu cevabın neden daha fazla sevişmediğini merak ediyorum. Benim amacım için kesinlikle en iyi çözüm. Uygulamamda, tüm tarihler UTC'dir, ancak kullanıcı arayüzündeki açık IANA zaman dilimlerinde giriş veya çıkış olması gerekir. Bu amaçla bu çözümü kullanıyorum ve mükemmel çalışıyor. Basit, etkili, standart.
logidelic

2
@logidelic oldukça yeni bir gönderi. dürüst olmak gerekirse, toLocaleStringters etki elde etmek için kullanabileceğiniz bana çarptığında şaşırdım ve evet, bu ortak bilgi olmalı! bunun hakkında bir makale yaz ve benden bahset :-)
commonpike

1
Zaten GMT'de olmayan bir düğüm ortamı için yukarıdaki kodun var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); var invdate = new Date ($ {date.toLocaleString ('tr-US', {timeZone: ianatz})} GMT`);
Mike P.

5
Tarayıcıların toLocaleString çıktısını ayrıştırması gerekmez , bu nedenle hatalı bir öncül temel alır.
RobG

3
“... neden bu cevap daha fazla sevgi almıyor?” - çünkü Dategeri döndürdüğü nesne bir yalandır. Gösterilen örnek bile, dize çıktısında yerel makinenin saat dilimi bulunur. Bilgisayarımda, her iki sonuç da "GMT-0700 (Pasifik Yaz Saati)" demektedir, bu sadece birincisi için doğrudur (Toronto aslında Doğu Saati'ndedir.) Sadece dize çıktısının ötesinde, Datenesne içinde tutulan zaman damgası zaman içinde farklı bir noktaya kaydı. Bu olabilir yararlı bir teknik olabilir, ancak uyarılar bir sürü geliyor - öncelikle o Datenesne fonksiyonları normal çıktı olmayacaktır.
Matt Johnson-Pint

30

Bir saat dilimi uzaklığı belirtebilirsiniz new Date(), örneğin:

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

veya

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

Yana Datemağaza UTC zaman (yani getTimeUTC döner), javascript onları UTC içine zaman dönüştürür ve gibi şeyler aradığınızda toStringtarayıcının yerel saat dilimi içine UTC zaman dönüştürmek ve yani yerel saat dilimine, dizeyi dönecektir javascript kullanıyorum varsa UTC+8:

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

Ayrıca normal getHours/Minute/Secondyöntemi kullanabilirsiniz :

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

(Bu 8, zamanın yerel saatime dönüştürülmesinden sonra - UTC+8saat sayısının olduğu anlamına gelir 8.)


16
ISO 8601 genişletilmiş formatı dışında herhangi bir formatın ayrıştırılması uygulamaya bağlıdır ve güvenilmemelidir. Saat dilimi kısaltmaları için standart yoktur, örneğin "EST" 3 farklı bölgeden herhangi birini temsil edebilir.
RobG

1
Bu yorumdaki örnekler genellikle internet explorer'da çalışmaz. Bu yazının önceki yorumunda belirtildiği gibi, ISO 8601 önemlidir. ECMA-262 (Javascript 5.) baskı dili Spesifikasyonunu okuyarak onayladım.
Dakusan

12

Bu sorununuzu çözmelidir, lütfen düzeltmeler sunmaktan çekinmeyin. Bu yöntem aynı zamanda belirtilen tarih için yaz saati uygulamasını da hesaba katacaktır.

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

Saat dilimi ve yerel saat ile nasıl kullanılır:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)

Bu çözüm benim ihtiyacım için uygun. Gün ışığından nasıl faydalanabileceğimi tam olarak bilmiyordum.
Hossein Amin

Neden sadece tzDatedoğrudan geri dönmüyorsun ?
levi

8

Bunu yapmanın en desteklenen yolunu, bir üçüncü taraf kütüphanesi hakkında endişelenmeden getTimezoneOffset, uygun zaman damgasını hesaplamak veya zamanı güncellemek için kullanmak, sonra da gerekli tarih ve saati almak için normal yöntemleri kullanmaktı.

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

DÜZENLE

Daha önce UTCyanlış olan tarih dönüşümlerini gerçekleştirirken yöntemler kullanıyordum . Zamana ofset ekleyerek, yerel getişlevleri kullanmak istediğiniz sonuçları döndürür.


Nerede hesapladınız timezone_offset?
Anand Somani

1
Hata! Değişkenlerimden birini yanlış etiketledim. timezone_offsetolmalı offsetya da tam tersi olmalıdır. Doğru değişken adını göstermek için cevabımı düzenledim.
Shaun Cockerill

3

Birim testleri ile benzer bir sorunla karşılaştım (özellikle birim testleri anlık görüntüler oluşturmak için yerel olarak çalıştırıldığında ve sonra CI sunucusu anlık olarak karşılaştırmanın başarısız olmasına neden olan farklı bir saat diliminde çalışır). Ben alay bizim Dateve bazı destekleyici yöntemler böyle:

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

});


2

Yukarıdaki cevaplara dayanarak, uzun zaman dilimi dizesini üç harfli dizeye dönüştürmek için bu yerel bir astar kullanıyorum:

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

Bu, verilen tarihe bağlı olarak PDT veya PST verecektir. Salesforce (Aura / Lightning) üzerinde geliştirdiğim özel kullanım durumumda, kullanıcı saat dilimini arka uçtan uzun biçimde alabiliriz.


Zaman zaman bilgiç oynamayı sevdiğim için, 'America / Los_Angeles' bir saat dilimi değil, belirli bir ofset ve gün ışığından yararlanma kuralları ve bu değişikliklerin geçmişi ( IANA zaman dilimi veritabanına göre ) için "temsili bir konum" . ;-)
RobG 23:19

Haha, bu yorumu birden fazla cevaba
Shane

2

Ben onun 3 yıl çok geç biliyorum, ama belki başka birine yardımcı olabilir çünkü ben burada istediğini tam olarak aynı değil an-zaman dilimi kütüphanesi dışında böyle bir şey bulamadım.

Alman saat dilimi için benzer bir şey yaptım, gün ışığından yararlanma süresi ve 366 günün olduğu artık yıllar nedeniyle bu biraz karmaşık.

"isDaylightSavingTimeInGermany" fonksiyonu ile biraz çalışma gerektirebilirken, farklı saat dilimleri yaz saatinin farklı zamanlarında değişir.

neyse, bu sayfayı kontrol et: https://github.com/zerkotin/german-timezone-converter/wiki

ana yöntemler şunlardır: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Belgelemek için çaba gösterdim, bu yüzden kafa karıştırıcı olmayacak.


Bu bir yorum olmalı, bir cevap değil.
RobG

1

Deneyin: saat dilimi tarihinden itibaren, yerel olarak mevcut olanların yardımıyla beklenen tarihi çözer Intl.DateTimeFormat.

Bu yöntemi birkaç yıldır projelerimden birinde kullandım, ancak şimdi küçük işletim sistemi projesi olarak yayınlamaya karar verdim :)


1

Belki bu size yardımcı olur

/**
 * 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')
);


0

İyonik kullanıcılar için, cehennem vardı çünkü .toISOString()html şablonuyla kullanılmalı.

Bu, geçerli tarihi alır, ancak elbette seçilen bir tarih için önceki cevaplara eklenebilir.

Bunu kullanarak sabit var:

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

* 60000, CST olan UTC -6'yı gösterir, böylece TimeZone ne olursa olsun, sayı ve fark değiştirilebilir.


Bu soru soran kişinin aradığı cevap değildir. Tarihi belirli bir saat dilimine almak istiyordu.
Richard Vergis

@RichardVergis Soru, kullanıcının yerel saate geçmek istediğini açıkça belirtiyor, ancak hangi saat diliminde olduklarını belirtmiyor. Saat dilimimi örnek olarak belirttim ve kullanıcı örneğime göre açıkça okuyabilir, girebilirler onların saat dilimi uzaklığı.
Stephen Romero

-2

Aynı sorunla karşı karşıyaydı, bunu kullandım

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

U kontrol edebilirsiniz milisaniye dönecektir + 100 timzone İngiliz zaman intialize yardımcı olur Umut yardımcı olur!


3
(Bu yazı soruya kaliteli bir cevap vermiyor gibi görünüyor . Lütfen ya cevabınızı düzenleyin ya da sadece soruya yorum olarak gönderin).
sɐunıɔ ןɐ qɐp
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.