Сравнение диапазонов дат в формате ISO

#javascript #reactjs #iso8601 #daterangepicker

#javascript #reactjs #iso8601 #daterangepicker

Вопрос:

Ссылка на codeSandox

У меня есть два диапазона дат, один из API, а другой из пользовательского ввода. Оба находятся в формате ISO.

диапазон дат из API:

 dateStart 2019-04-01T03:04:00Z
dateStop 2019-04-01T03:05:00Z
  

диапазон дат из пользовательского ввода:

 convertedDateFrom 2020-09-15T18:30:00.000Z
convertedDateTo 2020-09-21T18:30:00.000Z
  

Я хочу преобразовать date range from user input в date range from API . Как я могу этого добиться?

 EXPECTED: I want to compare the values two date-ranges and depending on that 
I will perform certain export functionality.
    The user input date-range could 
     - fall completely within the date-range of the API
    - or at least one of the date values could fall or overlap within the 
      date-range from the API.
  

должен перекрывать диапазон дат из API.

это мой date range picker handle()

 handleDatePickerChange = (setSelectedDayRange) => {
    console.log("initializing handleDatePickerChange()");
    console.log("setSelectedDayRange", setSelectedDayRange);
    // TODO
    // convert the dates
    let convertedDateFrom = moment(setSelectedDayRange.from).toISOString();
    console.log("convertedDateFrom", convertedDateFrom);

    let convertedDateTo = moment(setSelectedDayRange.to).toISOString();
    console.log("convertedDateTo", convertedDateTo);

    // compare dates
    // if(convertedDateFrom ===  )
    // check if data exists

    this.setState({
      selectedDayRange: setSelectedDayRange,
    });
  };
  

Комментарии:

1. Каков ваш ожидаемый результат здесь? Это непонятно!

2. @AlwaysHelping ohk. приношу извинения за это. Я обновил описание ожидаемым результатом

Ответ №1:

Чао, ты мог бы использовать функцию isBetween , предоставляемую moment таким образом:

 // interval comes from API
let dateAPIFrom = moment().toISOString();
let dateAPITo = moment().add(2, "days").toISOString();

// user date interval
let convertedDateFrom = moment(setSelectedDayRange.from).toISOString();
let convertedDateTo = moment(setSelectedDayRange.to).toISOString();

if (
  moment(convertedDateFrom)
    .subtract(1, "month")
    .isBetween(dateAPIFrom, dateAPITo) amp;amp;
  moment(convertedDateTo)
    .subtract(1, "month")
    .isBetween(dateAPIFrom, dateAPITo)
) {

  //The user input date-range fall completely within the date-range of the API

} else if (
  moment(convertedDateFrom)
    .subtract(1, "month")
    .isBetween(dateAPIFrom, dateAPITo) ||
  moment(convertedDateTo)
    .subtract(1, "month")
    .isBetween(dateAPIFrom, dateAPITo)
) {

  //or at least one of the date values could fall or overlap within the date-range from the API.
 
}
  

.subtract(1, "month") потому что moment({day: 19, month: 8, year: 2020}).toISOString() возвращает всегда month 1 .

Здесь ваш codesandbox изменен.

Комментарии:

1. @Giovani спасибо. по какой-то причине, но условия if, похоже, не выполняются.

2. @NaumanTanwir да, вы правы. Это происходит потому, что .toIsoString возвращает всегда month 1 , поэтому вам нужно .subtract(1, "month") convertedDateFrom и convertedDateTo , и это работает. Я обновил codesandbox. Теперь я обновляю свой ответ. Извините за это 🙂

3. @LionelRowe moment(moment({day: 19, month: 8, year: 2020}).toISOString()) возвращает 9 (мы говорим о моменте, но проблема не в моменте, проблема в выводе react-modern-calendar-datepicker !!!)

4. Я обновил свой ответ: это из-за react-modern-calendar-datepicker . DatePicker возвращает объект, подобный {day: 19, month: 8, year: 2020} и если вы передадите этот объект в moment, moment вернет 19/9/2020, а не 19/8/2020, как ожидалось. Итак, я добавил subtract(1, "month") .

5. @GiovanniEsposito Я удалил свои комментарии, поскольку они были вызваны моим незнанием react-modern-calendar-datepicker . Похоже, что это ошибка в datepicker, а не moment, как вы упомянули.

Ответ №2:

Это обе даты стандарта ISO-8601, которые могут быть легко преобразованы в собственные Date объекты, которые могут быть преобразованы в количество миллисекунд с эпохи Unix. Вам не нужна какая-либо сложная логика или даже использовать moment для этого.

 /**
 * If result is negative, the first date is earlier
 * If result is positive, the second date is earlier
 * If result is 0, both dates are exactly the same
 */
const compareIsoDates = (isoString1, isoString2) => {
  return new Date(isoString1).valueOf() - new Date(isoString2).valueOf()
}

// result is -46106760000, meaning first date is earlier
console.log(compareIsoDates('2019-04-01T03:04:00Z', '2020-09-15T18:30:00.000Z'))

/**
 * strictly between (cannot be the same as start or end)
 * if you want to allow same as start and end, change to
 * >= and <= instead of > and <
 */
const isStrictlyBetween = (targetDate, [startDate, endDate]) => {
  return compareIsoDates(targetDate, startDate) > 0
    amp;amp; compareIsoDates(targetDate, endDate) < 0
}

// true
console.log(isStrictlyBetween(
  '2020-05-01T00:00:00.000Z',
  ['2020-04-20T18:30:00Z', '2020-05-10T00:00:00Z']
))

// you can also use `compareIsoDates` a sort function to sort an array of
// ISO strings in ascending order (earliest to latest)
console.log([
  "1998-02-12T08:18:27.991Z",
  "2005-03-19T19:48:59.501Z",
  "1997-05-01T14:58:13.848Z",
  "2008-08-31T01:30:11.880Z",
  "2004-08-05T16:07:55.443Z"
].sort(compareIsoDates))  

Ответ №3:

Отличная особенность форматов даты ISO в том, что они сортируются по алфавиту.

Это означает, что пока вы можете преобразовать его в Date / moment / etc. вы получите точно такие же результаты, как при сравнении их в виде строк.

Вы можете написать, например, compareISODates функцию Лайонела как:

 /**
 * If result is negative, the first date is earlier
 * If result is positive, the second date is earlier
 * If result is 0, both dates are exactly the same
 */
const compareISODates = (isoString1, isoString2) => {
  return isoString1.localeCompare(isoString2);
};
  

Или в качестве другого примера, convertedDateFrom/To полностью подпадающего под dateStart/End , вы можете просто проверить, является ли

 convertedDateFrom >= dateStart amp;amp; convertedDateTo <= dateEnd
  

Конечно, если вам нужна более сложная логика, вы должны использовать одну из библиотек дат, упомянутых выше.

Комментарии:

1. При таком подходе вы столкнетесь с проблемами с часовыми поясами, отличными от Z (включая часовой пояс 00:00 , что означает то же самое, что Z ), и при сравнении дат, которые имеют / не имеют дробные доли секунды (как показано в разнице между форматом API и форматом пользовательского ввода в OP). Пример: '2010-10-10T10:10:10.000Z'.localeCompare('2010-10-10T10:10:10Z') возвращает -1 , но вы хотите 0 .