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

Date & Timezone issue related with Front-Matter #4548

Open
5 tasks done
SukkaW opened this issue Sep 29, 2020 · 4 comments
Open
5 tasks done

Date & Timezone issue related with Front-Matter #4548

SukkaW opened this issue Sep 29, 2020 · 4 comments

Comments

@SukkaW
Copy link
Member

SukkaW commented Sep 29, 2020

Check List

Please check the following before submitting a new issue.

The issue has been reported in


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:

if (data.date) {
if (timezoneCfg) data.date = timezone(data.date, timezoneCfg);
} else {

exports.timezone = (date, timezone) => {
if (moment.isMoment(date)) date = date.toDate();
const offset = date.getTimezoneOffset();
const ms = date.getTime();
const target = moment.tz.zone(timezone).utcOffset(ms);
const diff = (offset - target) * DURATION_MINUTE;
return new Date(ms - diff);
};

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
Copy link
Member Author

SukkaW commented Sep 29, 2020

cc @yoshinorin @itszero @stevenjoezhang @curbengh


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

@curbengh
Copy link
Contributor

curbengh commented Oct 4, 2020

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

@stevenjoezhang stevenjoezhang pinned this issue Oct 5, 2020
@SukkaW
Copy link
Member Author

SukkaW commented Oct 5, 2020

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
Copy link
Member Author

SukkaW commented Oct 22, 2020

@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.

@stevenjoezhang stevenjoezhang unpinned this issue Feb 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants