JSON Stringify, UTC nedeniyle tarihin saatini değiştirir


99

JavaScript'teki tarih nesnelerim, bulunduğum yer nedeniyle her zaman UTC +2 ile temsil edilir. Dolayısıyla bunun gibi

Mon Sep 28 10:00:00 UTC+0200 2009

Sorun, JSON.stringifyyukarıdaki tarihi şuna dönüştürüyor:

2009-09-28T08:00:00Z  (notice 2 hours missing i.e. 8 instead of 10)

İhtiyacım olan şey tarih ve saatin onurlandırılması ama değil, bu yüzden olmalı

2009-09-28T10:00:00Z  (this is how it should be)

Temelde şunu kullanıyorum:

var jsonData = JSON.stringify(jsonObject);

Değiştirici bir parametre geçirmeyi denedim (stringify'daki ikinci parametre) ancak sorun, değerin zaten işlenmiş olmasıdır.

Ayrıca toString()ve toUTCString()tarih nesnesini kullanmayı denedim , ancak bunlar da bana istediğimi vermiyor ..

Biri bana yardım edebilir mi?


17
2009-09-28T10:00:00Z zaman içinde aynı anı temsil etmez olarak Mon Sep 28 10:00:00 UTC+0200 2009. ZBir in ISO 8601 tarihinden UTC anlamına gelir ve UTC olarak 10:00 olduğu zaman içinde farklı bir anı +0200 10 saat için. Tarihin doğru saat dilimiyle serileştirilmesini istemek bir şey olabilir, ancak bizden onu tartışmasız, nesnel olarak yanlış bir temsil olarak serileştirmenize yardım etmemizi istiyorsunuz .
Mark Amery

1
Marks yorumuna eklemek için, çoğu durumda, tarih saatlerinizi UTC saati olarak saklamak en iyi uygulamadır, böylece kullanıcıları farklı saat dilimlerinde destekleyebilirsiniz
JoeCodeFrog

Yanıtlar:


69

Son zamanlarda aynı sorunla karşılaştım. Ve aşağıdaki kod kullanılarak çözüldü:

x = new Date();
let hoursDiff = x.getHours() - x.getTimezoneOffset() / 60;
let minutesDiff = (x.getHours() - x.getTimezoneOffset()) % 60;
x.setHours(hoursDiff);
x.setMinutes(minutesDiff);

evet ama bu, web sitesi ülkemde kullanılıyorsa, ABD gibi başka bir ülkede kullanılıyorsa - 2 olmazdı ...
mark smith

Açıkçası, bu değer hesaplanmalıdır.
Anatoliy

3
teşekkürler ... Aslında burada harika bir kitaplık buldum, blog.stevenlevithan.com/archives/date-time-format bunu yapmanız için gereken her şey (belki size yardımcı olabilir), yanlış geçiyorsunuz ve dönüştürmüyor. var bir şey = tarihFormat (myStartDate, "isoDateTime", false);
mark smith

12
Bu, kodunuzu saat dilimi dışında güvenli hale getirdiği için yanlıştır - tarihi tekrar okuduğunuzda saat dilimini düzeltmeniz gerekir.
olliej

5
Bu cevap yanlış. OP, "2009-09-28T08: 00: 00Z" ve "28 Eylül Pzt 10:00:00 UTC + 0200 2009" un tam olarak aynı an olduğunu ve saat dilimi farkını ayarlamanın aslında yanlış zaman.
RobG

41

JSON, Date.prototype.toISOStringyerel saati temsil etmeyen işlevi kullanır - zamanı değiştirilmemiş UTC'de temsil eder - tarih çıktınıza bakarsanız UTC + 2 saatte olduğunuzu görebilirsiniz, bu nedenle JSON dizesi iki saat değişir, ancak bu aynı zamanın birden çok zaman diliminde doğru şekilde temsil edilmesine izin veriyorsa.


2
Bunu hiç düşünmedim ama haklısın. Çözüm şudur: Prototiplemeyi kullanarak tercih ettiğim herhangi bir formatı belirleyebilirim.
2013


10

İşte başka bir cevap (ve şahsen bunun daha uygun olduğunu düşünüyorum)

var currentDate = new Date(); 
currentDate = JSON.stringify(currentDate);

// Now currentDate is in a different format... oh gosh what do we do...

currentDate = new Date(JSON.parse(currentDate));

// Now currentDate is back to its original form :)

@Rohaan bunu belirttiğiniz için teşekkürler ama sorudaki etiketler JavaScript'ten bahsediyor.
Ağustos

7

date.toJSON (), UTC-Date'i formatlanmış bir String'e yazdırır (Yani, JSON formatına dönüştürdüğünde bununla ofseti ekler).

date = new Date();
new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();

6
Genellikle kodunuzun ne yaptığına dair bir açıklama eklemek iyi bir fikirdir. Bu, yeni geliştiricilerin kodun nasıl çalıştığını anlamasına olanak tanır.
Caleb Kleveter

Cevaptaki kodun neden çalışması gerektiğini açıklayabilir misiniz?
Cristik

Bu benim için de çalışıyor, ama bunu nasıl yaptığını açıklayabilir misin?
Deven Patil

Bu kodun ikinci satırını açıklamaya çalışacağım .. date.getTime()zamanı milisaniye cinsinden döndürür, bu yüzden ikinci işleneni de milisaniyeye çevirmeliyiz. Yana date.getTimezoneOffset()döner dakika içinde ofset, biz 1 dakika 60000milliseconds = çarpın o 60000, çünkü. Dolayısıyla, geçerli saatten ofseti çıkararak saati UTC olarak elde ederiz.
Maksim Pavlov

4

yerel saatle biçimlendirmek için moment.js'yi kullanabilirsiniz :

Date.prototype.toISOString = function () {
    return moment(this).format("YYYY-MM-DDTHH:mm:ss");
};

ortak sınıf Tarihinin üzerine yazmayın. Bazı harici modüller kullanırsanız, uygulamanızı kolayca bozabilir.
Viacheslav Dobromyslov

3

Biraz geç kaldım, ancak Prototip kullanarak bir Tarih kullanılması durumunda her zaman toJson işlevinin üzerine yazabilirsiniz:

Date.prototype.toJSON = function(){
    return Util.getDateTimeString(this);
};

Benim durumumda, Util.getDateTimeString (this) şuna benzer bir dize döndürür: "2017-01-19T00: 00: 00Z"


2
Tarayıcı globallerinin üzerine yazmanın, katıştırdığınız üçüncü taraf kitaplıklarını bozabileceğini ve büyük bir kalıp karşıtı olduğunu unutmayın. Bunu asla üretimde yapmayın.
jakub.g

3

JSON.stringifySaat dilimlerini göz ardı etmeye zorlamak için kullanıma hazır çözüm :

  • Saf javascript (Anatoliy cevabına göre):

// Before: JSON.stringify apply timezone offset
const date =  new Date();
let string = JSON.stringify(date);
console.log(string);

// After: JSON.stringify keeps date as-is!
Date.prototype.toJSON = function(){
    const hoursDiff = this.getHours() - this.getTimezoneOffset() / 60;
    this.setHours(hoursDiff);
    return this.toISOString();
};
string = JSON.stringify(date);
console.log(string);

An + an-saat dilimi kitaplıklarını kullanma:

const date =  new Date();
let string = JSON.stringify(date);
console.log(string);

Date.prototype.toJSON = function(){
    return moment(this).format("YYYY-MM-DDTHH:mm:ss:ms");;
};
string = JSON.stringify(date);
console.log(string);
<html>
  <header>
    <script src="https://momentjs.com/downloads/moment.min.js"></script>
    <script src="https://momentjs.com/downloads/moment-timezone-with-data-10-year-range.min.js"></script>
</header>
</html>


2

Genellikle tarihlerin her kullanıcıya kendi yerel saatinde sunulmasını istersiniz.

bu yüzden GMT (UTC) kullanıyoruz.

Yerel saat dizesini almak için Date.parse (jsondatestring) kullanın,

yerel saatinizin her ziyaretçiye gösterilmesini istemediğiniz sürece .

Bu durumda Anatoly'nin yöntemini kullanın.


2

moment.jsKitaplığı (saat dilimi olmayan sürüm) kullanarak bu sorunu çözdüm.

var newMinDate = moment(datePicker.selectedDates[0]);
var newMaxDate = moment(datePicker.selectedDates[1]);

// Define the data to ask the server for
var dataToGet = {"ArduinoDeviceIdentifier":"Temperatures",
                "StartDate":newMinDate.format('YYYY-MM-DD HH:mm'),
                "EndDate":newMaxDate.format('YYYY-MM-DD HH:mm')
};

alert(JSON.stringify(dataToGet));

flatpickr.min.jsKütüphaneyi kullanıyordum . Oluşturulan sonuçtaki JSON nesnesinin saati, sağlanan yerel saatle, ancak tarih seçiciyle eşleşir.


2

Sadece ABD'nin doğu yakasında çalıştıkları ve tarihleri ​​UTC'de saklamadıkları eski şeylerle biraz çalışıyorum, hepsi EST. Tarayıcıdaki kullanıcı girişine göre tarihlere göre filtrelemem gerekiyor, bu yüzden tarihi JSON formatında yerel saatte geçirmeliyim.

Sadece zaten yayınlanan bu çözümü detaylandırmak için - bunu kullanıyorum:

// Could be picked by user in date picker - local JS date
date = new Date();

// Create new Date from milliseconds of user input date (date.getTime() returns milliseconds)
// Subtract milliseconds that will be offset by toJSON before calling it
new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();

Yani anladığım kadarıyla bu devam edecek ve saat dilimi farkına dayalı olarak başlangıç ​​tarihinden milisaniye cinsinden (dolayısıyla 60000) çıkarılacak (dakika döndürür) - zamanın eklenmesi beklentisiyle JSON () eklenecektir.


2

JavaScript normalde yerel saat dilimini UTC'ye çevirir.

date = new Date();
date.setMinutes(date.getMinutes()-date.getTimezoneOffset())
JSON.stringify(date)

1

Burada gerçekten düzgün ve basit bir şey var (en azından öyle olduğuna inanıyorum :)) ve tarihin klonlanmasını veya toJSON gibi tarayıcının yerel işlevlerinden herhangi birinin aşırı yüklenmesini gerektirmiyor (referans: JSON, bir javascript tarihini nasıl dizgeleştirir ve saat dilimini korur , courty Shawson)

