Jaki jest najlepszy sposób określenia liczby dni w miesiącu za pomocą JavaScript?

107

Używałem tej funkcji, ale chciałbym wiedzieć, jaki jest najbardziej skuteczny i dokładny sposób jej uzyskania.

function daysInMonth(iMonth, iYear) {
   return 32 - new Date(iYear, iMonth, 32).getDate();
}
Chatu
źródło

Odpowiedzi:

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.

Dzień 0 to ostatni dzień poprzedniego miesiąca. Ponieważ konstruktor miesiąca jest oparty na 0, działa to ładnie. Trochę hack, ale to w zasadzie to, co robisz, odejmując 32.

FlySwat
źródło
42
ta składnia od jakiegoś czasu mnie dezorientuje. Aby podążać za wzorcem JS, polecam zaimplementować taką sztuczkę:return new Date(year, month + 1, 0).getDate();
fguillen
2
Niestety nie udaje się to w przypadku dat sprzed 1000 rne, w których można poprawnie ustawić rok tylko za pomocą SetFullYear (). Aby uczynić go kuloodpornym, użyj nowej daty (2000+ (rok% 2000), miesiąc, 0) .getDate ()
Noel Walters
4
Uwaga dla mojej przyszłej jaźni: równanie Fguillena z „+ 1” daje 28 dni, gdy rok jest 2014, a miesiąc 1 (co w JavaScript Date-object oznacza luty). Prawdopodobnie chciałbym się z tym pogodzić dla najmniejszego zdziwienia. Ale co za wspaniały pomysł od FlySwat!
Harry Pehkonen,
@ NoelWalters - masz rację, że niektóre przeglądarki nieprawidłowo konwertują lata dwucyfrowe na daty w XX wieku (a więc daty sprzed 100 rne, a nie 1000 rne), jednak twoja poprawka nie naprawia tego w tych przeglądarkach. Jedynym sposobem, aby wiarygodnie ustalonych dwa lata cyfrowych jest użycie setFullYear : var d=new Date();d.setFullYear(year, month, date);.
RobG
1
A jeśli monthma 12 lat ? Czy Datekonstruktor nie powinien przyjmować wartości od 0 do 11 ?
anddero
8

Jeśli często wywołujesz tę funkcję, może być przydatne buforowanie wartości w celu uzyskania lepszej wydajności.

Oto wersja buforująca odpowiedzi 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
źródło
6
Czy wewnętrzna procedura C / C ++ jest tak powolna, że ​​wymaga buforowania?
Pete Alvin
1
@PeteAlvin Zależy to od implementacji Date(więc nie ma uniwersalnej odpowiedzi na to pytanie) i tego, jak często Twój kod będzie wywoływał metodę dayInMonthz tymi samymi wartościami. Więc jedyną rozsądną odpowiedzią jest: profiluj swój kod i testuj go!
dolmen
1
Lubię to! Ale zamiast używać nowego obiektu, cacheużywam localStorage.
andrew
5

Niektóre odpowiedzi (także na inne pytania) dotyczyły problemów z rokiem przestępnym lub wykorzystywały obiekt Date. Chociaż javascript Date objectobejmuje około 285616 lat (100 000 000 dni) po obu stronach 1 stycznia 1970 r., Miałem dość wszelkiego rodzaju nieoczekiwanych niespójności dat w różnych przeglądarkach (zwłaszcza w latach od 0 do 99). Byłem też ciekawy, jak to obliczyć.

Napisałem więc prosty, a przede wszystkim mały algorytm do obliczania poprawnej ( Proleptic Gregorian / Astronomical / ISO 8601: 2004 (punkt 4.3.2.1), więc rok0 istnieje i jest rokiem przestępnym, a lata ujemne są obsługiwane ) liczby dni dla danego miesiąca i roku.
Wykorzystuje algorytm zwarcia bitmask-modulo leapYear (nieznacznie zmodyfikowany dla js) i wspólny algorytm mod-8 miesięcy.

Zauważ, że w AD/BCnotacji rok 0 AD / BC nie istnieje: zamiast tego rok 1 BCjest rokiem przestępnym!
JEŚLI musisz uwzględnić notację BC, po prostu odejmij najpierw jeden rok (w przeciwnym razie dodatniej) wartości roku !! (Lub odejmij rok od 1do dalszych obliczeń).

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>

Uwaga, miesiące muszą być liczone od 1!

Zauważ, że jest to inny algorytm niż magiczne wyszukiwanie liczb, których użyłem w moim JavaScript, aby obliczyć dzień roku (1 - 366) odpowiedź, ponieważ tutaj dodatkowa gałąź dla roku przestępnego jest potrzebna tylko dla lutego.

GitaarLAB
źródło
4

Aby wyeliminować zamieszanie, prawdopodobnie utworzyłbym ciąg miesiąca na podstawie, ponieważ obecnie jest oparty na 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
źródło
4

Z moment.js możesz użyć metody 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
źródło
2

Oto idzie

new Date(2019,2,0).getDate(); //28
new Date(2020,2,0).getDate(); //29
Masum Billah
źródło
2

Składnia ES6

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

zwroty

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

console.log( d(2020, 6) );
// 30
RASG
źródło
1
Dodając nową odpowiedź do starszego pytania z 14 istniejącymi odpowiedziami, naprawdę ważne jest, aby zwrócić uwagę, jakiego nowego aspektu pytania dotyczy twoja odpowiedź. Czy to tylko składnia ES6?
Jason Aller
Rozumiem. doda kontekst do odpowiedzi od teraz.
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
źródło
1

Biorąc pod uwagę lata przestępne:

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
źródło
Miły. Łatwe do obejrzenia. Propozycje [ertie] do użycia mapy wbudowanej / wartości z kluczem natychmiastowym.
Cody
1

Bezpośrednie obliczenia jednowierszowe (bez obiektu 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

Odchylenie z miesiącami od 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
źródło
1

Jeśli chcesz określić liczbę dni w bieżącym miesiącu obiektu Date, rozważ następującą metodę:

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

Następnie możesz uruchomić to w ten sposób:

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
źródło
1

W jednej linii:

// 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
źródło
1

Może być nieco przesadzone w porównaniu z wybraną odpowiedzią :) Ale oto jest:

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

Powyższy kod znalazłem tutaj: 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));

Powyższy kod znalazłem tutaj: https://github.com/datejs/Datejs/blob/master/src/core.js

Syed
źródło
1

Jeśli zamierzasz przekazać zmienną daty, może to być pomocne

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

daysInThisMonth = getDaysInMonth(new Date());

console.log(daysInThisMonth);

sanka sanjeeva
źródło
-1

Być może nie jest to najbardziej eleganckie rozwiązanie, ale łatwe do zrozumienia i utrzymania; i jest przetestowany w walce.

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
źródło