任何月份的天数

时间:2009-11-27 23:26:22

标签: javascript date

如何编写一个JavaScript函数,该函数接受1到12之间代表一年中几个月的数字,然后返回该月份的天数?

11 个答案:

答案 0 :(得分:51)

function getDaysInMonth(m, y) {
   return /8|3|5|10/.test(--m)?30:m==1?(!(y%4)&&y%100)||!(y%400)?29:28:31;
}

答案 1 :(得分:45)

试试这个:

function numberOfDays(year, month) {
    var d = new Date(year, month, 0);
    return d.getDate();
}

由于闰年,你也需要过年。

答案 2 :(得分:27)

九月三十天, 四月,六月和十一月;
其余的都有三十一,
仅在2月份之外, 哪个有二十八天清楚,
每个闰年都有29个。

<3 Wikipedia

答案 3 :(得分:13)

爱詹姆斯的回答。对那些感兴趣的人稍微重新格式化。

function getDaysInMonth(m, y)
{
    // months in JavaScript start at 0 so decrement by 1 e.g. 11 = Dec
    --m;

    // if month is Sept, Apr, Jun, Nov return 30 days
    if( /8|3|5|10/.test( m ) ) return 30;

    // if month is not Feb return 31 days
    if( m != 1 ) return 31;

    // To get this far month must be Feb ( 1 )
    // if the year is a leap year then Feb has 29 days
    if( ( y % 4 == 0 && y % 100 != 0 ) || y % 400 == 0 ) return 29;

    // Not a leap year. Feb has 28 days.
    return 28;
}

小提琴here

答案 4 :(得分:7)

每个人都知道,在月的任何一天,计算Chuck's knuckle sandwich几乎都只是诗歌。

Chuck Norris' Fist. Count the knucle's.

如果你无法编译和运行(如诗歌),请继续阅读。


没有正则表达式和减去2 modulo 余数运算,也没有闰年问题或日期对象。
虽然javascript的Date object涵盖了1970年1月1日左右的大约285616年(100,000,000天),但我厌倦了不同浏览器中的各种意外日期inconsistencies(最明显的是0到99年)。我也很好奇如何计算它。

