Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect endOf & startOf when daylight saving in effect #1262

Open
janousek opened this issue Dec 8, 2020 · 6 comments
Open

Incorrect endOf & startOf when daylight saving in effect #1262

janousek opened this issue Dec 8, 2020 · 6 comments

Comments

@janousek
Copy link

janousek commented Dec 8, 2020

Describe the bug
startOf and endOf functions does not work correctly when timezone plugin in use and daylight saving in effect.
This seems to be regression from 1.9.6 (worked in 1.9.6 and does not work in 1.9.7).

This is output from 1.9.7:

dayjs("2021-02-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-02-28T22:59:59.999Z
dayjs("2021-03-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-03-31T22:59:59.999Z

This is output from 1.9.6:

dayjs("2021-02-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-02-28T22:59:59.999Z
dayjs("2021-03-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-03-31T21:59:59.999Z

Expected behavior
Momentjs produces correct result:

moment("2021-02-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-02-28T22:59:59.999Z
moment("2021-03-25T22:00:00.000Z").tz('Europe/Prague').endOf('month').toISOString() // 2021-03-31T21:59:59.999Z

Related
I believe the error is caused by this pull request: #1229
Not sure but this is maybe related to this bug: #1260

@lucasrcosta
Copy link

Here's a mimatch comparison of day.js an moment showing different results which I believe is related to daylight savings.

https://runkit.com/lucasrcosta/5feca83aea7c09001b879b3a is related to

@janousek janousek closed this as completed Feb 1, 2021
@janousek janousek reopened this Feb 1, 2021
@Thebarda
Copy link

Hello,
I have the same error with startOf('month')
Here is a repro https://codesandbox.io/s/bug-startofmonth-dayjs-g9rpz?file=/src/index.js:339-361

BTW, it seems there are other issue tickets that affect the startOf with timezone

@jessejenks
Copy link

Strangely, the offset appears to be correct when using format.

dayjs("2021-03-25T22:00:00.000Z").tz("Europe/Prague").endOf("month").format()
// 2021-03-31T23:59:59+02:00

So one workaround is

new Date(dayjs("2021-03-25T22:00:00.000Z").tz("Europe/Prague").endOf("month").format()).toISOString()
// 2021-03-31T21:59:59.000Z

I just wrote a simple wrapper function

function toISOString(dateTime: string | Date | dayjs.Dayjs) {
    return new Date(dayjs(dateTime).format()).toISOString();
}

I am using version 1.10.6, so maybe this is new behavior.

In any case, part of the issue appears to be that toISOString does not use the correct offset across daylight saving time starting or ending.

Here's a more comprehensive example for "America/Los_Angeles".
In this timezone, DST begins on 03-14 and ends on 11-07.
So on 03-01, the offset should be -08:00 and on 03-31 the offset should be -07:00.
Similarly, on 11-01 the offset should still be -07:00, while on 11-30 the offset should be -08:00.

The issue seems to be that toISOString uses the offset of the starting date, rather than the offset of the final date.

import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";

dayjs.extend(utc);
dayjs.extend(timezone);

const march10 = "2021-03-10T22:00:00.000Z";
const march20 = "2021-03-20T22:00:00.000Z";
const november3 = "2021-11-03T22:00:00.000Z";
const november20 = "2021-11-20T22:00:00.000Z";

// ===
// startOf month from 03-10
// ===
console.log("startOf month from 03-10");
console.log(dayjs(march10).tz("America/Los_Angeles").startOf("month").toISOString());
// 2021-03-01T07:00:00.000Z

console.log(dayjs(march10).tz("America/Los_Angeles").startOf("month").format());
// 2021-03-01T00:00:00-08:00

console.log(new Date(dayjs(march10).tz("America/Los_Angeles").startOf("month").format()).toISOString());
// 2021-03-01T08:00:00.000Z

// ===
// endOf month from 03-10
// ===
console.log("endOf month from 03-10");
console.log(dayjs(march10).tz("America/Los_Angeles").endOf("month").toISOString());
// 2021-04-01T05:59:59.999Z

console.log(dayjs(march10).tz("America/Los_Angeles").endOf("month").format());
// 2021-03-31T23:59:59-07:00

console.log(new Date(dayjs(march10).tz("America/Los_Angeles").endOf("month").format()).toISOString());
// 2021-04-01T06:59:59.000Z

// ===
// startOf month from 03-20
// ===
console.log("startOf month from 03-20");
console.log(dayjs(march20).tz("America/Los_Angeles").startOf("month").toISOString());
// 2021-03-01T07:00:00.000Z

console.log(dayjs(march20).tz("America/Los_Angeles").startOf("month").format());
// 2021-03-01T00:00:00-08:00

console.log(new Date(dayjs(march20).tz("America/Los_Angeles").startOf("month").format()).toISOString());
// 2021-03-01T08:00:00.000Z

// ===
// endOf month from 03-20
// ===
console.log("endOf month from 03-20");
console.log(dayjs(march20).tz("America/Los_Angeles").endOf("month").toISOString());
// 2021-04-01T05:59:59.999Z

console.log(dayjs(march20).tz("America/Los_Angeles").endOf("month").format());
// 2021-03-31T23:59:59-07:00

console.log(new Date(dayjs(march20).tz("America/Los_Angeles").endOf("month").format()).toISOString());
// 2021-04-01T06:59:59.000Z

// ===
// startOf month from 11-03
// ===
console.log("startOf from 11-03");
console.log(dayjs(november3).tz("America/Los_Angeles").startOf("month").toISOString());
// 2021-11-01T06:00:00.000Z

console.log(dayjs(november3).tz("America/Los_Angeles").startOf("month").format());
// 2021-11-01T00:00:00-07:00

console.log(new Date(dayjs(november3).tz("America/Los_Angeles").startOf("month").format()).toISOString());
// 2021-11-01T07:00:00.000Z

// ===
// endOf month from 11-03
// ===
console.log("endOf from 11-03");
console.log(dayjs(november3).tz("America/Los_Angeles").endOf("month").toISOString());
// 2021-12-01T06:59:59.999Z

console.log(dayjs(november3).tz("America/Los_Angeles").endOf("month").format());
// 2021-11-30T23:59:59-08:00

console.log(new Date(dayjs(november3).tz("America/Los_Angeles").endOf("month").format()).toISOString());
// 2021-12-01T07:59:59.000Z

// ===
// startOf month from 11-20
// ===
console.log("startOf from 11-20");
console.log(dayjs(november20).tz("America/Los_Angeles").startOf("month").toISOString());
// 2021-11-01T06:00:00.000Z

console.log(dayjs(november20).tz("America/Los_Angeles").startOf("month").format());
// 2021-11-01T00:00:00-07:00

console.log(new Date(dayjs(november20).tz("America/Los_Angeles").startOf("month").format()).toISOString());
// 2021-11-01T07:00:00.000Z

// ===
// endOf month from 11-20
// ===
console.log("endOf from 11-20");
console.log(dayjs(november20).tz("America/Los_Angeles").endOf("month").toISOString());
// 2021-12-01T06:59:59.999Z

console.log(dayjs(november20).tz("America/Los_Angeles").endOf("month").format());
// 2021-11-30T23:59:59-08:00

console.log(new Date(dayjs(november20).tz("America/Los_Angeles").endOf("month").format()).toISOString());
// 2021-12-01T07:59:59.000Z

@Kinco4TC
Copy link

Kinco4TC commented Mar 8, 2022

I believe it causes by the error of valueOf function.
There is the solution:
After dayjs extend utc,
dayjs.prototype.valueOf = function () { const addedOffset = !this.$utils().u(this.$offset) ? this.$offset + (this.$x.$localOffset || this.$d.getTimezoneOffset()) : 0; return this.$d.valueOf() - addedOffset * MILLISECONDS_A_MINUTE; };

@fjcero
Copy link

fjcero commented Apr 23, 2023

I confirm the issue persists. Also, the workaround with format works well enough for me.

@BoneFiend
Copy link

Issue is still there in 1.11.13
Another workaround is to simply add .tz() again at the end.

For example instead of:
dayjs().tz("GMT").endOf('day')

You use:
dayjs().tz("GMT").endOf('day').tz()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants