Как лучше всего определять количество дней в месяце с помощью JavaScript?

107

Я использовал эту функцию, но хотел бы знать, какой способ ее получить наиболее эффективный и точный.

function daysInMonth(iMonth, iYear) {
   return 32 - new Date(iYear, iMonth, 32).getDate();
}
Чату
источник

Ответы:

200

function daysInMonth (month, year) { // Use 1 for January, 2 for February, etc.
  return new Date(year, month, 0).getDate();
}

console.log(daysInMonth(2, 1999)); // February in a non-leap year.
console.log(daysInMonth(2, 2000)); // February in a leap year.

День 0 - последний день предыдущего месяца. Поскольку конструктор месяца основан на 0, это прекрасно работает. Немного взлома, но в основном это то, что вы делаете, вычитая 32.

FlySwat
источник
42
этот синтаксис какое-то время сбивал меня с толку. Чтобы следовать паттерну JS, я рекомендую реализовать трюк следующим образом:return new Date(year, month + 1, 0).getDate();
fguillen
2
К сожалению, это не работает для дат до 1000 г. н.э., где вы можете правильно установить год только с помощью SetFullYear (). Чтобы сделать его пуленепробиваемым, используйте новую дату (2000+ (год% 2000), месяц, 0) .getDate ()
Ноэль Уолтерс,
4
Обратите внимание на мое будущее: уравнение fguillen с '+ 1' дает 28 дней, когда год - 2014, а месяц - 1 (что в JavaScript Date-object означает февраль). Наверное, хочу пойти с этим для наименьшего удивления. Но какая отличная идея от FlySwat!
Гарри Пехконен
@ NoelWalters - вы правы, что некоторые браузеры некорректно конвертируют двузначные годы в даты 20-го века (то есть даты до 100 г. н.э., а не 1000 г. н.э.), однако ваше исправление не исправляет это в этих браузерах. Единственный способ надежно установить две цифры года, чтобы использовать setFullYear : var d=new Date();d.setFullYear(year, month, date);.
RobG
1
Что делать , если monthесть 12 ? Разве Dateконструктор не должен принимать значение от 0 до 11 ?
anddero
8

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

Вот кеширующая версия ответа FlySwat :

var daysInMonth = (function() {
    var cache = {};
    return function(month, year) {
        var entry = year + '-' + month;

        if (cache[entry]) return cache[entry];

        return cache[entry] = new Date(year, month, 0).getDate();
    }
})();
дольмен
источник
6
Неужели внутренняя процедура C / C ++ настолько медленная, что требует кеширования?
Пит Элвин
1
@PeteAlvin Это зависит от реализации Date(поэтому универсального ответа на этот вопрос нет) и от того, как часто ваш код будет вызывать dayInMonthс одинаковыми значениями. Итак, единственный разумный ответ: профилируйте свой код и сравните его!
дольмен
1
Мне это нравится! Но вместо использования нового объекта cacheя использую localStorage.
Эндрю
5

В некоторых ответах (также на другие вопросы) были проблемы с високосным годом или использовался объект Date. Хотя javascript Date objectохватывает примерно 285616 лет (100000000 дней) по обе стороны от 1 января 1970 года, мне надоели всевозможные неожиданные несоответствия дат в разных браузерах (особенно с 0 по 99 год). Мне также было любопытно, как это рассчитать.

Поэтому я написал простой и, прежде всего, небольшой алгоритм для вычисления правильного ( Proleptic Gregorian / Astronomical / ISO 8601: 2004 (пункт 4.3.2.1), поэтому год0 существует и является високосным, и поддерживаются отрицательные годы ) количества дней для данный месяц и год.
Он использует алгоритм короткого замыкания bitmask-modulo leapYear (немного изменен для js) и общий алгоритм mod-8 месяцев.

Обратите внимание, что в AD/BCнотации год 0 AD / BC не существует: вместо этого год 1 BCявляется високосным!
ЕСЛИ вам нужно учесть нотацию BC, тогда просто сначала вычтите один год из (в противном случае положительного) годового значения !! (Или вычтите год из 1для дальнейших расчетов года.)

function daysInMonth(m, y){
  return m===2?y&3||!(y%25)&&y&15?28:29:30+(m+(m>>3)&1);
}
<!-- example for the snippet -->
<input type="text" value="enter year" onblur="
  for( var r='', i=0, y=+this.value
     ; 12>i++
     ; r+= 'Month: ' + i + ' has ' + daysInMonth(i, y) + ' days<br>'
     );
  this.nextSibling.innerHTML=r;
" /><div></div>

Обратите внимание, месяцы должны начинаться с 1!

Обратите внимание, что это другой алгоритм, чем поиск по магическому числу, который я использовал в своем Javascript, вычисляющий день года (1 - 366) , потому что здесь дополнительная ветка для високосного года нужна только для февраля.

GitaarLAB
источник
4

Чтобы устранить путаницу, я бы, вероятно, сделал строку месяца на основе, поскольку в настоящее время она основана на 1.

