JSON Stringify change l'heure de la date en raison de l'UTC

98

Mes objets de date en JavaScript sont toujours représentés par UTC +2 en raison de l'endroit où je me trouve. Par conséquent comme ça

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

Le problème est de faire une JSON.stringifyconversion de la date ci-dessus en

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

Ce dont j'ai besoin, c'est que la date et l'heure soient honorées, mais ce n'est pas le cas, donc cela devrait être

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

Fondamentalement, j'utilise ceci:

var jsonData = JSON.stringify(jsonObject);

J'ai essayé de passer un paramètre de remplacement (deuxième paramètre sur stringify) mais le problème est que la valeur a déjà été traitée.

J'ai aussi essayé d'utiliser toString()et toUTCString()sur l'objet date, mais cela ne me donne pas non plus ce que je veux ..

Quelqu'un peut-il m'aider?

Mark Smith
la source
17
2009-09-28T10:00:00Z ne représente pas le même moment dans le temps que Mon Sep 28 10:00:00 UTC+0200 2009. Le Zdans une date ISO 8601 signifie UTC, et 10 heures en UTC est un moment différent de 10 heures en +0200. Ce serait une chose de vouloir que la date soit sérialisée avec le bon fuseau horaire, mais vous nous demandez de vous aider à la sérialiser en une représentation qui est sans équivoque, objectivement fausse .
Mark Amery
1
Pour ajouter au commentaire Marks, dans la plupart des cas, il est
recommandé

Réponses:

67

Récemment, j'ai rencontré le même problème. Et il a été résolu en utilisant le code suivant:

x = new Date();
let hoursDiff = x.getHours() - x.getTimezoneOffset() / 60;
let minutesDiff = (x.getHours() - x.getTimezoneOffset()) % 60;
x.setHours(hoursDiff);
x.setMinutes(minutesDiff);
Anatoliy
la source
oui mais c'est si le site Web est utilisé dans mon pays, s'il est utilisé dans un autre pays comme les États-Unis - ce ne serait pas 2 ...
mark smith
Évidemment, cette valeur doit être calculée.
Anatoliy
3
merci ... J'ai en fait trouvé une excellente bibliothèque ici, blog.stevenlevithan.com/archives/date-time-format tout ce dont vous avez besoin pour faire cela (peut-être que cela vous aidera), vous passez false et cela ne convertit pas. var quelque chose = dateFormat (myStartDate, "isoDateTime", false);
mark smith
11
ceci est incorrect car cela rend votre code non-fuseau horaire sûr - vous devriez corriger le fuseau horaire lorsque vous
relisez
4
Cette réponse est fausse. L'OP ne se rend pas compte que "2009-09-28T08: 00: 00Z" et "Mon Sep 28 10:00:00 UTC + 0200 2009" sont exactement le même moment dans le temps et que l'ajustement du décalage du fuseau horaire crée en fait le mauvais moment.
RobG
41

JSON utilise la Date.prototype.toISOStringfonction qui ne représente pas l'heure locale - elle représente l'heure en UTC non modifiée - si vous regardez votre sortie de date, vous pouvez voir que vous êtes à UTC + 2 heures, c'est pourquoi la chaîne JSON change de deux heures, mais si cela permet à la même heure d'être représentée correctement sur plusieurs fuseaux horaires.

olliej
la source
2
Jamais pensé à ça, mais tu as raison. Voici la solution: je peux spécifier n'importe quel format que je préfère en utilisant le prototypage.
courses du
9

Voici une autre réponse (et personnellement je pense que c'est plus approprié)

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 :)
août
la source
@Rohaan merci de l'avoir signalé, mais les balises sur la question mentionnent JavaScript.
août
6

date.toJSON () imprime la date UTC dans une chaîne formatée (ajoute donc le décalage avec elle lors de la conversion au format JSON).

date = new Date();
new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();
ali-myousefi
la source
6
Il est généralement judicieux d'ajouter une explication sur ce que fait votre code. Cela permet aux nouveaux développeurs de comprendre le fonctionnement du code.
Caleb Kleveter
Pouvez-vous expliquer pourquoi le code de la réponse devrait fonctionner?
Cristik
Cela fonctionne pour moi aussi, mais pouvez-vous expliquer comment vous avez fait cela?
Deven Patil
Je vais essayer d'expliquer la deuxième ligne de ce code .. date.getTime()renvoie le temps en millisecondes, donc nous devrions également convertir le deuxième opérande en millisecondes. Puisque les date.getTimezoneOffset()retours sont décalés en minutes, nous le multiplions par 60000, car 1 minute = 60000 millisecondes. Donc, en soustrayant le décalage de l'heure actuelle, nous obtenons l'heure en UTC.
Maksim Pavlov
4