JSON.stringify'a bir ikame işlevi iletin, bu da her şeyi kalbinizin içeriğine göre düzenler !!! Bu şekilde, saat ve dakika farkları veya başka herhangi bir işlem yapmanız gerekmez.

Ara sonuçları görmek için console.log'ları koydum, böylece neler olduğu ve özyinelemenin nasıl çalıştığı açık. Bu, dikkate değer bir şeyi ortaya çıkarır: değiştiriciye değer parametresi zaten ISO tarih formatına dönüştürülmüştür :). Orijinal verilerle çalışmak için bu [tuşu] kullanın.

var replacer = function(key, value)
{
    var returnVal = value;
    if(this[key] instanceof Date)
    {
        console.log("replacer called with key - ", key, " value - ", value, this[key]); 

        returnVal = this[key].toString();

        /* Above line does not strictly speaking clone the date as in the cloned object 
         * it is a string in same format as the original but not a Date object. I tried 
         * multiple things but was unable to cause a Date object being created in the 
         * clone. 
         * Please Heeeeelp someone here!

        returnVal = new Date(JSON.parse(JSON.stringify(this[key])));   //OR
        returnVal = new Date(this[key]);   //OR
        returnVal = this[key];   //careful, returning original obj so may have potential side effect

*/
    }
    console.log("returning value: ", returnVal);

    /* if undefined is returned, the key is not at all added to the new object(i.e. clone), 
     * so return null. null !== undefined but both are falsy and can be used as such*/
    return this[key] === undefined ? null : returnVal;
};

