【问题标题】:JSON Stringify changes time of date because of UTCJSON Stringify 因 UTC 而更改日期时间
【发布时间】:2010-12-01 23:17:15
【问题描述】:

由于我所在的位置,我在 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(),但这些也没有给我想要的东西..

谁能帮帮我?

【问题讨论】:

  • 2009-09-28T10:00:00Z Mon Sep 28 10:00:00 UTC+0200 2009 代表的时间不同ISO 8601 日期中的 Z 表示 UTC,而 UTC 中的 10 点钟与 +0200 中的 10 点钟时间不同。希望使用正确的时区对日期进行序列化是一回事,但您要求我们帮助您将其序列化为明确、客观地错误的表示形式。
  • 要添加到 Marks 评论,在大多数情况下,最好将您的日期时间存储为 UTC 时间,这样您就可以支持不同时区的用户
  • 这个被接受的答案解决了我的问题stackoverflow.com/questions/31096130/…

标签: javascript json datetime utc


【解决方案1】:

最近我遇到了同样的问题。并使用以下代码解决:

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

【讨论】:

  • 是的,但这是如果该网站在我的国家/地区使用,如果在美国等其他国家/地区使用 - 它不会是 2 ...
  • 显然应该计算这个值。
  • 谢谢...我实际上在这里找到了一个很棒的库,blog.stevenlevithan.com/archives/date-time-format 你只需要这样做(也许它会帮助你),你传递 false 并且它不会转换。 var something = dateFormat(myStartDate, "isoDateTime", false);
  • 这是不正确的,因为它使您的代码非时区安全——您应该在重新读取日期时更正时区。
  • 这个答案是错误的。 OP 没有意识到“2009-09-28T08:00:00Z”和“Mon Sep 28 10:00:00 UTC+0200 2009”是完全相同的时间并且正在调整因为时区偏移实际上是在创建错误的时间。
【解决方案2】:

JSON 使用 Date.prototype.toISOString 函数,它不代表本地时间——它代表未修改的 UTC 时间——如果您查看日期输出,您会发现您处于 UTC+2 小时,这就是 JSON 的原因字符串更改两个小时,但如果这允许跨多个时区正确表示相同的时间。

【讨论】:

  • 从来没有想过这个,但你是对的。这是解决方案:我可以使用原型指定任何我喜欢的格式。
【解决方案3】:

为了记录,请记住“2009-09-28T08:00:00Z”中的最后一个“Z”表示时间确实是UTC。

详情请见http://en.wikipedia.org/wiki/ISO_8601

【讨论】:

    【解决方案4】:

    date.toJSON() 将 UTC-Date 打印为格式化的字符串(因此在将其转换为 JSON 格式时添加偏移量)。

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

    【讨论】:

    • 通常最好对代码的作用进行解释。这使新开发人员能够了解代码的工作原理。
    • 你能解释一下为什么答案中的代码应该有效吗?
    • 这也适用于我,但你能解释一下你是怎么做到的吗?
    • 我将尝试解释这段代码的第二行。date.getTime() 以毫秒为单位返回时间,因此我们也应该将第二个操作数转换为毫秒。由于date.getTimezoneOffset() 以分钟为单位返回偏移量,因此我们将其乘以 60000,因为 1 分钟 = 60000 毫秒。因此,通过从当前时间中减去偏移量,我们得到了 UTC 时间。
    • 对我来说这是最好的答案
    【解决方案5】:

    这是另一个答案(我个人认为更合适)

    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】:

    强制JSON.stringify忽略时区的开箱即用解决方案:

    • 纯 javascript(基于 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>

    【讨论】:

      【解决方案7】:

      您可以使用moment.js 来格式化本地时间:

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

      【讨论】:

      • 不要覆盖公共类日期。如果使用一些外部模块,它很容易破坏您的应用程序。
      【解决方案8】:

      我有点晚了,但你总是可以像这样使用 Prototype 覆盖 toJson 函数:

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

      在我的例子中,Util.getDateTimeString(this) 返回一个像这样的字符串:“2017-01-19T00:00:00Z”

      【讨论】:

      • 请注意,覆盖浏览器全局变量可能会破坏您嵌入的第三方库,这是一个很大的反模式。永远不要在生产环境中这样做。
      【解决方案9】:

      我在处理遗留的东西时遇到了一些问题,它们只在美国东海岸工作,不以 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()。

      【讨论】:

        【解决方案10】:

        JavaScript 通常将本地时区转换为 UTC 。

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

        【讨论】:

          【解决方案11】:

          通常您希望在每个用户自己的当地时间显示日期-

          这就是我们使用 GMT (UTC) 的原因。

          使用 Date.parse(jsondatestring) 获取本地时间字符串,

          除非您想要向每位访问者显示您的当地时间。

          在这种情况下,使用 Anatoly 的方法。

          【讨论】:

            【解决方案12】:

            使用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 对象的时间与提供的本地时间匹配,但与日期选择器匹配。

            【讨论】:

              【解决方案13】:

              这里有一些非常简洁的东西(至少我相信 :))并且不需要对日期进行操作来克隆或重载任何浏览器的本机函数,如 toJSON(参考:How to JSON stringify a javascript Date and preserve timezone,礼貌的 Shawson)

              将替换函数传递给 JSON.stringify 将内容字符串化为您的心脏内容!!!这样您就不必进行小时和分钟差异或任何其他操作。

              我已将 console.logs 放入以查看中间结果,因此很清楚发生了什么以及递归是如何工作的。这揭示了一些值得注意的事情:替换器的值参数已经转换为 ISO 日期格式:)。使用 this[key] 处理原始数据。

              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.
              */
              

              【讨论】:

                【解决方案14】:

                一切都归结为您的服务器后端是否与时区无关。 如果不是,那么您需要假设服务器的时区与客户端的时区相同,或者传输有关客户端时区的信息并将其也包含在计算中。

                基于 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'
                

                最后一个可能是你想在数据库中使用的,如果你不愿意正确实现时区逻辑。

                【讨论】:

                  【解决方案15】:

                  您可以使用format 函数代替toJSON,它总是给出正确的日期和时间+GMT

                  这是最强大的显示选项。它需要一串令牌 并将它们替换为对应的值。

                  【讨论】:

                    【解决方案16】:

                    我在 Angular 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");

                    您将根据您的时区正确获取您的日期。

                    【讨论】:

                      【解决方案17】:

                      在这种情况下,我认为您需要将日期转换为 UNIX 时间戳

                      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 以秒为单位返回时间。这就是为什么你需要除以 1000 和 round

                      【讨论】:

                        【解决方案18】:

                        我遇到了同样的问题。 我解决的方法是:

                          var currentTime = new Date();
                        
                          Console.log(currentTime); //Return: Wed Sep 15 13:52:09 GMT-05:00 2021
                          Console.log(JSON.stringify(currentTime));  //Return: "2021-09-15T18:52:09.891Z"
                        
                        var currentTimeFixed = new Date(currentTime.setHours(currentTime.getHours() - (currentTime.getUTCHours() - currentTime.getHours())));
                        
                          Console.log(JSON.stringify(currentTimeFixed)); //Return:  "2021-09-15T13:52:09.891Z"
                        

                        【讨论】:

                          猜你喜欢
                          • 2023-03-21
                          • 1970-01-01
                          • 2015-06-14
                          • 2018-12-31
                          • 1970-01-01
                          • 2011-09-06
                          • 1970-01-01
                          • 1970-01-01
                          • 2017-11-29
                          相关资源
                          最近更新 更多