JSON Stringify เปลี่ยนเวลาของวันที่เนื่องจาก UTC


99

วัตถุวันที่ของฉันใน 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);

ฉันพยายามส่งพารามิเตอร์ replacer (พารามิเตอร์ที่สองบน stringify) แต่ปัญหาคือค่าได้ถูกประมวลผลแล้ว

ฉันยังลองใช้toString()และtoUTCString()กับวัตถุวันที่ แต่สิ่งเหล่านี้ไม่ได้ให้สิ่งที่ฉันต้องการเช่นกัน ..

ใครสามารถช่วยฉัน?


17
2009-09-28T10:00:00Z ไม่ได้เป็นตัวแทนช่วงเวลาเดียวกันในเวลาที่Mon Sep 28 10:00:00 UTC+0200 2009เป็น Zในมาตรฐาน ISO 8601วันหมายความ UTC และ 10:00 ใน UTC เป็นช่วงเวลาที่แตกต่างกันในเวลา 10 โมง 0200 มันจะเป็นสิ่งหนึ่งที่จะต้องการวันที่จะต่อเนื่องกับโซนเวลาที่ถูกต้อง แต่คุณกำลังขอให้เราช่วยคุณ serialise มันจะเป็นตัวแทนที่เป็นที่แจ่มแจ้งวัตถุที่ไม่ถูกต้อง
Mark Amery

1
หากต้องการเพิ่มความคิดเห็นใน Marks ในกรณีส่วนใหญ่วิธีปฏิบัติที่ดีที่สุดคือการจัดเก็บวันที่ของคุณเป็นเวลา UTC ดังนั้นคุณจึงสามารถรองรับผู้ใช้ในเขตเวลาต่างๆได้
JoeCodeFrog

คำตอบ:


69

เมื่อเร็ว ๆ นี้ฉันพบปัญหาเดียวกัน และได้รับการแก้ไขโดยใช้รหัสต่อไปนี้:

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

ใช่ แต่นี่คือกรณีที่ใช้เว็บไซต์ในประเทศของฉันหากใช้ในประเทศอื่นเช่นสหรัฐอเมริกาจะไม่เป็น 2 ...
mark smith

เห็นได้ชัดว่าควรคำนวณค่านี้
Anatoliy

3
ขอบคุณ ... ฉันพบห้องสมุดที่ยอดเยี่ยมที่นี่blog.stevenlevithan.com/archives/date-time-format ทั้งหมดที่คุณต้องทำ (อาจจะช่วยคุณได้) คุณส่งเท็จและไม่แปลง var something = dateFormat (myStartDate, "isoDateTime", false);
mark smith

12
สิ่งนี้ไม่ถูกต้องเนื่องจากทำให้รหัสของคุณไม่ใช่เขตเวลาที่ปลอดภัย - คุณควรแก้ไขเขตเวลาเมื่อคุณอ่านวันที่ย้อนกลับไป
olliej

5
คำตอบนี้ผิด OP ไม่ทราบว่า "2009-09-28T08: 00: 00Z" และ "จันทร์ 28 ก.ย. 10:00:00 UTC + 0200 2009" เป็นช่วงเวลาเดียวกันเป๊ะและการปรับค่าชดเชยเขตเวลากำลังสร้าง ผิดเวลา
RobG

41

JSON ใช้Date.prototype.toISOStringฟังก์ชันที่ไม่ได้แสดงเวลาท้องถิ่นซึ่งแสดงถึงเวลาใน UTC ที่ไม่ได้ปรับเปลี่ยน - หากคุณดูที่เอาต์พุตวันที่ของคุณคุณจะเห็นว่าคุณอยู่ที่ UTC + 2 ชั่วโมงซึ่งเป็นสาเหตุที่สตริง JSON เปลี่ยนแปลงภายในสองชั่วโมง แต่หากสิ่งนี้ช่วยให้สามารถแสดงเวลาเดียวกันได้อย่างถูกต้องในหลายเขตเวลา


2
ไม่เคยคิดเรื่องนี้ แต่คุณคิดถูก นี่คือวิธีแก้ปัญหา: ฉันสามารถระบุรูปแบบใดก็ได้ที่ต้องการโดยใช้การสร้างต้นแบบ
racs


10

นี่คือคำตอบอื่น (และโดยส่วนตัวฉันคิดว่าเหมาะสมกว่า)

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
สิงหาคม

7

date.toJSON () พิมพ์ UTC-Date ลงในรูปแบบสตริง (ดังนั้นเพิ่มออฟเซ็ตด้วยเมื่อแปลงเป็นรูปแบบ JSON)

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

6
โดยปกติแล้วเป็นความคิดที่ดีที่จะเพิ่มคำอธิบายว่าโค้ดของคุณทำอะไร สิ่งนี้ช่วยให้นักพัฒนารุ่นใหม่เข้าใจวิธีการทำงานของโค้ด
Caleb Kleveter

คุณช่วยอธิบายได้ไหมว่าทำไมโค้ดในคำตอบจึงควรใช้งานได้
Cristik

สิ่งนี้ใช้ได้กับฉันเช่นกัน แต่คุณช่วยอธิบายได้ไหมว่าคุณทำเช่นนั้นได้อย่างไร
Deven Patil

