JSON Stringify mengubah waktu tanggal karena UTC

99

Objek tanggal saya di JavaScript selalu diwakili oleh UTC +2 karena di mana saya berada. Makanya begini

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

Masalahnya adalah melakukan JSON.stringifykonversi tanggal di atas menjadi

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

Yang saya butuhkan adalah tanggal dan waktu untuk dihormati tetapi sebenarnya tidak, maka seharusnya begitu

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

Pada dasarnya saya menggunakan ini:

var jsonData = JSON.stringify(jsonObject);

Saya mencoba melewatkan parameter pengganti (parameter kedua pada stringify) tetapi masalahnya adalah nilainya sudah diproses.

Saya juga mencoba menggunakan toString()dan toUTCString()pada objek tanggal, tetapi ini juga tidak memberikan apa yang saya inginkan ..

Ada yang bisa bantu saya?

tandai smith
sumber
17
2009-09-28T10:00:00Z tidak mewakili saat yang sama dalam waktu sebagai Mon Sep 28 10:00:00 UTC+0200 2009. Tanggal Zdalam ISO 8601 berarti UTC, dan jam 10 di UTC adalah momen waktu yang berbeda dengan jam 10 di +0200. Menginginkan tanggal untuk diserialkan dengan zona waktu yang tepat adalah satu hal, tetapi Anda meminta kami membantu Anda membuat serialisasi ke representasi yang benar-benar salah secara obyektif .
Mark Amery
1
Untuk menambahkan ke komentar Marks, dalam banyak kasus adalah praktik terbaik untuk menyimpan waktu Anda sebagai waktu UTC, sehingga Anda dapat mendukung pengguna di zona waktu yang berbeda
JoeCodeFrog

Jawaban:

69

Baru-baru ini saya mengalami masalah yang sama. Dan itu diselesaikan menggunakan kode berikut:

x = new Date();
let hoursDiff = x.getHours() - x.getTimezoneOffset() / 60;
let minutesDiff = (x.getHours() - x.getTimezoneOffset()) % 60;
x.setHours(hoursDiff);
x.setMinutes(minutesDiff);
Anatoliy
sumber
ya tapi ini jika situs web digunakan di negara saya, jika digunakan di negara lain seperti AS - tidak akan menjadi 2 ...
tandai smith
Jelas, nilai ini harus dihitung.
Anatoliy
3
terima kasih ... Saya benar-benar menemukan perpustakaan yang bagus di sini, blog.stevenlevithan.com/archives/date-time-format semua yang Anda butuhkan untuk melakukan ini (mungkin ini akan membantu Anda), Anda lulus palsu dan tidak dikonversi. var something = dateFormat (myStartDate, "isoDateTime", false);
tandai smith
11
ini tidak benar karena membuat kode Anda tidak aman untuk zona waktu - Anda harus mengoreksi zona waktu saat Anda membaca tanggal sebelumnya.
olliej
4
Jawaban ini salah. OP tidak menyadari bahwa "2009-09-28T08: 00: 00Z" dan "Sen 28 Sep 10:00:00 UTC + 0200 2009" adalah momen yang persis sama dan penyesuaian zona waktu sebenarnya menciptakan waktu yang salah.
RobG
41

JSON menggunakan Date.prototype.toISOStringfungsi yang tidak mewakili waktu lokal - ini mewakili waktu dalam UTC yang tidak diubah - jika Anda melihat keluaran tanggal Anda, Anda dapat melihat bahwa Anda berada di UTC + 2 jam, itulah sebabnya string JSON berubah dua jam, tetapi jika ini memungkinkan waktu yang sama direpresentasikan dengan benar di beberapa zona waktu.

olliej.dll
sumber
2
Tidak pernah memikirkan ini, tetapi Anda benar. Inilah solusinya: Saya dapat menentukan format apa pun yang saya suka dengan menggunakan prototipe.
balapan
9

Ini jawaban lain (dan secara pribadi menurut saya itu lebih tepat)

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 :)
agustus
sumber
@Rohaan terima kasih telah menunjukkan hal itu tetapi tag pada pertanyaan menyebutkan JavaScript.
Agustus
6

date.toJSON () mencetak UTC-Date menjadi String yang diformat (Jadi menambahkan offset dengannya saat mengubahnya menjadi format JSON).

date = new Date();
new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();
ali-myousefi
sumber
6
Biasanya merupakan ide yang baik untuk menambahkan penjelasan tentang fungsi kode Anda. Ini memungkinkan pengembang baru untuk memahami cara kerja kode.
Caleb Kleveter
Bisakah Anda menjelaskan mengapa kode dalam jawaban harus berfungsi?
Cristik
Ini Bekerja untuk saya juga, Tapi bisakah Anda menjelaskan bagaimana Anda melakukannya?
Deven Patil
Saya akan mencoba menjelaskan baris kedua kode ini .. date.getTime()mengembalikan waktu dalam milidetik, jadi kita harus mengonversi operan kedua menjadi milidetik juga. Karena date.getTimezoneOffset()pengembalian offset dalam beberapa menit, kami mengalikannya 60000, karena 1 menit = 60000milidetik. Jadi dengan mengurangi offset dari waktu saat ini kita mendapatkan waktu dalam UTC.
Maksim Pavlov
4

