Qual é a melhor maneira de determinar o número de dias em um mês com JavaScript?

107

Tenho usado essa função, mas gostaria de saber qual é a maneira mais eficiente e precisa de obtê-la.

function daysInMonth(iMonth, iYear) {
   return 32 - new Date(iYear, iMonth, 32).getDate();
}
Chatu
fonte

Respostas:

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.

O dia 0 é o último dia do mês anterior. Como o construtor de mês é baseado em 0, isso funciona bem. Um pouco hack, mas é basicamente isso que você está fazendo subtraindo 32.

FlySwat
fonte
42
esta sintaxe tem me confundido por um tempo. Para seguir o padrão JS, recomendo implementar o truque como este:return new Date(year, month + 1, 0).getDate();
fguillen
2
Infelizmente, isso falha para datas anteriores a 1000 DC, onde você só pode definir o ano corretamente usando SetFullYear (). Para torná-lo à prova de balas, use a nova data (2000+ (ano% 2000), mês, 0) .getDate ()
Noel Walters
4
Nota para o meu futuro: a equação de fguillen com o '+ 1' dá 28 dias quando o ano é 2014 e o mês é 1 (que, em JavaScript Date-object, significa fevereiro). Provavelmente quer ir com isso, pelo menos para espanto. Mas que grande ideia do FlySwat!
Harry Pehkonen
@ NoelWalters — você está correto que alguns navegadores convertem incorretamente anos de dois dígitos em datas no século 20 (então datas anteriores a 100 DC, não 1000 DC), no entanto, sua correção não corrige isso nesses navegadores. A única maneira de definir de forma confiável dois anos dígito é usar setFullYear : var d=new Date();d.setFullYear(year, month, date);.
RobG
1
E se monthfor 12 ? O Dateconstrutor não deveria ter um valor de 0 a 11 ?
anddero de
8

Se você chamar essa função com frequência, pode ser útil armazenar o valor em cache para melhor desempenho.

Aqui está a versão em cache da resposta do 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();
    }
})();
dolmen
fonte
6
A rotina interna C / C ++ é tão lenta que exige armazenamento em cache?
Pete Alvin
1
@PeteAlvin Depende da implementação de Date(portanto, não há uma resposta universal para essa pergunta) e da frequência com que seu código chamará o dayInMonthcom os mesmos valores. Portanto, a única resposta sensata é: analise o seu código e faça um benchmark!
Dolmen
1
Eu gosto disso! Mas em vez de usar um novo objeto cache, eu uso localStorage.
andrew
5

Algumas respostas (também em outras questões) tiveram problemas de ano bissexto ou usaram o objeto Data. Embora o javascript Date objectcubra aproximadamente 285616 anos (100 milhões de dias) em ambos os lados de 1º de janeiro de 1970, eu estava farto de todos os tipos de inconsistências de data inesperadas em diferentes navegadores (principalmente do ano 0 a 99). Também estava curioso para saber como calculá-lo.

Então eu escrevi um algoritmo simples e, acima de tudo, pequeno para calcular o número correto de dias ( Proléptico Gregoriano / Astronômico / ISO 8601: 2004 (cláusula 4.3.2.1), então ano0 existe e é um ano bissexto e anos negativos são suportados ) número de dias para um determinado mês e ano.
Ele usa o algoritmo bitmask-módulo leapYear de curto-circuito (ligeiramente modificado para js) e o algoritmo comum mod-8 meses.

Observe que, em AD/BCnotação, o ano 0 AD / BC não existe: em vez disso, o ano 1 BCé o ano bissexto!
SE você precisar contabilizar a notação BC, simplesmente subtraia um ano do valor do ano (caso contrário positivo) primeiro !! (Ou subtraia o ano 1para cálculos de ano adicionais.)

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>

Nota, os meses devem ser baseados em 1!

Observe, este é um algoritmo diferente da pesquisa de número mágico que usei no meu Javascript para calcular o dia do ano (1 - 366) , porque aqui o ramo extra para o ano bissexto é necessário apenas para fevereiro.

GitaarLAB
fonte
4

Para tirar a confusão, eu provavelmente faria a string do mês com base, pois atualmente é baseada em 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
fonte
4

Com moment.js, você pode usar o método 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
fonte
2

Aqui vai

new Date(2019,2,0).getDate(); //28
new Date(2020,2,0).getDate(); //29
Masum Billah
fonte
2

Sintaxe ES6

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

retorna

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

console.log( d(2020, 6) );
// 30
RASG
fonte
1
Ao adicionar uma nova resposta a uma pergunta mais antiga com 14 respostas existentes, é realmente importante observar qual novo aspecto da pergunta sua resposta aborda. É apenas a sintaxe ES6?
Jason Aller
Entendi. irá adicionar contexto à resposta de agora em diante.
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
        }
Tony Li
fonte
1

Considerando os anos bissextos:

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];
}
Yash
fonte
Agradável. Fácil de olhar. Prop [ertie] s para o uso de um Inline-Map / Immediately-Keyed Value.
Cody
1

Cálculo direto de uma linha (sem objeto 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

Variação com meses baseados em 0:

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);
}
Tomas Langkaas
fonte
1

Se você quiser o número de dias no mês atual de um objeto Date, considere o seguinte método:

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();
    }
}

Então você pode executá-lo assim:

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
fonte
1

Em uma única linha:

// 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;
}
Shl
fonte
1

Pode ser um pouco matador em comparação com a resposta selecionada :) Mas aqui está:

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));

Encontrei o código acima aqui: 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));

Encontrei o código acima aqui: https://github.com/datejs/Datejs/blob/master/src/core.js

Syed
fonte
1

Se você vai passar uma variável de data, isso pode ser útil

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

daysInThisMonth = getDaysInMonth(new Date());

console.log(daysInThisMonth);

sanka sanjeeva
fonte
-1

Talvez não seja a solução mais elegante, mas fácil de entender e manter; e é testado em batalha.

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
fonte