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.