Как переместить дату и время в текущий часовой пояс при преобразовании строки даты и времени ISO 8601 в объект ** Date **? - PullRequest
0 голосов
/ 16 апреля 2020

Допустим, мы в Лондоне в полночь 2020-01-01 и сделаем запись в приложении, в котором дата и время хранятся в виде строки ISO-8601, например:
2020-01-01T00:00:00-00:00

Позже я нахожусь в Лос-Анджелесе и хочу просмотреть эту дату на графике, для которого требуется объект javascript.

Получить локализованный объект даты очень просто.

const iso8601Date = '2020-01-01T00:00:00+00:00';
const theDate = new Date(iso8601Date);

console.log(typeOf(theDate)); // date
console.log(theDate);        // Tue Dec 31 2019 16:00:00 GMT-0800 (PST)

Но иногда мы хотим "игнорировать" смещение часового пояса и анализировать данные, как если бы это произошло в текущем часовом поясе.

Это результат, который я ищу, но не знаю, как его выполнить sh.

const iso8601Date = '2020-01-01T00:00:00+00:00';
const theRepositionedDate = someMagic(iso8601Date);

console.log(typeOf(theRepositionedDate)); // date
console.log(theRepositionedDate);         // Wed Jan 01 2020 00:00:00 GMT-0800 (PST)

Как изменить положение даты и вернуть дату объект?

/* Helper function

Returns the object type
https://stackoverflow.com/a/28475133/25197
    typeOf(); //undefined
    typeOf(null); //null
    typeOf(NaN); //number
    typeOf(5); //number
    typeOf({}); //object
    typeOf([]); //array
    typeOf(''); //string
    typeOf(function () {}); //function
    typeOf(/a/) //regexp
    typeOf(new Date()) //date
*/

function typeOf(obj) {
  return {}.toString
    .call(obj)
    .split(' ')[1]
    .slice(0, -1)
    .toLowerCase();
}

Ответы [ 4 ]

2 голосов
/ 16 апреля 2020

Это действительно дубликат Почему Date.parse дает неверные результаты? , но это может показаться не очевидным на первый взгляд.

Первое правило из парсинга меток времени является «не использовать встроенный синтаксический анализатор», даже для 2 или 3 форматов, поддерживаемых ECMA-262.

Чтобы надежно проанализировать метку времени, вы должны знать формат. Встроенные парсеры пробуют и работают, поэтому между ними есть различия, которые могут привести к неожиданным результатам. Просто бывает, что '2020-01-01T00: 00: 00 + 00: 00', вероятно, является единственным поддерживаемым форматом, который фактически надежно анализируется. Но он отличается немного от строгого ISO 8601, и разные браузеры отличаются тем, насколько строго они применяют правила синтаксического анализа ECMAScript, поэтому очень легко ошибиться.

Вы можете преобразовать его в «локальная» временная метка, просто обрезав информацию о смещении, то есть «2020-01-01T00: 00: 00», однако Safari по крайней мере ошибается и все равно обрабатывает ее как UT C. Сам ECMAScrip несовместим с ISO 8601, рассматривая формы ISO 8601 только для даты как UT C (т. Е. '2020-01-01' как UT C, когда ISO 8601 говорит, что он должен рассматриваться как локальный).

Так что, просто напишите свой собственный парсер или используйте библиотеку, есть из чего выбирать. Если вам нужен только синтаксический анализ и форматирование, то некоторые из них будут сокращены менее чем на 2 тыс. (И есть примеров по SO ).

Написание собственного не так сложно, если вы просто хочу поддерживать форматы, похожие на ISO 8601, например

// Parse ISO 8601 timestamps in YYYY-MM-DDTHH:mm:ss±HH:mm format
// Optional "T" date time separator and
// Optional ":" offset hour minute separator
function parseIso(s, local) {
  let offset = (s.match(/[+-]\d\d:?\d\d$/) || [])[0];
  let b = s.split(/\D/g);
  // By default create a "local" date
  let d = new Date(
    b[0],
    b[1]-1,
    b[2] || 1,
    b[3] || 0,
    b[4] || 0,
    b[5] || 0
  );
  // Use offset if present and not told to ignore it
  if (offset && !local){
    let sign = /^\+/.test(offset)? 1 : -1;
    let [h, m] = offset.match(/\d\d/g);
    d.setMinutes(d.getMinutes() - sign * (h*60 + m*1) - d.getTimezoneOffset());
  }
  return d;
}