ab = {prop1: "p1", prop2: [1, "str2", {p1: "p1inner", p2: undefined, p3: null, p4date: new Date()}]};
var abstr = JSON.stringify(ab, replacer);
var abcloned = JSON.parse(abstr);
console.log("ab is: ", ab);
console.log("abcloned is: ", abcloned);

/* abcloned is:
 * {
  "prop1": "p1",
  "prop2": [
    1,
    "str2",
    {
      "p1": "p1inner",
      "p2": null,
      "p3": null,
      "p4date": "Tue Jun 11 2019 18:47:50 GMT+0530 (India Standard Time)"
    }
  ]
}
Note p4date is string not Date object but format and timezone are completely preserved.
*/

0

Her şey, sunucu arka ucunuzun saat diliminden bağımsız olup olmadığına bağlıdır. Değilse, sunucunun saat diliminin istemciyle aynı olduğunu varsaymanız veya istemcinin saat dilimi hakkındaki bilgileri aktarmanız ve bunu da hesaplamalara dahil etmeniz gerekir.

PostgreSQL arka uç tabanlı bir örnek:

select '2009-09-28T08:00:00Z'::timestamp -> '2009-09-28 08:00:00' (wrong for 10am)
select '2009-09-28T08:00:00Z'::timestamptz -> '2009-09-28 10:00:00+02'
select '2009-09-28T08:00:00Z'::timestamptz::timestamp -> '2009-09-28 10:00:00'

