JSON Stringify zmienia czas i datę z powodu czasu UTC

99

Moje obiekty daty w JavaScript są zawsze reprezentowane przez UTC +2 ze względu na to, gdzie się znajduję. Stąd tak

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

Problem polega na tym, że dokonujesz JSON.stringifykonwersji powyższej daty na

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

To, czego potrzebuję, to uszanowanie daty i godziny, ale tak nie jest, więc powinno

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

Zasadniczo używam tego:

var jsonData = JSON.stringify(jsonObject);

Próbowałem przekazać parametr zastępczy (drugi parametr w stringify), ale problem polega na tym, że wartość została już przetworzona.

Próbowałem też użyć obiektu daty toString()i toUTCString()na nim, ale te też nie dają mi tego, czego chcę.

Czy ktoś może mi pomóc?

Mark Smith
źródło
17
2009-09-28T10:00:00Z nie przedstawia tego samego momentu w czasie co Mon Sep 28 10:00:00 UTC+0200 2009. Data Zwedług ISO 8601 oznacza UTC, a godzina 10 w UTC to inny moment w czasie do godziny 10 w +0200. To jedno, gdyby data była serializowana z odpowiednią strefą czasową, ale prosisz nas o pomoc w serializacji do reprezentacji, która jest jednoznacznie, obiektywnie błędna .
Mark Amery,
1
Aby dodać do komentarza Marksa, w większości przypadków najlepiej jest przechowywać swoje daty jako czas UTC, aby można było wspierać użytkowników w różnych
strefach

Odpowiedzi:

69

Ostatnio napotkałem ten sam problem. I został rozwiązany za pomocą następującego kodu:

x = new Date();
let hoursDiff = x.getHours() - x.getTimezoneOffset() / 60;
let minutesDiff = (x.getHours() - x.getTimezoneOffset()) % 60;
x.setHours(hoursDiff);
x.setMinutes(minutesDiff);
Anatolij
źródło
tak, ale dzieje się tak, jeśli strona internetowa jest używana w moim kraju, jeśli jest używana w innym kraju, takim jak USA - nie byłoby 2 ...
mark smith
Oczywiście wartość tę należy obliczyć.
Anatoliy
3
dzięki ... Właściwie znalazłem tutaj świetną bibliotekę, blog.stevenlevithan.com/archives/date-time-format wszystko, czego potrzebujesz, aby to zrobić (może ci to pomoże), zdajesz fałsz i nie konwertuje. var coś = dateFormat (myStartDate, "isoDateTime", false);
mark smith
11
jest to niepoprawne, ponieważ sprawia, że ​​Twój kod nie jest
strefą
4
Ta odpowiedź jest błędna. OP nie zdaje sobie sprawy, że „2009-09-28T08: 00: 00Z” i „Mon Sep 28 10:00:00 UTC + 0200 2009” to dokładnie ten sam moment i że tak naprawdę tworzy się korekta przesunięcia strefy czasowej zły czas.
RobG
41

JSON używa Date.prototype.toISOStringfunkcji, która nie reprezentuje czasu lokalnego - reprezentuje czas w niezmodyfikowanym UTC - jeśli spojrzysz na dane wyjściowe, zobaczysz, że jesteś w UTC + 2 godziny, dlatego ciąg JSON zmienia się o dwie godziny, ale jeśli pozwala to na prawidłowe przedstawienie tego samego czasu w wielu strefach czasowych.

olliej
źródło
2
Nigdy o tym nie myślałem, ale masz rację. Oto rozwiązanie: mogę określić dowolny format, który preferuję, korzystając z prototypowania.
wyścigi
9

Oto inna odpowiedź (i osobiście uważam, że jest bardziej odpowiednia)

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 :)
aug
źródło
@Rohaan dzięki za wskazanie tego, ale tagi w pytaniu wspominają o JavaScript.
sierpień
6

date.toJSON () wypisuje datę UTC do formatu String (więc dodaje do niej przesunięcie, gdy konwertuje ją na format JSON).

date = new Date();
new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();
ali-myousefi
źródło
6
Zwykle dobrym pomysłem jest dodanie wyjaśnienia, co robi Twój kod. Dzięki temu nowi programiści mogą zrozumieć, jak działa kod.
Caleb Kleveter
Czy możesz wyjaśnić, dlaczego kod w odpowiedzi powinien działać?
Cristik
U mnie to też działa, ale czy możesz wyjaśnić, jak to zrobiłeś?
Deven Patil
Spróbuję wyjaśnić drugą linię tego kodu .. date.getTime()zwraca czas w milisekundach, więc powinniśmy również przekonwertować drugi operand na milisekundy. Ponieważ date.getTimezoneOffset()zwraca przesunięcie w minutach, mnożymy to 60000, ponieważ 1 minuta = 60000 milisekund. Więc odejmując przesunięcie od aktualnego czasu otrzymujemy czas w UTC.
Maksim Pavlov
4

możesz użyć momentu.js do formatowania według czasu lokalnego:

