Функции даты с високосными годами

#javascript

#javascript

Вопрос:

Я думал, что с помощью getTimezoneOffset будут учитываться високосные годы

 export const dateToDays = async (date: string): Promise<number | null> => {
  if (date === "") return null;
  const now: Date = new Date(date);
  const start: Date = new Date(now.getFullYear(), 0, 0);
  const diff =
     now -
     start  
    (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
  const oneDay = 1000 * 60 * 60 * 24;
  const day = Math.floor(diff / oneDay);
  if (!day) return null;
  return Number(day);
};
 

но когда я запускал тест на 29 февраля 1960 года (не високосный год, в феврале всего 28 дней), я ожидал, что он вернет значение null

 
test("Test Leap 1961", async () => {
  expect(await dateToDays("1961-02-29")).toEqual(null); // 61
});
 

в феврале 1960 года также нет 30 дней

 test("Test Leap 1960", async () => {
  expect(await dateToDays("1960-02-30")).toEqual(null); // 60
});
 

Как я могу гарантировать, что вышеуказанные 2 теста возвращают значение null? или, другими словами, как заставить функцию возвращать значение null, если запрашивается день, которого не существует?

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

1. Я не вижу здесь вопроса

2. 1960 год был високосным. Кроме того, new Date("1960-02-30") возвращается 1960-03-01T00:00:00.000Z так 1 марта.

3. да 1960 год был високосным но в феврале не было 30 дней

4. Но в некоторых реализациях конструктор даты переносится на следующий месяц. Вы можете сверить getDate() значение новой даты с прошедшим днем и вернуть null, если они не совпадают.

Ответ №1:

Поскольку проверка выполняется только для 1 <= month <= 12 и 1 <= day <= 31 вам нужно будет проверить, совпадает ли анализируемый месяц now.getMonth() 1 ( 1 потому что он показывает статистику 0 ) с месяцем, который был передан функции через date

Вы выполняете это с помощью следующей строки:

 if (parseInt(date.split('-')[1]) !== now.getMonth() 1) return null;
 
 const dateToDays = function(date) {
  if (date === "") return null;
  if (isNaN(new Date(date).getDate())) return null;
  const now = new Date(date);
  const start = new Date(now.getFullYear(), 0, 0);
  const diff =
     now -
     start  
    (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
  const oneDay = 1000 * 60 * 60 * 24;
  const day = Math.floor(diff / oneDay);
  if (!day) return null;

  // check if month is still the same
  // ----------------------------------------------------------------
  if (parseInt(date.split('-')[1]) !== now.getMonth() 1) return null;
  // ----------------------------------------------------------------

  return Number(day);
};

console.log(dateToDays('1960-2-28')); // Ok
console.log(dateToDays('1960-2-29')); // Ok
console.log(dateToDays('1961-2-29')); // Fail
console.log(dateToDays('1960-2-30')); // Fail
console.log(dateToDays('1960-2-31')); // Fail
console.log(dateToDays('1960-4-31')); // Fail
console.log(dateToDays('1960-2-31')); // Fail
console.log(dateToDays('1960-13-1')); // Fail
console.log(dateToDays('Hello :)')); // Fail 

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

 if(isNaN(new Date(date).getDate())) return null;
 

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

1. new Date не возвращает значение null, оно возвращает объект Date с valueOf() NaN или toString() значением ‘Недопустимая дата`

2. Добавлен случай, спасибо за предложение @pilchard

3. date.split('-') ограничивает формат даты ввода YYYY-MM-DD

4. Действительно @FilipSeman но я оставлю уточнение до OP

Ответ №2:

Обновите первое условие.

 if (date === "" || isMonthOwerflow(date)) {
    return null;
}

...
 

Пример new Date("1960-02-x") , где x может быть 1 31 для каждого месяца. Если день не существует, он будет перетекать в следующий месяц.


Если вы можете использовать библиотеки, то я рекомендую MomentJs , это в значительной степени отраслевой стандарт для задач, связанных с датой.

 console.log(moment().diff(moment('1960-02-28'), 'years')); // ok, 60
console.log(moment().diff(moment('1960-02-29'), 'years')); // ok, 60
console.log(moment().diff(moment('1960-02-30'), 'years')); // NaN
console.log(moment().diff(moment('1961-02-28'), 'years')); // ok, 59
console.log(moment().diff(moment('1961-02-29'), 'years')); // NaN 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script> 

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

1. new Date() возвращает действительную дату, перенесенную на следующий месяц для любых дней до 31 в Chrome и Node.