Time zone issue involving date fns format()

前端 未结 1 1907
傲寒
傲寒 2021-02-08 06:49
const dt = new Date(\'2017-12-12\');
console.log(format(dt, \'YYYY-MM-DD\'));

The above code logs 2017-12-11 in the US, but 2017-12-12 in India.

<
1条回答
  •  无人共我
    2021-02-08 07:08

    You will need to subtract the time zone offset of your local time zone from the Date instance, before you pass it to format from date-fns. For example:

    const dt = new Date('2017-12-12');
    const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
    console.log(format(dtDateOnly, 'YYYY-MM-DD')); // Always "2017-12-12"
    

    Problem

    You want to handle only the date part of the Date instance, because the time part does not make sense for birthdates. However, the Date object does not offer any "date-only" mode. You can access both its date and time parts in the local time zone or UTC. The problem is, that format from date-fns prints the output always in the local time zone.

    When you executed the constructor only with the date part:

    const dt = new Date('2017-12-12');
    

    The JavaScript engine actually assumed a string in the incomplete ISO 8601 format and perfomed this:

    const dt = new Date('2017-12-12T00:00:00.000Z');
    

    It may still look "harmless" to you, but the date instance exposes the value not only in UTC, but also in the local time zone. If you construct the Date instance on the East Coast of the US, you will see the following output:

    > const dt = new Date('2017-12-12');
    > dt.toISOString()
    '2017-12-12T00:00:00.000Z'
    > dt.toString()
    'Tue Dec 11 2017 19:00:00 GMT-0500 (EST)'
    > d.toLocaleString()
    '12/11/2017 7:00:00 PM'
    

    Solution

    If you know, that format from date-fns reads date and time parts from the date instance in the local time zone, you will need to make your date "looking like" the midnight in your local time zone and not in UTC, which you passed to the Date constructor. Then you will see the year, month and date numbers preserved. It means, that you need to subtract the time zone offset of your local time zone for the specified day. Date.prototype.getTimezoneOffset returns the offset, but with an inverted sign and in minutes.

    const dt = new Date('2017-12-12');
    // Tue Dec 11 2017 19:00:00 GMT-0500 (EST)
    const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
    // Tue Dec 12 2017 00:00:00 GMT-0500 (EST)
    console.log(format(dtDateOnly, 'YYYY-MM-DD'));
    // Prints always "2017-12-12", regardless the time zone it executed in
    

    However, such Date instance can be used only to format the date-only value. You cannot use it for computing date differences, for example, which would need the original and correct UTC value.

    Alternative

    If you need always the same date-only format and not the format specific to the current locale, you do not need date-fns. You can format the string by the concatenation of padded numbers:

    const dt = new Date('2017-12-12');
    
    const year = dt.getUTCFullYear()
    const month = dt.getUTCMonth() + 1 // Date provides month index; not month number
    const day = dt.getUTCDate()
    
    // Print always "2017-12-12", regardless the time zone it executed in
    console.log(year + '-' + padToTwo(month) + '-', padToTwo(day));
    // Or use a template literal
    console.log(`${year}-${padToTwo(month)}-${padToTwo(day)}`);
    
    function padToTwo (number) {
      return number > 9 ? number : '0' + number
    }
    

    0 讨论(0)
提交回复
热议问题