iamkun / dayjs

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

startOf/endOf misbehave with timezone plugin enabled #1573

Open Kanatorabo opened 3 years ago

Kanatorabo commented 3 years ago

Describe the bug startOf/endOf returns the wrong value with timezone plugin enabled. The information about the timezone gets lost

const dayjs = require("dayjs")
const utc = require("dayjs/plugin/utc");
const timezone = require("dayjs/plugin/timezone");

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

console.log(dayjs.tz("2020-11-02T00:00:00", "Europe/Berlin").startOf("month").toDate());
// Output: 2020-11-01T00:00:00.000Z

Expected behavior

The expected output to code above is imho 2020-10-31T23:00:00.000Z, which relates to 2020-11-01T00:00:00 in Europe/Berlin

Workaround

We looked into the code and found the .toDate() parameter. The code works as expected when calling .toDate("s").

console.log(dayjs.tz("2020-11-02T00:00:00", "Europe/Berlin").startOf("month").toDate("s"));
// Output: 2020-10-31T23:00:00.000Z

Information

imwh0im commented 3 years ago

has no issue in my case

const dayjs = require("dayjs")
const utc = require("dayjs/plugin/utc");
const timezone = require("dayjs/plugin/timezone");

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

console.log(dayjs.tz("2020-11-02T00:00:00", "Europe/Berlin").startOf("month").toDate());  // 2020-10-31T23:00:00.000Z

Day.js Version: 1.10.6 Time zone: Asia/Seoul

flashspys commented 3 years ago

@imwh0im Indeed. The default system TZ is part of this problem.

TZ=UTC node ./the-code-you-posted-above.js // 2020-10-31T23:00:00.000Z <- correct
TZ=Europe/Berlin node ./the-code-you-posted-above.js // 2020-11-01T00:00:00.000Z <- wrong
imwh0im commented 3 years ago

@flashspys Should the two result values be the same? I'm asking because I don't know the script in the the-code-you-posted-above.js.

kmcs commented 3 years ago

@imwh0im it's probably the code from your comment. @flashspys it works for me if I use the internal Date object $d: index.js: toDate() { return new Date(this.$d); } But then utc-utcOffset.test fails.

bpolaszek commented 3 years ago

Hello, just stumbled upon the same bug and it doesn't seem related to the system's timezone. Here's an easily reproducible snippet.

Here's the context:

Example: User input: 2021-06-18 00:00:00 Europe/Paris Expected output: 2021-06-18 23:59:59 Europe/Paris -> 2021-06-18 21:59:59 UTC

const input = dayjs.tz('2021-06-18 00:00', 'Europe/Paris');
console.log(input.endOf('day').format('YYYY-MM-DDTHH:mm:ssZ[Z]')); // 2021-06-18T23:59:59+02:00Z - correct
console.log(input.endOf('day').tz('UTC').format('YYYY-MM-DDTHH:mm:ssZ[Z]')); // 2021-06-18T20:59:59+00:00Z - invalid

Instead of using endOf(), doing this "by hand" actually works (but requires first ensuring hour input is 00:00:00):

const input = dayjs.tz('2021-06-18 00:00', 'Europe/Paris');
console.log(input.add(1, 'day').subtract(1, 'second').format('YYYY-MM-DDTHH:mm:ssZ[Z]')); // 2021-06-18T23:59:59+02:00Z - correct
console.log(input.add(1, 'day').subtract(1, 'second').tz('UTC').format('YYYY-MM-DDTHH:mm:ssZ[Z]')); // 2021-06-18T21:59:59+00:00Z - valid