ฉันจะพยายามอธิบายบรรทัดที่สองของโค้ดนี้ .. date.getTime()คืนเวลาเป็นมิลลิวินาทีดังนั้นเราควรแปลงตัวถูกดำเนินการที่สองเป็นมิลลิวินาทีเช่นกัน เนื่องจากdate.getTimezoneOffset()คืนค่าชดเชยเป็นนาทีเราจึงคูณด้วย 60000 เพราะ 1 นาที = 60000 มิลลิวินาที ดังนั้นการลบออฟเซ็ตออกจากเวลาปัจจุบันทำให้เราได้เวลาเป็น UTC
Maksim Pavlov

4

คุณสามารถใช้moment.jsเพื่อจัดรูปแบบตามเวลาท้องถิ่น:

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

อย่าเขียนทับวันที่ของคลาสทั่วไป มันสามารถทำลายแอปพลิเคชันของคุณได้อย่างง่ายดายหากใช้โมดูลภายนอกบางส่วน
Viacheslav Dobromyslov

3

ฉันช้าไปหน่อย แต่คุณสามารถเขียนทับฟังก์ชัน toJson ได้ตลอดเวลาในกรณีของวันที่โดยใช้ Prototype ดังนี้:

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

ในกรณีของฉัน Util.getDateTimeString (this) ส่งคืนสตริงเช่นนี้: "2017-01-19T00: 00: 00Z"


2
โปรดทราบว่าการเขียนทับเบราว์เซอร์ globals สามารถทำลายไลบรารีของบุคคลที่สามที่คุณฝังไว้และเป็นการต่อต้านรูปแบบขนาดใหญ่ อย่าทำสิ่งนี้ในการผลิต
jakub.g

3

โซลูชันสำเร็จรูปเพื่อบังคับให้JSON.stringifyละเว้นเขตเวลา:

  • จาวาสคริปต์บริสุทธิ์ (ขึ้นอยู่กับคำตอบ 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);

การใช้ห้องสมุด moment + moment-timezone:

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

โดยปกติคุณต้องการให้แสดงวันที่แก่ผู้ใช้แต่ละคนในเวลาท้องถิ่นของเขาเอง

นั่นคือเหตุผลที่เราใช้ GMT (UTC)

ใช้ Date.parse (jsondatestring) เพื่อรับสตริงเวลาท้องถิ่น

เว้นแต่คุณต้องการให้แสดงเวลาท้องถิ่นของคุณแก่ผู้เยี่ยมชม

ในกรณีนั้นให้ใช้วิธีของ Anatoly


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

ฉันพบว่าสิ่งนี้ทำงานเล็กน้อยกับสิ่งที่เป็นมรดกซึ่งพวกเขาทำงานเฉพาะในชายฝั่งตะวันออกของสหรัฐอเมริกาและไม่ได้เก็บวันที่ใน 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) จากวันที่เริ่มต้นตามการชดเชยเขตเวลา (ส่งกลับนาที) - ในความคาดหมายสำหรับการเพิ่มเวลา toJSON () จะเพิ่ม


2

โดยปกติ JavaScript จะแปลงเขตเวลาท้องถิ่นเป็น UTC

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

1

นี่คือสิ่งที่เรียบร้อยและเรียบง่ายจริงๆ (อย่างน้อยฉันก็เชื่อเช่นนั้น :)) และไม่จำเป็นต้องมีการปรับแต่งวันที่เพื่อโคลนหรือโอเวอร์โหลดฟังก์ชันดั้งเดิมของเบราว์เซอร์ใด ๆ เช่น toJSON (อ้างอิง: วิธีการ JSON กำหนดวันที่จาวาสคริปต์และรักษาเขตเวลาชอว์สันที่สุภาพ)

ส่งผ่านฟังก์ชัน replacer ไปยัง 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.
*/

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

คุณจะได้วันที่ของคุณอย่างถูกต้องพร้อมกับเขตเวลาของคุณ


0

ในกรณีนี้ฉันคิดว่าคุณต้องเปลี่ยนวันที่เป็น UNIX timestamp

timestamp = testDate.getTime();
strJson = JSON.stringify(timestamp);

หลังจากนั้นคุณสามารถใช้เพื่อสร้างออบเจ็กต์วันที่และจัดรูปแบบได้อีกครั้ง ตัวอย่างด้วย javascript และtoLocaleDateString( https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/toLocaleDateString )

newDateObject = new Date(JSON.parse(strJson));
newDateObject = newDateObject.toLocalDateStrin([
  "fr-FR",
]);

หากคุณใช้ stringify เพื่อใช้ AJAX ตอนนี้ก็ไม่มีประโยชน์ คุณเพียงแค่ต้องส่งการประทับเวลาและรับในสคริปต์ของคุณ:

$newDateObject = new \DateTime();
$newDateObject->setTimestamp(round($timestamp/1000));

โปรดทราบว่าgetTime()จะคืนเวลาเป็นมิลลิวินาทีและฟังก์ชัน PHP setTimestampจะใช้เวลาเป็นวินาที นั่นเป็นเหตุผลที่คุณต้องหารด้วย 1,000 และround.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.