// Samples
['2020-01-01T00:00:00+00:00', // UTC, ISO 8601 standard
 '2020-01-01 00:00:00+05:30', // IST, missing T
 '2020-01-01T00:00:00-0400',  // US EST, missing T and :
 '2020-01-01 00:00:00',       // No timezone, local always
 '2020-01-01'                 // Date-only as local (differs from ECMA-262)
].forEach(s => {
  console.log(s);
  console.log('Using offset\n' + parseIso(s).toString());
  console.log('Ignoring offset\n' + parseIso(s, true).toString());
});
0 голосов
/ 22 апреля 2020

Опираясь на ответ @ RobG, я смог немного ускорить его, используя одно регулярное выражение. Публикация здесь для потомков.

const isoToDate = (iso8601, ignoreTimezone = false) => {
  // Differences from default `new Date()` are...
  // - Returns a local datetime for all without-timezone inputs, including date-only strings.
  // - ignoreTimezone processes datetimes-with-timezones as if they are without-timezones.
  // - Accurate across all mobile browsers.  https://stackoverflow.com/a/61242262/25197

  const dateTimeParts = iso8601.match(
    /(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{0,7}))?(?:([+-])(\d{2}):(\d{2}))?)?/,
  );

  // Create a "localized" Date by always specifying a time. If you create a date without specifying
  // time a date set at midnight in UTC Zulu is returned.  https://www.diigo.com/0hc3eb
  const date = new Date(
    dateTimeParts[1], // year
    dateTimeParts[2] - 1, // month (0-indexed)
    dateTimeParts[3] || 1, // day
    dateTimeParts[4] || 0, // hours
    dateTimeParts[5] || 0, // minutes
    dateTimeParts[6] || 0, // seconds
    dateTimeParts[7] || 0, // milliseconds
  );

  const sign = dateTimeParts[8];
  if (sign && ignoreTimezone === false) {
    const direction = sign === '+' ? 1 : -1;
    const hoursOffset = dateTimeParts[9] || 0;
    const minutesOffset = dateTimeParts[10] || 0;
    const offset = direction * (hoursOffset * 60 + minutesOffset * 1);
    date.setMinutes(date.getMinutes() - offset - date.getTimezoneOffset());
  }

  return date;
};

Ключевым отличием является одно регулярное выражение, которое возвращает все соответствующие группы одновременно.

Regular expression visualization

Вот regex101 с некоторыми примерами сопоставления / группировки.

Это примерно вдвое быстрее удивительного принятого ответа @ RobG и в 4-6 раз быстрее, чем пакеты moment.js и date-fns. ?

0 голосов
/ 16 апреля 2020

Но иногда мы хотим "игнорировать" смещение часового пояса и анализировать данные, как если бы это произошло в текущем часовом поясе.

Хорошо, а затем игнорировать его.

const iso8601Date = '2020-01-01T00:00:00+00:00';
const theDate = new Date(iso8601Date.substring(0, 19));

Это работает, потому что вы создаете объект Date из 2020-01-01T00:00:00 - дата-время ISO 8601 без смещения.

раздел ECMAScript 20.3.1.15 - Формат строки даты и времени говорит:

Когда смещение часового пояса отсутствует, формы только для даты интерпретируются как время UT C, а формы даты и времени интерпретируются как местное время .

0 голосов
/ 16 апреля 2020

const createDate = (isoDate) => {
  isoDate = new Date(isoDate)

  return new Date(Date.UTC(
    isoDate.getUTCFullYear(),
    isoDate.getUTCMonth(),
    isoDate.getUTCDate(),
    isoDate.getUTCMinutes(),
    isoDate.getUTCSeconds(),
    isoDate.getUTCMilliseconds()
  ));
}

const iso8601Date = '2020-01-01T00:00:00+00:00';
const theRepositionedDate = createDate(iso8601Date);

console.log(theRepositionedDate instanceof Date); // true
console.log(theRepositionedDate);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...