function daysInMonth(month,year) {
    monthNum =  new Date(Date.parse(month +" 1,"+year)).getMonth()+1
    return new Date(year, monthNum, 0).getDate();
}

daysInMonth('feb', 2015)
//28

daysInMonth('feb', 2008)
//29
Jaybeecave
источник
4

В moment.js вы можете использовать метод daysInMonth ():

moment().daysInMonth(); // number of days in the current month
moment("2012-02", "YYYY-MM").daysInMonth() // 29
moment("2012-01", "YYYY-MM").daysInMonth() // 31
artem_p
источник
2

Вот идет

new Date(2019,2,0).getDate(); //28
new Date(2020,2,0).getDate(); //29
Масум Биллах
источник
2

Синтаксис ES6

const d = (y, m) => new Date(y, m, 0).getDate();

возвращается

console.log( d(2020, 2) );
// 29

console.log( d(2020, 6) );
// 30
RASG
источник
1
При добавлении нового ответа к более старому вопросу с 14 существующими ответами действительно важно отметить, какой новый аспект вопроса касается вашего ответа. Это просто синтаксис ES6?
Джейсон Аллер,
понял. с этого момента добавит контекст к ответу.
RASG
1
function numberOfDays(iMonth, iYear) {
         var myDate = new Date(iYear, iMonth + 1, 1);  //find the fist day of next month
         var newDate = new Date(myDate - 1);  //find the last day
            return newDate.getDate();         //return # of days in this month
        }
Тони Ли
источник
1

С учетом високосных лет:

function (year, month) {
    var isLeapYear = ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);

    return [31, (isLeapYear ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}
Яш
источник
Ницца. Легко смотреть. Свойство [ertie] s для использования значения Inline-Map / Immediately-Keyed.
Коди,
1

Однострочное прямое вычисление (без объекта Date):

function daysInMonth(m, y) {//m is 1-based, feb = 2
   return 31 - (--m ^ 1? m % 7 & 1:  y & 3? 3: y % 25? 2: y & 15? 3: 2);
}

console.log(daysInMonth(2, 1999)); // February in a non-leap year
console.log(daysInMonth(2, 2000)); // February in a leap year

Вариация с нулевыми месяцами:

function daysInMonth(m, y) {//m is 0-based, feb = 1
   return 31 - (m ^ 1? m % 7 & 1:  y & 3? 3: y % 25? 2: y & 15? 3: 2);
}
Томас Лангкаас
источник
1

Если вам нужно количество дней в текущем месяце объекта Date, рассмотрите следующий метод:

Date.prototype.getNumberOfDaysInMonth = function(monthOffset) {
    if (monthOffset !== undefined) {
        return new Date(this.getFullYear(), this.getMonth()+monthOffset, 0).getDate();
    } else {
        return new Date(this.getFullYear(), this.getMonth(), 0).getDate();
    }
}

Тогда вы можете запустить его так:

var myDate = new Date();
myDate.getNumberOfDaysInMonth(); // Returns 28, 29, 30, 31, etc. as necessary
myDate.getNumberOfDaysInMonth(); // BONUS: This also tells you the number of days in past/future months!
mrplants
источник
1

Одной строкой:

// month is 1-12
function getDaysInMonth(year, month){
    return month == 2 ? 28 + (year % 4 == 0 ? (year % 100 == 0 ? (year % 400 == 0 ? 1 : 0) : 1):0) : 31 - (month - 1) % 7 % 2;
}
ЗЫ
источник
1

Может быть, немного убить по сравнению с выбранным ответом :) Но вот он:

function getDayCountOfMonth(year, month) {
  if (month === 3 || month === 5 || month === 8 || month === 10) {
    return 30;
  }

  if (month === 1) {
    if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
      return 29;
    } else {
      return 28;
    }
  }

  return 31;
};

console.log(getDayCountOfMonth(2020, 1));

Я нашел приведенный выше код здесь: https://github.com/ElemeFE/element/blob/dev/src/utils/date-util.js

function isLeapYear(year) { 
  return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); 
};

const getDaysInMonth = function (year, month) {
  return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
};

console.log(getDaysInMonth(2020, 1));

Я нашел приведенный выше код здесь: https://github.com/datejs/Datejs/blob/master/src/core.js

Сайед
источник
1

Если вы собираетесь передать переменную даты, это может быть полезно

const getDaysInMonth = date =>
  new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();

daysInThisMonth = getDaysInMonth(new Date());

console.log(daysInThisMonth);

Санка Санджива
источник
-1

Возможно, не самое элегантное решение, но простое для понимания и обслуживания; и это проверено в боях.

function daysInMonth(month, year) {
    var days;
    switch (month) {
        case 1: // Feb, our problem child
            var leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
            days = leapYear ? 29 : 28;
            break;
        case 3: case 5: case 8: case 10: 
            days = 30;
            break;
        default: 
            days = 31;
        }
    return days;
},
kmiklas
источник