JSON Stringify змінює час дати через UTC


98

Мої об'єкти дати в JavaScript завжди представлені UTC +2 через те, де я перебуваю. Звідси так

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

Проблема полягає в JSON.stringifyперетворенні зазначеної вище дати в

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

Мені потрібно, щоб дату та час вшановували, але це не так, отже, це повинно бути

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

В основному я використовую це:

var jsonData = JSON.stringify(jsonObject);

Я спробував передати параметр замінника (другий параметр на stringify), але проблема в тому, що значення вже оброблено.

Я також намагався використовувати toString()і toUTCString()на об'єкті дати, але вони теж не дають мені того, що я хочу ..

Хто-небудь може мені допомогти?


17
2009-09-28T10:00:00Z не представляє того самого моменту в часі, що і Mon Sep 28 10:00:00 UTC+0200 2009. Дата Zв ISO 8601 означає UTC, а 10 годин за UTC - це інший момент часу до 10 години за +0200. Одне було б бажати, щоб дата була серіалізована з правильним часовим поясом, але ви просите нас допомогти вам серіалізувати її до подання, яке є однозначно, об’єктивно неправильним .
Mark Amery

1
Щоб додати коментар до Marks, у більшості випадків найкраще зберігати свої дати як UTC-час, щоб ви могли підтримувати користувачів у різних часових поясах
JoeCodeFrog

Відповіді:


67

Нещодавно я зіткнувся з тим самим питанням. І це було вирішено за допомогою наступного коду:

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

так, але це якщо веб-сайт використовується в моїй країні, якщо він використовується в іншій країні, як США - це не буде 2 ...
Марк Сміт

Очевидно, це значення слід розрахувати.
Анатолій

3
спасибі ... Я насправді знайшов тут чудову бібліотеку, blog.stevenlevithan.com/archives/date-time-format, все, що вам потрібно для цього (можливо, це допоможе вам), ви передаєте false і воно не конвертує. var something = dateFormat (myStartDate, "isoDateTime", false);
Марк Сміт

11
це неправильно, оскільки робить ваш код
нечасовим

4
Ця відповідь неправильна. Оператор не розуміє, що "2009-09-28T08: 00: 00Z" та "Пн 28 вересня 10:00:00 UTC + 0200 2009" - це абсолютно однаковий момент часу, і що коригування зміщення часового поясу насправді створює неправильний час.
RobG

41

JSON використовує Date.prototype.toISOStringфункцію, яка не представляє місцевий час - вона представляє час у незміненому UTC - якщо ви подивитесь на вихідні дані дати, ви побачите, що ви знаходитесь у UTC + 2 години, саме тому рядок JSON змінюється на дві години, але якщо це дозволяє правильно відображати один і той же час у кількох часових поясах.


2
Ніколи про це не думав, але ти маєш рацію. Це рішення: я можу вказати будь-який бажаний формат за допомогою прототипування.
racs


9

Ось ще одна відповідь (і особисто я вважаю це більш доречним)

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 дякую, що вказав на це, але теги у питанні згадують JavaScript.
серпень

6

date.toJSON () друкує UTC-Date у форматі String (отже, додає зсув разом із ним, коли перетворює його у формат JSON).

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

6
Зазвичай це гарна ідея додати пояснення щодо того, що робить ваш код. Це дозволяє новим розробникам зрозуміти, як працює код.
Калеб Клеветер

Чи можете ви пояснити, чому код у відповіді повинен працювати?
Крістік

Це працює і для мене, але чи можете ви пояснити, як ви це зробили?
Deven Patil

Я спробую пояснити другий рядок цього коду .. date.getTime()повертає час у мілісекундах, тому нам слід також перетворити другий операнд у мілісекунди. Оскільки date.getTimezoneOffset()зсув повертається за хвилини, ми множимо його 60000, оскільки 1 хвилина = 60000мілісекунд. Отже, віднімаючи зміщення від поточного часу, ми отримуємо час у UTC.
Максим Павлов

4