所以我写了一个简单的,最重要的,算法(很容易击败James的答案)来计算正确Proleptic Gregorian / Astronomical / ISO 8601:2004(第4.3.2.1节),因此year 0存在并且是闰年,支持负数年)给定月份和年份的天数。<登记/> 它使用short-circuit bitmask-modulo leapYear algorithm(略微修改为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" placeholder="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 calculate the day of the year (1 - 366)答案中使用的幻数查找,因为这里闰年的额外分支仅在二月份才需要。


编辑( - 历史):
我解除了修改后的mod8月算法

return(m===2?y&3||!(y%25)&&y&15?28:29:30)+(m+(m>>3)&1);   //algo 1

到三元并移除了现在不需要的外括号(好的电话,TrueBlueAussie):

return m===2?y&3||!(y%25)&&y&15?28:29:30+(m+(m>>3)&1);    //algo 2

经过严格的测试后,这被证明是最快的算法(感谢TrueBlueAussie对于我为缓存jsperf设置而进行的测试)。我们猜测这比我的(更短且看似更快)算法3(下面的幻数位按位查找)更快的原因是,现代浏览器可能可以预先优化m>>3中的常量位移。

我想......好吧......“为什么我不再这样做?:”

return m===2?y&3||!(y%25)&&y&15?28:29:30+(5546>>m&1);     // algo 3

它使用一个“幻数”来执行偏移的简单逐位查找:

DNOSAJJMAMFJ* = Months (right to left)
CBA9876543210 = Month === bit position in hex (we never use pos 0: January is 1)
1010110101010 = offset from 30 = binary === 5546 decimal

13位小于31位,因此我们可以安全地在bitshift上保存另一个字符而不是>>>(因为我们不需要强制无符号32位)。

这消除了一个内存调用(var m),一个加法和一个优先级! (这是一个较短的字符)

有人会认为:显然这3个额外的优化击败了我的第一个/第二个算法(正如TrueBlueAussie评论的那样,已经是最快的了)...... 但正如我已经提到的,事实证明这个(算法3)在现代浏览器上并不快(我知道,相当意外),我们认为这是因为引擎无法再优化比特移位。
我会把它留在这里,也许有一天更快,谁知道......

事实证明,我的算法2是最快的全部(除了TrueBlueAussie的完整2D阵列当然,虽然这需要更多的内存,但仍然需要快速的算法来构建它客户端-side),我按照TrueBlueAussie的建议将我的答案恢复到使用我的算法2.

我仍然进行了一次爆炸式合作,并感谢我重新回答我的答案!

答案 5 :(得分:5)

在计算机术语中,new Date()regular expression解决方案慢!如果你想要一个超快速(超级神秘)的单行,请试试这个(假设m根据问题采用Jan=1格式):

唯一真正的速度竞争来自@GitaarLab,所以我创建了一个头对头的JSPerf供我们测试:http://jsperf.com/days-in-month-head-to-head/5

我一直在尝试不同的代码更改以获得最佳性能。

当前版本

在查看相关问题Leap year check using bitwise operators (amazing speed)并发现25&amp; 15个神奇的数字代表,我已经想出了这个优化的答案混合:

function getDaysInMonth(m, y) {
    return m===2 ? y & 3 || !(y % 25) && y & 15 ? 28 : 29 : 30 + (m +(m >> 3) & 1);
}

JSFiddle: http://jsfiddle.net/TrueBlueAussie/H89X3/22/

JSPerf结果: http://jsperf.com/days-in-month-head-to-head/5

出于某种原因,(m+(m>>3)&1)几乎所有浏览器上的(5546>>m&1)更有效。


以前的版本:

这个通过反转值(略微增加)删除了一个!测试:

function getDaysInMonth(m, y) {
    return m === 2 ? (y % 4 || !(y % 100) && (y % 400)) ? 28 : 29 : 30 + (m + (m >> 3) & 1);
}

这个删除了任何不必要的括号:

function getDaysInMonth2(m, y) {
    return m === 2 ? !(y % 4 || !(y % 100) && (y % 400)) ? 29 : 28 : 30 + (m + (m >> 3) & 1);
}

这个比{1}更快一点(+

^

这是我最初的抨击:

function getDaysInMonth(m, y) {
    return (m === 2) ? (!((y % 4) || (!(y % 100) && (y % 400))) ? 29 : 28) : 30 + ((m + (m >> 3)) & 1);
}

它基于我的闰年答案:javascript to find leap year此答案Leap year check using bitwise operators (amazing speed)以及以下二进制逻辑。


二元月份的快速课程:

如果您在二进制中解释所需月份(Jan = 1)的索引,您会注意到31天的月份有3位清除和位0设置,或位3设置和位0清楚。

function getDaysInMonth(m, y) {
    return m == 2 ? (!((y % 4) || (!(y % 100) && (y % 400))) ? 29 : 28) : (30 + ((m >> 3 ^ m) & 1));
}

这意味着您可以使用Jan = 1 = 0001 : 31 days Feb = 2 = 0010 Mar = 3 = 0011 : 31 days Apr = 4 = 0100 May = 5 = 0101 : 31 days Jun = 6 = 0110 Jul = 7 = 0111 : 31 days Aug = 8 = 1000 : 31 days Sep = 9 = 1001 Oct = 10 = 1010 : 31 days Nov = 11 = 1011 Dec = 12 = 1100 : 31 days 移动值3个位置,将原始>> 3的位置转换为XOR,然后查看结果是^ m还是1 在位置0 使用0。注意:结果& 1略快于XOR(+),^在位0中得到相同的结果。

JSPerf结果http://jsperf.com/days-in-month-perf-test/6(比接受的答案快23倍)。


更新:我对前两个答案+最新(@ James,@ Caleb&amp; @GitaarLAB)进行了比较,以确保它们提供一致的结果,并且所有4个都返回相同的值从第1年到第4000年的所有年份的所有月份:http://jsfiddle.net/TrueBlueAussie/8Lmpnpz4/6/。除了@Caleb之外,所有年份的0年都相同。

另一次更新:

如果绝对速度是唯一的目标,并且你不介意浪费记忆,那么将结果存储在给定的年限内,在二维表中可能是最快的方式:

(m >> 3) + m

JSPerf: http://jsperf.com/days-in-month-head-to-head/5

答案 6 :(得分:1)

本着没有为你做功课的精神,我在很多年前的POVRay(抱歉,不是JS)中提出了一个版本。

在POVRay中,没有布尔变量。我想出的方法是在'm'中创建一个多项式,给出答案&gt; 0个月,31天,&lt; 0个月,30天。

#declare m0 = (m-0.5)*(m-1.5)*(m-2.5)*(m-3.5)*(m-4.5)*(m-5.5);
#declare m0 = m0*(m-6.5)*(m-8.5)*(m-9.5)*(m-10.5)*(m-11.5);
#if (m0 > 0)
  #declare maxdays = 31;
#else
  #declare maxdays = 30;
#end

棘手的部分是决定年份何时是闰年。这是闰年的全面测试。大多数人都知道4年规则,自2000年以来,有些人了解100年和400年的规则, 没有4000年的规则。

#declare LEAPYEAR = 2.0;
#if (mod(YEAR,4.0)=0)
  #declare LEAPYEAR = 1.0;
  #if (mod(YEAR,100.0)=0)
    #declare LEAPYEAR = 2.0;
  #end
  #if (mod(YEAR,400.0)=0
    #declare LEAPYEAR = 1.0;
  #end
#end
#if (MONTH = 2.0)
  #declare maxdays = maxdays - LEAPYEAR;
#end
#if (DAY > maxdays)
  #declare MONTH = MONTH + 1;
  #declare DAY = DAY - maxdays;
#end
#if (MONTH > 12)
  #declare YEAR = YEAR + 1;
  #declare MONTH = MONTH - 12;
#end

答案 7 :(得分:1)

喜欢詹姆斯的回答以及布鲁诺对此的解释。然而,对解决方案过于神秘的性质感到恼火。所以这里是相同的解决方案,但清除了任何不必要的过度加密。

function getDaysInMonth(m, y) {
   return /4|6|9|11/.test(m)?30:m==2?(!(y%4)&&y%100)||!(y%400)?29:28:31;
}

具体细节信息

  1. 似乎没有必要减少月份 - javascript与它无关,因为我们只将它用于比较,所以为了清晰起见,我使用了真实的月份数字。

  2. 为什么要写出4月4日,6月,9月和11月的号码?那只是 混乱。

  3. *或者,我们可以增加月份(++m)以获得接受(new Date()).getMonth()作为输入的版本

答案 8 :(得分:1)

我在玩C / C ++ / C#hobby project时遇到了这个问题。所以,尽管这个答案可能与OP无关,但其余的答案/评论似乎与JavaScript“高尔夫”有关;由于JS JIT的变幻无常,这是一种黑色艺术,但仍然很有趣。

站在GitaarLAB,TrueBlueAussie, et al 的肩膀上,我建议:

return m===2?y&3||!(y%25)&&y&15?28:29:30|(m+(m>>3));

答案 9 :(得分:0)

试试这个:

function DaysinMonth(aDate)  {
    return aDate.setMonth(aDate.getMonth()+1, 0).getDate();
}    

答案 10 :(得分:0)

试试这个:

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

    if (month == 2)
        return (isLeap) ? 29 : 28;
    return 30 + (month % 2);
}