Same date in all timezones

1.2k Views Asked by At

I have a problem showing the same date in all timezones. Users input is for example 01-01-2002 and I store it like a date with Eureope/Berlin timezone parseFromTimeZone(String(birthDate), { timeZone: 'Europe/Berlin' }) and the result of parseFromTimeZone is this string '2001-12-31T23:00:00.000Z'. String date counts with timezone in Berlin that is why it is shifted for one hour.

And I need to get from '2001-12-31T23:00:00.000Z' this 01-01-2002 in all timezones.

I using formatISO(new Date(date), { representation: 'date' })) this returns 01-01-2002 when my timezone is Europe/Prague or Europe/Berlin

but when I change the timezone to America/Tijuana then formatISO returns 2001-12-31 and that is wrong I need to have the same date as is in Europe/Berlin always! Bud for Asia/Tokyo this function returns 01-01-2002 that is right ...

Some ideas? I have tried a lot of solutions but none works for all timezones...

I am using "date-fns": "^2.15.0", "date-fns-timezone": "^0.1.4"

2

There are 2 best solutions below

0
On BEST ANSWER

The Date object, despite its name, is does not represent a "date". It represents a timestamp. All that it stores internally is the number of milliseconds since the Unix epoch (which is UTC based). It outputs values based on either UTC or the local time zone of the machine where its running, depending on the function being called.

Thus, if you construct a Date object from a date-only value, you're really taking "the time at midnight" from that time zone and adjusting it to UTC. This is demonstrated by your example of 2002-01-01 in Europe/Berlin. Your treating that as 2002-01-01T00:00:00.000+01:00, which indeed has a UTC equivalent of 2001-12-31T23:00:00.000Z, and thus doesn't carry the same year, month, and day elements as the intended time zone.

You really only have two options to deal with date-only values if you want to prevent them from shifting:

  • Your first option is to use the Date object, but treat the input as UTC and only use the UTC-based functions. For example:

    var dt = new Date(Date.UTC(2002, 0, 1));  // "2002-01-01T00:00:00.000Z"
    
    var y = dt.getUTCFullYear();  // 2002
    var m = dt.getUTCMonth() + 1; // 1
    var d = dt.getUTCDate();      // 1
    
    var dtString = d.toISOString().substring(0, 10) // "2002-01-01"
    
    • If you need to parse a date string, be aware that current ECMAScript spec treats date-only values as UTC (which is what you want), but in the past such behavior was undefined. Thus some browsers might create a local-time result from new Date('2002-01-01'). You may want to explicitly add the time and Z, as in new Date('2002-01-01' + 'T00:00:00.000Z') to be on the safe side.

    • If you intend to use date-fns, be careful - the parseISO and formatISO functions use local time, not UTC.

  • The second option is to not use the Date object. Keep the dates in their string form (in ISO 8601 yyyy-mm-dd format), or keep them in a different object of either your own construction or from a library.

The ECMAScript TC39 Temporal proposal is intended to fix such deficiencies in JavaScript. In particular, the Temporal.Date object (preliminary name) will be able to be used for date-only values without having the shifting problem you and so many others have encountered. The proposal is currently in Stage 2 of the ECMAScript process, so it's not available to use today, but this problem will be solved eventually!

0
On

Try this function with an ISO_8601 date, then change the timezone in your computer's settings and try again with the new timezone. It should print the same date on your web page for both time zones.

getDateFromISO(iso_string: string): string | Date {

    if (!iso_string)
        return null;


    const isIsoDate = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(iso_string); // check if string is in format 2022-01-01T00:00:00.000Z
    const isDateTimeWithoutZone = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(iso_string); // check if string is in format 2022-01-01T00:00:00
    const isDateYMD = /\d{4}-\d{2}-\d{2}/.test(iso_string); // check if string is in format 2022-01-01

    if (!isIsoDate && isDateTimeWithoutZone)
        iso_string += '.000Z';
    else if (!isIsoDate && isDateYMD)
        iso_string += 'T00:00:00.000Z';
    else if (isIsoDate)
        iso_string = iso_string;
    else
        return iso_string;


    const dateFromServer = new Date(iso_string);
    const localOffset = new Date().getTimezoneOffset(); // in minutes
    const localOffsetMillis = 60 * 1000 * localOffset;
    const localDate = new Date(dateFromServer.getTime() + localOffsetMillis);

    return localDate;

 }