dayjs icon indicating copy to clipboard operation
dayjs copied to clipboard

Dayjs does not preserve timezone information when serializing to JSON string

Open svdHero opened this issue 5 years ago • 8 comments

When I do this

const timestamp = dayjs("2019-11-26T17:28:35.518+03:00")
const actualJson = JSON.stringify(timestamp)
const expectedJson = "\"2019-11-26T17:28:35.518+03:00\""

I want actualJson to be equal to expectedJson. However, actualJson is "\"2019-11-26T14:28:35.518Z\"". With Moment.js I can do this:

moment.fn.toJSON = function() { return this.format(); }

in order to get my expected JSON string with the preserved timezone information, see https://momentjs.com/docs/#/displaying/as-json/

How can I achieve this in dayjs? I am new to Javascript/Typescript and my compiler tells me that dayjs.fn does not exist. What would be the right workaround?

Information

  • Day.js Version 1.8.28
  • OS: Windows 8.1
  • Time zone: CEST (Berlin)

svdHero avatar Jun 22 '20 10:06 svdHero

As JSON will serialize Dayjs object to an ISO 8601 string. https://day.js.org/docs/en/display/as-json

However, if you want to customize this function, you can write your own plugin to modify it. https://day.js.org/docs/en/plugin/plugin

const JsonPlugin = (option, dayjsClass, dayjsFactory) => {
  // overriding existing API
  dayjsClass.prototype.toJSON = function() {
      return this.format()
   }
}
dayjs.extend(JsonPlugin)

iamkun avatar Jun 22 '20 10:06 iamkun

Thanks @iamkun . I shall try that. I assume that I just add that to the beginning of my project so that it is executed before the first JSON.stringify() call? No need to make a separate node package for the plugin (that would be way over my noobish head at this point of time)?

svdHero avatar Jun 22 '20 10:06 svdHero

Yes, just update it in your project's index.js file and the new dayjs is available in the global scope.

iamkun avatar Jun 22 '20 11:06 iamkun

Hm, there seems to be a more fundamental problem. Apparently, already the parsing does not preserve the timezone information. I have the following Jest test code:

describe("dajys", () => {
    it("preserves timezone information on roundtrip", () =>{
        const originalISOString = "2019-11-26T18:28:35.518+04:00"
        const ISO_FORMAT = "YYYY-MM-DDTHH:mm:ss.SSSZ"
        // ###############
        const actualString = dayjs(originalISOString).format(ISO_FORMAT)
        // ###############
        expect(actualString).toEqual(originalISOString)
    })
})

When I run this, I get a failed test with output:

    Expected: "2019-11-26T18:28:35.518+04:00"
    Received: "2019-11-26T15:28:35.518+01:00"

The date strings are equivalent, but the parsing obviously returned a moment in my local timezone (GMT+01, Berlin). This is exactly how moment.js behaves. However, moment.js has moment.parseZone(...), see https://momentjs.com/docs/#/parsing/, which does exactly what I want and makes the above test code succeed.

Does day.js have anything similar?

svdHero avatar Jun 24 '20 08:06 svdHero

I have the same issue. When parsing a date/time, dayjs immediately converts to local time, losing any timezone information. It does seem like a fundamental requirement to be able to create a date/time object which preserves the timezone. The only workaround seems to be to parse the timezone information out manually and set the offset on the dayjs object - but that's not ideal.

Unless I am missing something? Did you find another solution @svdHero?

EDIT: Actually I tried that workaround and it didn't work

let when = dayjs('2020-10-07T17:00:00-05:00'); // 5pm in -5 timezone parsed as 11pm in +1 timezone
when = when.utcOffset(-300); // Set -300min / -5hr offset
console.log(when.format('HH:mm:ssZ'));

Output: 18:00:00-05:00 // Expected 5pm

PaulJNewell77 avatar Dec 09 '20 16:12 PaulJNewell77

The issue is still present, does anybody know a workaround ?

geraman21 avatar Nov 15 '21 13:11 geraman21

I created the moment parseZone function using dayJS.

`/**

  • @function
  • @name parseZone
  • @description This function returns a dayjs object keeping zone exactly how moment.parseZone() worked
  • @param {string} dateTimeString
  • @returns {dayjs} object */ function parseZone(dateTimeString) { let date = dayjs(dateTimeString); let minutes = 0; if (dateTimeString.includes('-12:00')) { minutes = -720; } else if (dateTimeString.includes('-11:00')) { minutes = -660; } else if (dateTimeString.includes('-10:00')) { minutes = -600; } else if (dateTimeString.includes('-09:30')) { minutes = -570; } else if (dateTimeString.includes('-09:00')) { minutes = -540; } else if (dateTimeString.includes('-08:00')) { minutes = -480; } else if (dateTimeString.includes('-07:00')) { minutes = -420; } else if (dateTimeString.includes('-06:00')) { minutes = -360; } else if (dateTimeString.includes('-05:00')) { minutes = -300; } else if (dateTimeString.includes('-04:00')) { minutes = -240; } else if (dateTimeString.includes('-03:30')) { minutes = -210; } else if (dateTimeString.includes('-03:00')) { minutes = -180; } else if (dateTimeString.includes('-02:00')) { minutes = -120; } else if (dateTimeString.includes('-01:00')) { minutes = -60; } else if (dateTimeString.includes('-00:00') || dateTimeString.includes('+00:00')) { minutes = 0; } else if (dateTimeString.includes('+01:00')) { minutes = 60; } else if (dateTimeString.includes('+02:00')) { minutes = 120; } else if (dateTimeString.includes('+03:00')) { minutes = 180; } else if (dateTimeString.includes('+03:30')) { minutes = 210; } else if (dateTimeString.includes('+04:00')) { minutes = 240; } else if (dateTimeString.includes('+04:30')) { minutes = 270; } else if (dateTimeString.includes('+05:00')) { minutes = 300; } else if (dateTimeString.includes('+05:30')) { minutes = 330; } else if (dateTimeString.includes('+05:45')) { minutes = 345; } else if (dateTimeString.includes('+06:00')) { minutes = 360; } else if (dateTimeString.includes('+06:30')) { minutes = 390; } else if (dateTimeString.includes('+07:00')) { minutes = 420; } else if (dateTimeString.includes('+08:00')) { minutes = 480; } else if (dateTimeString.includes('+08:45')) { minutes = 525; } else if (dateTimeString.includes('+09:00')) { minutes = 540; } else if (dateTimeString.includes('+09:30')) { minutes = 570; } else if (dateTimeString.includes('+10:00')) { minutes = 600; } else if (dateTimeString.includes('+10:30')) { minutes = 630; } else if (dateTimeString.includes('+11:00')) { minutes = 660; } else if (dateTimeString.includes('+12:00')) { minutes = 720; } else if (dateTimeString.includes('+12:30')) { minutes = 750; } else if (dateTimeString.includes('+13:00')) { minutes = 780; } else if (dateTimeString.includes('+14:00')) { minutes = 840; } date = date.utcOffset(minutes); return date; }`

bach942 avatar Feb 15 '22 21:02 bach942

Any progress on this? I don't get it why this isn't a bigger issue.

holm-dk avatar May 22 '24 19:05 holm-dk