JSON Stringify cambia l'ora della data a causa dell'UTC


98

I miei oggetti data in JavaScript sono sempre rappresentati da UTC +2 a causa di dove mi trovo. Quindi così

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

Il problema sta facendo una JSON.stringifyconversione della data sopra in

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

Quello di cui ho bisogno è che la data e l'ora siano rispettate, ma non lo è, quindi dovrebbe essere

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

Fondamentalmente lo uso:

var jsonData = JSON.stringify(jsonObject);

Ho provato a passare un parametro di sostituzione (secondo parametro su stringify) ma il problema è che il valore è già stato elaborato.

Ho anche provato a usare toString()e toUTCString()sull'oggetto data, ma nemmeno questi mi danno quello che voglio ..

Qualcuno può aiutarmi?


17
2009-09-28T10:00:00Z non rappresenta lo stesso momento temporale di Mon Sep 28 10:00:00 UTC+0200 2009. La data Zin una ISO 8601 significa UTC, e le 10 in UTC sono un momento diverso rispetto alle 10 in +0200. Una cosa sarebbe voler serializzare la data con il fuso orario corretto, ma ci stai chiedendo di aiutarti a serializzarla in una rappresentazione che è inequivocabilmente, oggettivamente sbagliata .
— Mark Amery

1
Per aggiungere un commento a Marks, nella maggior parte dei casi è consigliabile memorizzare i datetimes come ora UTC, in modo da poter supportare gli utenti in diversi fusi orari
— JoeCodeFrog

Risposte:


67

Recentemente ho riscontrato lo stesso problema. Ed è stato risolto utilizzando il seguente codice:

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

sì, ma questo è se il sito web viene utilizzato nel mio paese, se viene utilizzato in un altro paese come gli Stati Uniti - non sarebbe 2 ...
— mark smith

Ovviamente questo valore dovrebbe essere calcolato.
— Anatoliy

3
grazie ... in realtà ho trovato un'ottima libreria qui, blog.stevenlevithan.com/archives/date-time-format tutto ciò che ti serve per fare questo (forse ti aiuterà), passi falso e non converte. var qualcosa = dateFormat (myStartDate, "isoDateTime", false);
— Mark Smith,

11
questo non è corretto in quanto rende il tuo codice non sicuro per il fuso orario - dovresti correggere il fuso orario quando leggi la data di nuovo.
— olliej

4
Questa risposta è sbagliata. L'OP non si rende conto che "2009-09-28T08: 00: 00Z" e "Mon Sep 28 10:00:00 UTC + 0200 2009" sono esattamente lo stesso momento nel tempo e che la regolazione della differenza di fuso orario sta effettivamente creando il momento sbagliato.
— RobG

41

JSON utilizza la Date.prototype.toISOStringfunzione che non rappresenta l'ora locale - rappresenta l'ora in UTC non modificato - se guardi l'output della data puoi vedere che sei a UTC + 2 ore, motivo per cui la stringa JSON cambia di due ore, ma se questo consente di rappresentare correttamente la stessa ora in più fusi orari.


2
Non ci ho mai pensato, ma hai ragione. Questa è la soluzione: posso specificare qualsiasi formato preferisco utilizzando la prototipazione.
— gare il


9

Ecco un'altra risposta (e personalmente penso sia più appropriata)

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 grazie per averlo sottolineato, ma i tag sulla domanda menzionano JavaScript.
— agosto

6