Sonuncusu, zaman dilimi mantığını doğru şekilde uygulamak istemiyorsanız, muhtemelen veritabanında kullanmak istediğiniz şeydir.


0

Bunun yerine, her zaman doğru tarih ve saati veren biçim işlevini toJSONkullanabilirsiniz +GMT

Bu, en sağlam görüntüleme seçeneğidir. Bir dizi jeton alır ve bunları karşılık gelen değerleriyle değiştirir.


0

Bunu açısal 8'de denedim:

  1. Model oluştur:

    export class Model { YourDate: string | Date; }
    
  2. senin bileşeninde

    model : Model;
    model.YourDate = new Date();
    
  3. Kaydetmek için API'nize Tarih gönderin

  4. Verilerinizi API'den yüklerken şunu yapacaksınız:

    model.YourDate = new Date(model.YourDate+"Z");

Tarihinizi saat diliminizle doğru bir şekilde alacaksınız.


0

Bu durumda tarihi UNIX zaman damgasına dönüştürmeniz gerektiğini düşünüyorum.

timestamp = testDate.getTime();
strJson = JSON.stringify(timestamp);

Bundan sonra onu bir tarih nesnesi oluşturmak ve biçimlendirmek için yeniden kullanabilirsiniz. Javascript ve toLocaleDateString( https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/toLocaleDateString ) ile örnek

newDateObject = new Date(JSON.parse(strJson));
newDateObject = newDateObject.toLocalDateStrin([
  "fr-FR",
]);

AJAX kullanmak için stringify kullanırsanız, artık kullanışlı değildir. Sadece zaman damgası göndermeniz ve komut dosyanıza almanız gerekir:

$newDateObject = new \DateTime();
$newDateObject->setTimestamp(round($timestamp/1000));

Unutmayın getTime()milisaniye cinsinden zaman ve PHP işlevi dönecektir setTimestampsaniyede take zaman. Bu yüzden 1000'e bölmeniz gerekiyor ve round.

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.