Date.prototype.toISOString = function () {
    return moment(this).format("YYYY-MM-DDTHH:mm:ss");
};
karim fereidooni
źródło
nie nadpisuj wspólnej klasy Date. Może łatwo zepsuć aplikację, jeśli użyjesz niektórych modułów zewnętrznych.
Viacheslav Dobromyslov
3

Trochę się spóźniłem, ale zawsze możesz nadpisać funkcję toJson w przypadku Date używając Prototype w następujący sposób:

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

W moim przypadku Util.getDateTimeString (this) zwraca ciąg podobny do następującego: „2017-01-19T00: 00: 00Z”

charles-emile zaczyna lavigne
źródło
2
Zauważ, że nadpisywanie globalnych przeglądarek może zepsuć osadzone biblioteki innych firm i jest dużym anty-wzorcem. Nigdy nie rób tego na produkcji.
jakub.g
3

Gotowe rozwiązanie wymuszające JSON.stringifyignorowanie stref czasowych:

  • Czysty javascript (na podstawie odpowiedzi Anatolija):

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

Korzystanie z bibliotek stref czasowych typu moment + moment:

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
źródło
2

Zwykle chcesz, aby daty były prezentowane każdemu użytkownikowi w jego własnym czasie lokalnym.

dlatego używamy GMT (UTC).

Użyj Date.parse (jsondatestring), aby uzyskać lokalny ciąg czasu,

chyba że chcesz Twój czas lokalny był pokazywany każdemu odwiedzającemu.

W takim przypadku użyj metody Anatolija.

kennebec
źródło
2

Aby obejść ten problem, użyj moment.jsbiblioteki (wersja bez strefy czasowej).

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

Korzystałem z flatpickr.min.jsbiblioteki. Czas wynikowego utworzonego obiektu JSON jest zgodny z podanym czasem lokalnym, ale z selektorem dat.

Paweł
źródło
2

Wpadam na to trochę pracując ze starszymi rzeczami, gdzie działają tylko na wschodnim wybrzeżu USA i nie przechowują dat w UTC, wszystko to jest EST. Muszę filtrować daty na podstawie danych wprowadzonych przez użytkownika w przeglądarce, więc muszę przekazać datę w czasie lokalnym w formacie JSON.

Żeby rozwinąć to rozwiązanie już zamieszczone - oto czego używam:

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

Rozumiem więc, że to odejmie czas (w milisekundach (stąd 60000) od daty początkowej na podstawie przesunięcia strefy czasowej (zwraca minuty) - w oczekiwaniu na dodanie czasu do JSON () ma zamiar dodać.

MrRobboto
źródło
1

Oto coś naprawdę schludnego i prostego (przynajmniej tak mi się wydaje :)) i nie wymaga manipulacji datą w celu sklonowania lub przeładowania jakichkolwiek natywnych funkcji przeglądarki, takich jak toJSON (odniesienie: How to JSON stringify a javascript Date and keep timezone , court's Shawson)

Przekaż funkcję zamiennika do JSON.stringify, która ciągnie rzeczy do zawartości twojego serca !!! W ten sposób nie musisz robić różnic godzinowych i minutowych ani żadnych innych manipulacji.

Umieściłem w console.logs, aby zobaczyć wyniki pośrednie, aby było jasne, co się dzieje i jak działa rekurencja. To ujawnia coś godnego uwagi: parametr wartości zamiennika jest już przekonwertowany na format daty ISO :). Użyj tego [klawisza], aby pracować z oryginalnymi danymi.

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
źródło
1

JavaScript normalnie konwertuje lokalną strefę czasową na UTC.

date = new Date();
date.setMinutes(date.getMinutes()-date.getTimezoneOffset())
JSON.stringify(date)
Sarath Ak
źródło
0

Wszystko sprowadza się do tego, czy twój serwer jest niezależny od strefy czasowej, czy nie. Jeśli tak nie jest, musisz założyć, że strefa czasowa serwera jest taka sama jak klienta lub przesłać informacje o strefie czasowej klienta i uwzględnić to również w obliczeniach.

przykład oparty na bazie 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'

Ostatni z nich jest prawdopodobnie tym, czego chcesz użyć w bazie danych, jeśli nie chcesz poprawnie zaimplementować logiki strefy czasowej.

korc
źródło
0

Zamiast tego toJSONmożesz użyć funkcji formatowania, która zawsze podaje poprawną datę i godzinę +GMT

Jest to najbardziej niezawodna opcja wyświetlania. Pobiera ciąg tokenów i zastępuje je odpowiednimi wartościami.

Asqan
źródło
0

Próbowałem tego w kątowej 8:

  1. utwórz Model:

    export class Model { YourDate: string | Date; }
  2. w swoim komponencie

    model : Model;
    model.YourDate = new Date();
  3. wyślij datę do swojego API w celu zapisania

  4. Podczas ładowania danych z API zrobisz to:

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

otrzymasz datę poprawnie ze strefą czasową.

Mahmoud AbdElzaher
źródło