date.toJSON () stampa la data UTC in una stringa formattata (quindi aggiunge l'offset con essa quando la converte in formato JSON).

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

6
Di solito è una buona idea aggiungere una spiegazione su ciò che fa il tuo codice. Ciò consente agli sviluppatori più recenti di capire come funziona il codice.
— Caleb Kleveter

Puoi spiegare perché il codice nella risposta dovrebbe funzionare?
— Cristik

Funziona anche per me, ma puoi spiegarci come hai fatto?
— Deven Patil

Cercherò di spiegare la seconda riga di questo codice .. date.getTime()restituisce il tempo in millesecondi, quindi dovremmo convertire anche il secondo operando in millisecondi. Poiché i date.getTimezoneOffset()rendimenti si scostano in minuti, moltiplichiamo 60000, perché 1 minuto = 60000 millisecondi. Quindi sottraendo offset dall'ora corrente otteniamo l'ora in UTC.
— Maksim Pavlov

4

puoi usare moment.js per formattare con l'ora locale:

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

non sovrascrivere la data della classe comune. Può facilmente interrompere l'applicazione se si utilizzano alcuni moduli esterni.
— Viacheslav Dobromyslov

3

Sono un po 'in ritardo ma puoi sempre sovrascrivere la funzione toJson in caso di una data utilizzando Prototype in questo modo:

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

Nel mio caso, Util.getDateTimeString (this) restituisce una stringa come questa: "2017-01-19T00: 00: 00Z"


2
Tieni presente che la sovrascrittura delle variabili globali del browser può danneggiare le librerie di terze parti che incorpori ed è un grande anti-pattern. Non farlo mai in produzione.
— jakub.g

2

Di solito vuoi che le date siano presentate a ciascun utente nella sua ora locale-

ecco perché usiamo GMT (UTC).

Usa Date.parse (jsondatestring) per ottenere la stringa dell'ora locale,

a meno che tu non voglia mostrare l'ora locale a ciascun visitatore.

In tal caso, usa il metodo di Anatoly.


2

Ho aggirato questo problema utilizzando la moment.jslibreria (la versione senza fuso orario).

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

Stavo usando la flatpickr.min.jsbiblioteca. L'ora dell'oggetto JSON risultante creato corrisponde all'ora locale fornita ma al selettore di data.


2

Soluzione JSON.stringifypronta all'uso per forzare l' ignoranza dei fusi orari:

  • JavaScript puro (basato sulla risposta di 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);

Utilizzando la libreria moment.js:

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

Mi imbatto in questo un po 'lavorando con roba legacy in cui lavorano solo sulla costa orientale degli Stati Uniti e non memorizzano le date in UTC, è tutto EST. Devo filtrare le date in base all'input dell'utente nel browser, quindi devo passare la data nell'ora locale in formato JSON.

Giusto per elaborare questa soluzione già pubblicata, questo è quello che uso:

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

Quindi la mia comprensione è che questo andrà avanti e sottrarrà il tempo (in millisecondi (quindi 60000) dalla data di inizio in base allo scostamento del fuso orario (restituisce i minuti) - in previsione dell'aggiunta di tempo aJSON () verrà aggiunto.


1

Ecco qualcosa di veramente pulito e semplice (almeno credo di sì :)) e non richiede la manipolazione della data da clonare o il sovraccarico di nessuna delle funzioni native del browser come toJSON (riferimento: come stringere JSON una data javascript e preservare il fuso orario , courty Shawson)

Passa una funzione di sostituzione a JSON.stringify che restringe le cose al contenuto del tuo cuore !!! In questo modo non devi fare differenze di ore e minuti o altre manipolazioni.

Ho inserito console.logs per vedere i risultati intermedi in modo che sia chiaro cosa sta succedendo e come funziona la ricorsione. Ciò rivela qualcosa di degno di nota: value param to replacer è già convertito in formato data ISO :). Usa questo [tasto] per lavorare con i dati originali.

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.
*/

1

JavaScript normalmente converte il fuso orario locale in UTC.

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

0

Tutto si riduce a se il tuo server backend è indipendente dal fuso orario o meno. In caso contrario, è necessario presumere che il fuso orario del server sia lo stesso del client o trasferire le informazioni sul fuso orario del client e includerlo anche nei calcoli.

un esempio basato sul 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'

L'ultimo è probabilmente quello che vuoi usare nel database, se non vuoi implementare correttamente la logica del fuso orario.


0

Invece di toJSON, puoi usare la funzione di formattazione che fornisce sempre la data e l'ora corrette +GMT

Questa è l'opzione di visualizzazione più robusta. Prende una stringa di gettoni e li sostituisce con i valori corrispondenti.


0

Ho provato questo in angolare 8:

  1. creare modello:

    export class Model { YourDate: string | Date; }
  2. nel tuo componente

    model : Model;
    model.YourDate = new Date();
  3. invia la data alla tua API per il salvataggio

  4. Quando carichi i tuoi dati dall'API, farai questo:

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

otterrai la tua data correttamente con il tuo fuso orario.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.