Одна и та же дата во всех часовых поясах

#javascript #react-native #timezone #date-fns

#javascript #react-native #Часовой пояс #дата-fns

Вопрос:

У меня проблема с отображением одной и той же даты во всех часовых поясах. Вводимые пользователем данные, например, 01-01-2002, и я сохраняю их как дату с Eureope/Berlin часовым parseFromTimeZone(String(birthDate), { timeZone: 'Europe/Berlin' }) поясом, а результатом parseFromTimeZone является эта строка '2001-12-31T23:00:00.000Z' . Строковая дата считается с часовым поясом в Берлине, поэтому она сдвинута на один час.

И мне нужно получить от '2001-12-31T23:00:00.000Z' этого 01-01-2002 во всех часовых поясах.

Я использую formatISO(new Date(date), { representation: 'date' })) это 01-01-2002 , когда мой часовой пояс равен Europe/Prague или Europe/Berlin

но когда я меняю часовой пояс на America/Tijuana then formatISO возвращает 2001-12-31 , и это неправильно, мне нужно иметь ту же дату, Europe/Berlin что и всегда! Bud для Asia/Tokyo этой функции возвращает 01-01-2002 правильное значение …

Какие-нибудь идеи? Я перепробовал много решений, но ни одно из них не работает для всех часовых поясов…

Я использую "date-fns": "^2.15.0" , "date-fns-timezone": "^0.1.4"

Ответ №1:

Попробуйте использовать эту функцию с датой ISO_8601, затем измените часовой пояс в настройках вашего компьютера и повторите попытку с новым часовым поясом. Он должен печатать одну и ту же дату на вашей веб-странице для обоих часовых поясов.

 getDateFromISO(iso_string: string): string | Date {

    if (!iso_string)
        return null;


    const isIsoDate = /d{4}-d{2}-d{2}Td{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}Td{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 amp;amp; isDateTimeWithoutZone)
        iso_string  = '.000Z';
    else if (!isIsoDate amp;amp; 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;

 }
  

Ответ №2:

Date Объект, несмотря на его имя, не представляет «дату». Он представляет временную метку. Все, что он хранит внутри, — это количество миллисекунд, прошедших с эпохи Unix (которая основана на UTC). Он выводит значения, основанные либо на UTC, либо на местном часовом поясе компьютера, на котором он запущен, в зависимости от вызываемой функции.

Таким образом, если вы создаете Date объект из значения только для даты, вы действительно берете «время в полночь» из этого часового пояса и настраиваете его на UTC. Это демонстрируется вашим примером 2002-01-01 in Europe/Berlin . Вы рассматриваете это как 2002-01-01T00:00:00.000 01:00 , который действительно имеет эквивалент UTC 2001-12-31T23:00:00.000Z и, следовательно, не содержит те же элементы года, месяца и дня, что и предполагаемый часовой пояс.

На самом деле у вас есть только два варианта обработки значений только для даты, если вы хотите предотвратить их смещение:

  • Ваш первый вариант — использовать Date объект, но обрабатывать входные данные как UTC и использовать только функции на основе UTC. Например:

     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"
      
    • Если вам нужно проанализировать строку даты, имейте в виду, что текущая спецификация ECMAScript обрабатывает значения только для даты как UTC (что вам и нужно), но в прошлом такое поведение не было определено. Таким образом, некоторые браузеры могут создавать результат по местному времени из new Date('2002-01-01') . Возможно, вы захотите явно добавить время и Z , например, new Date('2002-01-01' 'T00:00:00.000Z') на всякий случай.

    • Если вы собираетесь использовать date-fns, будьте осторожны — функции parseISO и formatISO используют местное время, а не UTC.

  • Второй вариант — не использовать Date объект. Сохраняйте даты в их строковой форме (в формате ISO 8601 гггг-мм-дд) или храните их в другом объекте либо вашей собственной конструкции, либо из библиотеки.

Временное предложение ECMAScript TC39 предназначено для устранения таких недостатков в JavaScript. В частности, Temporal.Date объект (предварительное имя) можно будет использовать для значений только для даты без проблем со сдвигом, с которыми столкнулись вы и многие другие. В настоящее время предложение находится на стадии 2 процесса ECMAScript, поэтому сегодня оно недоступно для использования, но эта проблема будет решена в конечном итоге!