hexo icon indicating copy to clipboard operation
hexo copied to clipboard

Date & Timezone issue related with Front-Matter

Open SukkaW opened this issue 4 years ago • 4 comments

Check List

Please check the following before submitting a new issue.

  • [x] I have already read Docs page & Troubleshooting page
  • [x] I have already searched existing issues and they are not helpful to me
  • [x] I examined error or warning messages and it's difficult to solve
  • [x] Using the latest version of Hexo (run hexo version to check)
  • [x] Node.js is higher than minimum required version

The issue has been reported in

  • #4239
  • #3397
  • #3282
  • #2063

For example, I have a post setup as following:

# _config.yml
timezone: Asia/Shanghai
title: Hello World
date: 2020-09-29 23:00:00

Also, my machine is under the Asia/Shanghai timezone, which will also affect Node.js' timezone.

After the front-matter being processed by js-yaml, the "Date-like string" will be converted into Date object:

const jsYaml = require("js-yaml");

jsYaml.load('date: 2020-09-29 23:00:00');
// > "Tue, 29 Sep 2020 23:00:00 GMT" 

Notice that the input was converted into the UTC? Users will only fill in the front-matter with their local time, not the UTC. Thus it is not the desired behavior.

So during the processing of front-matter, Date#getTimezoneOffset has been used:

// https://github.com/hexojs/hexo-front-matter/blob/ccbdff36d151a56932418cdc6d0329d866032a1b/lib/front_matter.js#L55-L62

// Convert timezone
Object.keys(data).forEach(key => {
  const item = data[key];

  if (item instanceof Date) {
    data[key] = new Date(item.getTime() + (item.getTimezoneOffset() * 60 * 1000));
  }
});

It will result in:

"Tue, 29 Sep 2020 15:00:00 GMT"

Which is the desired behavior.

Then, during the processing pf posts, moment.timezone was used:

https://github.com/hexojs/hexo/blob/557487a2f8ab0065bf94d5a9466c54034f1db17f/lib/plugins/processor/post.js#L80-L82 https://github.com/hexojs/hexo/blob/557487a2f8ab0065bf94d5a9466c54034f1db17f/lib/plugins/processor/common.js#L51-L60

If the config.timezone is configured correctly, there will be no differences between before and after timezone():

"Tue, 29 Sep 2020 15:00:00 GMT"
// There's no differences

A demo can be found here: https://runkit.com/sukkaw/5f732261a7aac8001a42bcc4

Date#getTimezoneOffset: -480
Date parsed by js-yaml: Tue, 29 Sep 2020 23:00:00 GMT
Date after calculating the timezone offset: Tue, 29 Sep 2020 15:00:00 GMT
Date after moment-timezone: Tue, 29 Sep 2020 15:00:00 GMT
Date after moment-timezone (without pre offset): Tue, 29 Sep 2020 15:00:00 GMT // Will be discussed later

So, what about a different timezone environment? For example, the CI environment. Its timezone will not be Asia/Shanghai.

So here is another demo: https://repl.it/repls/LividBewitchedControlflowgraph#index.js

Date#getTimezoneOffset: 0 // See? It is not -480 anymore, it is now 0
Date parsed by js-yaml: Tue, 29 Sep 2020 23:00:00 GMT
Date after calculating the timezone offset: Tue, 29 Sep 2020 23:00:00 GMT // It is now wrong
Date after moment-timezone: Tue, 29 Sep 2020 15:00:00 GMT // But it is still correct
Date after moment-timezone (without pre offset): Tue, 29 Sep 2020 15:00:00 GMT // Will be discussed later

As you can see, Date after calculating the timezone offset is now wrong, while Date after moment-timezone is still correct. That's because timezone() takes getTimezoneOffset into consideration.


So, what about removing "getTimezoneOffset" completely?

Here goes Date after moment-timezone (without pre offset). It is generated without using Date#getTimezoneOffset, and the result is still correct. It also means it is not affected by the environment (no `Date#getTimezoneOffset being used).

By eliminating the Node.js timezone affection (only dependents on users' config.timezone configuration) the timezone issue should be solved.

SukkaW avatar Sep 29 '20 13:09 SukkaW

cc @YoshinoriN @ItsZero @stevenjoezhang @curbengh


So far I just assume those issues were caused by their machines' timezones are inconsistent with their hexo configurations.

SukkaW avatar Sep 29 '20 13:09 SukkaW

So, what about removing "getTimezoneOffset" completely?

+1, Hexo shouldn't need to query the host machine's timezone, config.timezone should be the canonical timezone.


There is still a need to query host's timezone since config.timezone is empty by default. Host's timezone should be ignored if config.timezone is specified.


Another complication is that it is also possible to specify timezone in front-matter, e.g. 2013-02-08 09:30:26.123+07:00.

Perhaps we can prioritise like this:

  1. Front-matter's timezone
  2. config.timezone
  3. host's timezone

curbengh avatar Oct 04 '20 01:10 curbengh

There is still a need to query the host's timezone

@curbengh

It appears that there is no way we could get the timezone name directly (If so, we could use it as a default value during hexo config processing). The only thing we get is getTimezoneOffset.

SukkaW avatar Oct 05 '20 09:10 SukkaW

@curbengh

We can use JavaScript built-in object Intl:

Intl.DateTimeFormat().resolvedOptions().timeZone
// UTC
// Asia/Tokyo
// etc.

We can use it for the default timezone in config.

SukkaW avatar Oct 22 '20 08:10 SukkaW