vous pouvez utiliser moment.js pour formater avec l'heure locale:

Date.prototype.toISOString = function () {
    return moment(this).format("YYYY-MM-DDTHH:mm:ss");
};
Karim fereidooni
la source
ne pas écraser la classe commune Date. Il peut facilement casser votre application si vous utilisez des modules externes.
Viacheslav Dobromyslov le
3

Je suis un peu en retard mais vous pouvez toujours écraser la fonction toJson dans le cas d'une Date utilisant Prototype comme ceci:

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

Dans mon cas, Util.getDateTimeString (this) renvoie une chaîne comme celle-ci: "2017-01-19T00: 00: 00Z"

charles-emile commence lavigne
la source
2
Notez que l'écrasement des globaux de navigateur peut casser les bibliothèques tierces que vous incorporez et constitue un grand anti-modèle. Ne faites jamais cela en production.
jakub.g
2

Habituellement, vous voulez que les dates soient présentées à chaque utilisateur dans sa propre heure locale-

c'est pourquoi nous utilisons GMT (UTC).

Utilisez Date.parse (jsondatestring) pour obtenir la chaîne d'heure locale,

sauf si vous souhaitez que votre heure locale soit affichée à chaque visiteur.

Dans ce cas, utilisez la méthode d'Anatoly.

Kennebec
la source
2

Contourné ce problème en utilisant la moment.jsbibliothèque (la version sans fuseau horaire).

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

J'utilisais la flatpickr.min.jsbibliothèque. L'heure de l'objet JSON résultant créé correspond à l'heure locale fournie mais au sélecteur de date.

Paul
la source
2

Solution JSON.stringifyprête à l'emploi pour forcer l' ignorance des fuseaux horaires:

  • JavaScript pur (basé sur la réponse 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);

Utilisation de la bibliothèque 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>

Benjamin Caure
la source
2

Je rencontre cela un peu en travaillant avec des trucs hérités où ils ne fonctionnent que sur la côte est des États-Unis et ne stockent pas les dates en UTC, tout est EST. Je dois filtrer sur les dates en fonction de l'entrée de l'utilisateur dans le navigateur, je dois donc transmettre la date en heure locale au format JSON.

Juste pour élaborer sur cette solution déjà publiée - c'est ce que j'utilise:

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

Donc, je crois comprendre que cela va aller de l'avant et soustraire le temps (en millisecondes (donc 60000) de la date de départ en fonction du décalage du fuseau horaire (renvoie les minutes) - en prévision de l'ajout de temps à JSON () va ajouter.

MrRobboto
la source
1

Voici quelque chose de vraiment soigné et simple (du moins je crois que oui :)) et ne nécessite aucune manipulation de la date pour être cloné ou surcharger l'une des fonctions natives du navigateur comme toJSON (référence: Comment JSON stringifier une date javascript et préserver le fuseau horaire , courtise Shawson)

Passez une fonction de remplacement à JSON.stringify qui stringifie les choses à votre guise !!! De cette façon, vous n'avez pas à faire des différences d'heures et de minutes ou toute autre manipulation.

J'ai mis dans console.logs pour voir les résultats intermédiaires afin que ce qui se passe et comment fonctionne la récursivité soit clair. Cela révèle quelque chose qui mérite d'être remarqué: le paramètre de valeur à remplacer est déjà converti au format de date ISO :). Utilisez cette [touche] pour travailler avec les données originales.

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
la source
1

JavaScript convertit normalement le fuseau horaire local en UTC.

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

Tout se résume à savoir si le backend de votre serveur est indépendant du fuseau horaire ou non. Si ce n'est pas le cas, vous devez supposer que le fuseau horaire du serveur est le même que celui du client, ou transférer des informations sur le fuseau horaire du client et les inclure également dans les calculs.

un exemple basé sur le 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'

Le dernier est probablement ce que vous souhaitez utiliser dans la base de données, si vous ne souhaitez pas implémenter correctement la logique de fuseau horaire.

Korc
la source
0

Au lieu de toJSON, vous pouvez utiliser la fonction de format qui donne toujours la date et l'heure correctes +GMT

C'est l'option d'affichage la plus robuste. Il prend une chaîne de jetons et les remplace par leurs valeurs correspondantes.

Asqan
la source
0

J'ai essayé ceci en angulaire 8:

  1. créer un modèle:

    export class Model { YourDate: string | Date; }
  2. dans votre composant

    model : Model;
    model.YourDate = new Date();
  3. envoyer la date à votre API pour la sauvegarder

  4. Lors du chargement de vos données depuis l'API, vous ferez ceci:

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

vous obtiendrez votre date correctement avec votre fuseau horaire.

Mahmoud AbdElzaher
la source