Ви можете використовувати moment.js для форматування за місцевим часом:

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

не перезаписувати загальний клас Date. Це може легко зламати вашу програму, якщо використовувати деякі зовнішні модулі.
В’ячеслав Добромислов

3

Я трохи запізнився, але ви завжди можете переписати функцію toJson у випадку дати, використовуючи прототип:

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

У моєму випадку Util.getDateTimeString (this) повертає такий рядок: "2017-01-19T00: 00: 00Z"


2
Зверніть увагу, що перезапис глобальних веб-переглядачів може зламати сторонні бібліотеки, які ви вбудовуєте, і є великим анти-шаблоном. Ніколи не робіть цього на виробництві.
jakub.g

2

Зазвичай ви хочете, щоб дати були представлені кожному користувачеві за власним місцевим часом -

тому ми використовуємо GMT (UTC).

Використовуйте Date.parse (jsondatestring), щоб отримати місцевий рядок часу,

якщо ви не хочете, щоб ваш місцевий час показувався кожному відвідувачеві.

У такому випадку скористайтеся методом Анатолія.


2

Вирішили цю проблему, скориставшись moment.jsбібліотекою (версія не часового поясу).

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

Я користувався flatpickr.min.jsбібліотекою. Час отриманого об'єкта JSON створюється збігається з місцевим часом, указаним за вибором дати.


2

Нестандартне рішення для примусового JSON.stringifyігнорування часових поясів:

  • Чистий javascript (на основі відповіді Анатолія):

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

Використання бібліотеки 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

Я трохи стикаюся з цим, працюючи зі застарілими речами, де вони працюють лише на східному узбережжі США і не зберігають дати в UTC, це все EST. Мені потрібно фільтрувати дати на основі введення користувачем у браузері, тому я повинен передавати дату за місцевим часом у форматі JSON.

Тільки для детальної розробки цього вже опублікованого рішення - ось що я використовую:

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

Отже, я розумію, це триватиме і відніме час (у мілісекундах (отже, 60000) від дати початку на основі зміщення часового поясу (повертає хвилини) - в очікуванні додавання часу до JSON ().


1

Ось щось справді охайне і просте (принаймні, я так вважаю :)) і не вимагає клонування дати, щоб клонувати або перевантажувати будь-яку з власних функцій браузера, наприклад toJSON (довідка: Як JSON розшифрувати дату JavaScript і зберегти часовий пояс , залицяння Шоусон)

Передайте функцію заміни в JSON.stringify, яка розшифровує речі до душі !!! Таким чином, вам не доведеться робити різниці в годину і хвилину або будь-які інші маніпуляції.

Я помістив console.logs, щоб побачити проміжні результати, щоб було зрозуміло, що відбувається і як працює рекурсія. Це виявляє щось гідне уваги: ​​параметр значення для заміни вже перетворений у формат дати ISO :). Використовуйте цю [клавішу] для роботи з вихідними даними.

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 зазвичай перетворює місцевий часовий пояс у UTC.

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

0

Все зводиться до того, чи є серверний сервер вашого сервера часовим поясом чи ні. Якщо це не так, то вам потрібно припустити, що часовий пояс сервера такий самий, як і клієнт, або передати інформацію про часовий пояс клієнта і включити це також у розрахунки.

приклад на основі серверної бази 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'

Останнє - це, мабуть, те, що ви хочете використовувати в базі даних, якщо ви не бажаєте належним чином реалізовувати логіку часових поясів.


0

Замість toJSON, ви можете використовувати функцію форматування, яка завжди дає правильну дату та час +GMT

Це найнадійніший варіант відображення. Він приймає рядок лексем і замінює їх відповідними значеннями.


0

Я спробував це в кутовому 8:

  1. створити модель:

    export class Model { YourDate: string | Date; }
  2. у вашому компоненті

    model : Model;
    model.YourDate = new Date();
  3. надішліть дату своєму API для збереження

  4. Завантажуючи дані з API, ви зробите наступне:

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

ви правильно отримаєте дату з часовим поясом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.