iamkun / iamkun/dayjs

`dayjs.tz` parses ISO string as local time instead of UTC

Open
#2,946 7 comments 5 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
48.7k
Forks
2.5k
PR merge metrics
No merged PRs in 30d

Description

**Describe the bug**
Using `moment-timezone`, this code will parse as UTC, then convert to the default timezone, which is what I would expect:

```javascript
moment.tz.setDefault("Australia/Melbourne");
const dt = moment("2025-10-24T05:00:00Z");
console.log(dt.toISOString()); // 2025-10-24T05:00:00.000Z
console.log(dt.tz()); // Australia/Melbourne
```

In Dayjs however, the equivalent parses the ISO string as local time, despite the `Z` suffix:

```javascript
dayjs.tz.setDefault("Australia/Melbourne");
const dt = dayjs.tz("2025-10-24T05:00:00Z");
console.log(dt.toISOString()); // 2025-10-23T18:00:00.000Z
console.log(dt["$x"]?.["$timezone"]); // Australia/Melbourne
```

The non-tz way of constructing a dayjs obj correctly parses as UTC, however the resulting object is not in the default timezone:

```javascript
const dt = dayjs("2025-10-24T05:00:00Z");
console.log(dt.toISOString()); // 2025-10-24T05:00:00.000Z
console.log(dt["$x"]?.["$timezone"]); // undefined
```

**Expected behavior**
I would expect `dayjs.tz` to parse `2025-10-24T05:00:00Z` as UTC, not local time.

Maybe I'm missing something, but it seems like this is a bug.

**Workaround**
To workaround it, I need to parse using `dayjs(timestamp)` then convert to the correct timezone. Since there's no function to get the default timezone, I need to cache the default somewhere (note: for this example 'caching' seems pointless, the point is the default tz needs to be stored somewhere accessible because `dayjs.tz.getDefault` doesn't exist). E.g.

```javascript
// This doesn't actually help for this use case
dayjs.tz.setDefault("Australia/Melbourne");
// Also cache the default timezone
const defaultTz = "Australia/Melbourne";
// Don't use dayjs.tz, just use dayjs
let dt = dayjs("2025-10-24T05:00:00Z");
// Convert to timezone
dt = dt.tz(defaultTz);
console.log(dt.toISOString()); // 2025-10-24T05:00:00.000Z
console.log(dt["$x"]?.["$timezone"]); // Australia/Melbourne
```

**Information**
- Day.js Version: v1.11.18
- OS: MacOS
- Browser: Firefox 143.0.4
- Time zone: GMT+11:00 AEDT (Australia/Melbourne)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.