Anda dapat menggunakan moment.js untuk memformat dengan waktu lokal:

Date.prototype.toISOString = function () {
    return moment(this).format("YYYY-MM-DDTHH:mm:ss");
};
karim fereidooni
sumber
jangan menimpa Tanggal kelas umum. Ini dapat dengan mudah merusak aplikasi Anda jika menggunakan beberapa modul eksternal.
Viacheslav Dobromyslov
3

Saya agak terlambat tetapi Anda selalu dapat menimpa fungsi toJson jika Tanggal menggunakan Prototipe seperti ini:

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

Dalam kasus saya, Util.getDateTimeString (this) mengembalikan string seperti ini: "2017-01-19T00: 00: 00Z"

charles-emile mulai lavigne
sumber
2
Perhatikan bahwa menimpa global browser dapat merusak pustaka pihak ketiga yang Anda sematkan, dan merupakan anti-pola yang besar. Jangan pernah melakukan ini dalam produksi.
jakub.g
3

Solusi out-of-the-box untuk memaksa JSON.stringifymengabaikan zona waktu:

  • JavaScript murni (berdasarkan jawaban Anatoliy):

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

Menggunakan pustaka momen + zona waktu:

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>

Benjamin Caure
sumber
2

Biasanya Anda ingin tanggal disajikan kepada setiap pengguna dalam waktu lokalnya sendiri-

itulah mengapa kami menggunakan GMT (UTC).

Gunakan Date.parse (jsondatestring) untuk mendapatkan string waktu lokal,

kecuali Anda ingin waktu lokal Anda ditampilkan kepada setiap pengunjung.

Jika demikian, gunakan metode Anatoly.

kennebec
sumber
2

Atasi masalah ini dengan menggunakan moment.jspustaka (versi non-zona waktu).

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

Saya menggunakan flatpickr.min.jsperpustakaan. Waktu dari objek JSON yang dibuat cocok dengan waktu lokal yang disediakan, tetapi pemilih tanggal.

Paul
sumber
2

Saya mengalami ini sedikit bekerja dengan barang-barang warisan di mana mereka hanya bekerja di pantai timur AS dan tidak menyimpan tanggal di UTC, itu semua EST. Saya harus memfilter tanggal berdasarkan input pengguna di browser jadi harus meneruskan tanggal dalam waktu lokal dalam format JSON.

Hanya untuk menguraikan solusi ini yang sudah diposting - inilah yang saya gunakan:

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

Jadi pemahaman saya adalah ini akan melanjutkan dan mengurangi waktu (dalam milidetik (karenanya 60000) dari tanggal mulai berdasarkan offset zona waktu (menit kembali) - untuk mengantisipasi penambahan waktu toJSON () akan ditambahkan.

MrRobboto
sumber
1

Ini adalah sesuatu yang sangat rapi dan sederhana (setidaknya saya percaya begitu :)) dan tidak memerlukan manipulasi tanggal untuk dikloning atau membebani fungsi asli browser apa pun seperti toJSON (referensi: Cara JSON merangkai javascript Tanggal dan mempertahankan zona waktu , sopan Shawson)

Teruskan fungsi pengganti ke JSON.stringify yang merangkai barang sesuka hati !!! Dengan cara ini Anda tidak perlu melakukan perbedaan jam dan menit atau manipulasi lainnya.

Saya telah memasukkan console.logs untuk melihat hasil antara sehingga jelas apa yang terjadi dan bagaimana rekursi bekerja. Itu mengungkapkan sesuatu yang perlu diperhatikan: nilai param to replacer sudah dikonversi ke format tanggal ISO :). Gunakan [kunci] ini untuk bekerja dengan data asli.

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.
*/
Atul Lohiya
sumber
1

JavaScript biasanya mengubah zona waktu lokal menjadi UTC.

date = new Date();
date.setMinutes(date.getMinutes()-date.getTimezoneOffset())
JSON.stringify(date)
Sarath Ak
sumber
0

Semua intinya adalah apakah backend server Anda adalah zona waktu-agnostik atau tidak. Jika tidak, maka Anda perlu berasumsi bahwa zona waktu server sama dengan klien, atau mentransfer informasi tentang zona waktu klien dan memasukkannya juga ke dalam penghitungan.

contoh berbasis backend PostgreSQL:

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'

Yang terakhir mungkin adalah apa yang ingin Anda gunakan dalam database, jika Anda tidak ingin menerapkan logika zona waktu dengan benar.

korc
sumber
0

Sebagai gantinya toJSON, Anda dapat menggunakan fungsi format yang selalu memberikan tanggal dan waktu + yang benarGMT

Ini adalah opsi tampilan paling kuat. Ini mengambil string token dan menggantinya dengan nilai yang sesuai.

Asqan
sumber
0

Saya mencoba ini di sudut 8:

  1. buat Model:

    export class Model { YourDate: string | Date; }
  2. di komponen Anda

    model : Model;
    model.YourDate = new Date();
  3. kirim Tanggal ke API Anda untuk disimpan

  4. Saat memuat data Anda dari API, Anda akan membuat ini:

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

Anda akan mendapatkan tanggal Anda dengan benar dengan zona waktu Anda.

Mahmoud AbdElzaher
sumber