iamkun / dayjs

⏰ Day.js 2kB immutable date-time library alternative to Moment.js with the same modern API
https://day.js.org
MIT License
46.81k stars 2.29k forks source link

Dayjs does not preserve timezone information when serializing to JSON string #934

Open svdHero opened 4 years ago

svdHero commented 4 years ago

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

iamkun commented 4 years ago

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)
svdHero commented 4 years ago

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

iamkun commented 4 years ago

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

svdHero commented 4 years ago

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?

PaulJNewell77 commented 3 years ago

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

geraman21 commented 2 years ago

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

bach942 commented 2 years ago

I created the moment parseZone function using dayJS.

`/**

thj-dk commented 4 months ago

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