【问题标题】:Number of days in any month [duplicate]任何一个月的天数[重复]
【发布时间】:2009-11-27 23:26:21
【问题描述】:

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

【问题讨论】:

  • 这是作业吗?
  • 是的,我正在阅读,但还没有弄清楚
  • 仅供参考:您的标题应该描述问题
  • 有什么方向可以让我弄清楚吗?
  • 我去看看 w3。不寻找直接的答案,只是寻找自己相处的方向。谢谢!

标签: javascript date


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

【讨论】:

  • 请原谅这个神秘的本质;我忍不住:D
  • 呵呵——可怜的人必须检查这些作业的正确性;)
  • 诗意的。我能听到脑海中的韵律;)
  • 布莱恩哈克特无法理解这一点......哈哈哈
  • 你应该把这个提交给@140bytes
【解决方案2】:

试试这个:

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

因为闰年你也需要过年。

【讨论】:

  • 这几乎是正确的,除了就 Javascript 的 Date 对象而言,月份从 0-11 索引并且 OP 希望输入从 1-12 索引的月份。如果您想要我的支持,您必须从传入的月份变量中减去 1。
  • @Asaph:再看答案。这是完美的。
  • @Crescent Fresh:好的,我知道现在发生了什么。该月需要减去 1 以纠正零索引,但随后使用的策略是检查下个月(即,将 1 添加到月份,从而抵消减去的 1)并回退当天(@ 的第三个参数987654324@构造函数)减1,使其成为下个月的第零天,直到当月的最后一天。所以它有效。但它很聪明。聪明的代码是不可维护的代码。我会将其重构为更具可读性的内容,或者至少添加注释来解释其聪明之处。
  • @Asaph:“聪明”是主观的。该算法是合理的。关于 IMO 唯一的负面影响是,它确实会影响系统时钟来做一些可以通过简单的条件和数学来确定的事情。
  • new Date 与正则表达式相比相对较慢。结合二进制和布尔数学可以更快地得出结果:jsperf.com/days-in-month-perf-test
【解决方案3】:

九月三十天,
四月、六月和十一月;
其余的都有三十一个,
仅二月除外,
有二十八天晴,
每个闰年有二十九个。

<3 Wikipedia

【讨论】:

  • +1 链接到维基百科和伟大的韵律
  • 我更喜欢“30天九月,四月六月,难怪其他人都有面包和果酱,除了奶奶,她会骑自行车。”的版本。
【解决方案4】:

喜欢詹姆斯的回答。为感兴趣的人稍微重新格式化。

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

【讨论】:

【解决方案5】:

每个人都知道,在月的任何一天,数查克的指关节三明治都胜过单纯的诗歌。

如果您无法编译和运行它(就像诗歌一样),请继续阅读。


没有正则表达式和负 2 余数运算,也没有闰年问题或日期对象。
尽管 javascript 的 Date object 涵盖了 1970 年 1 月 1 日前后大约 285616 年(100,000,000 天),但我已经厌倦了各种不同浏览器(尤其是 0 年到 99 年)的各种意外日期 inconsistencies。我也很好奇如何计算。

所以我写了一个简单的,最重要的是,small 算法(轻松击败 James 的答案)来计算 正确ProlepticGregorian/Astronomical/ISO 8601:2004(第 4.3.2.1 条),所以 year 0 存在并且是闰年,支持负年)给定月份和年份的天数。
它使用short-circuit bitmask-modulo leapYear algorithm(针对js稍作修改)和common mod-8 month算法(再次修改以获得最短路径)。

请注意,在 AD/BC 表示法中,公元 0 年/公元前 0 年不存在:相反,年份 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) 答案中使用的幻数查找不同的算法,因为这里仅需要 2 月的闰年的额外分支。


编辑(-历史):
我解除了修改后的 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&gt;&gt;3 中的常量位移。

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

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

它使用一个“幻数”对偏移量进行简单的逐位查找

DNOSAJJMAMFJ* = 月(从右到左) CBA9876543210 = 月 === 十六进制的位位置(我们从不使用 pos 0:一月是 1) 1010110101010 = 偏移 30 = 二进制 === 5546 十进制

13 位小于 31 位,因此我们可以安全地在位移中保存另一个字符,而不是 &gt;&gt;&gt;(因为我们不需要强制使用无符号 32 位)。

这消除了一次内存调用(var m)、一次加法和一次优先级! (而且它短了一个字符)

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

事实证明我的算法 2 是最快的毕竟(当然 TrueBlueAussie 的完整 2D 数组除外,尽管这需要更多的内存并且仍然需要快速算法来构建它的客户端-side),我按照 TrueBlueAussie 的建议恢复使用算法 2 的答案。

我仍然有一个爆炸性的合作,并感谢激励我重新审视我的答案!

【讨论】:

  • 我现在看到您的算法基于与我自己相似的布尔 + 二进制数学,并且都给出了完全相同的结果。因为你的比头发快 (0.9%),所以我会继续尝试改进我的。很好地拼凑起来:) 直到现在我才知道“mod-8 月”算法,但它基于原始二进制文件和我儿子在学校的“关节算法”。
  • 使用m + m &gt;&gt; 3(m &gt;&gt; 3) ^ m 快一点,我设法提高了代码的速度。为这个例子干杯:)
  • 经过一番分析。我发现您的(5546&gt;&gt;m&amp;1) 实际上比((m + (m &gt;&gt; 3)) &amp; 1)仅在 Chrome 中。我猜这个操作在 IE 中被优化了。
  • 这是一个可以进一步改进的直接测试平台:jsperf.com/days-in-month-head-to-head/3 我的在 Chrome 中更快,而你在 IE 中更快(而且每个人都讨厌 IE):)
  • 我最近玩过的最有趣的编码。 +1 是目前最快的(当然除了我的内存效率低下的可笑二维数组解决方案jsperf.com/days-in-month-head-to-head/5 :P)我回来! :)
【解决方案6】:

在计算机方面,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 和 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&gt;&gt;3)&amp;1)几乎所有浏览器上都比 (5546&gt;&gt;m&amp;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);
}

这个是 + 比 XOR (^) 快一点

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

这是我最初的尝试:

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

它的工作原理基于我的闰年答案:javascript to find leap year 这个答案在这里Leap year check using bitwise operators (amazing speed) 以及以下二进制逻辑。


二元月速成课:

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

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

这意味着您可以将值与 &gt;&gt; 3 移位 3 位,将原始的 ^ m 位异或,看看结果是 1 还是 0 在位位置 0使用&amp; 1。注意:事实证明+ 比 XOR (^) 稍快,(m &gt;&gt; 3) + m 在位 0 中给出相同的结果。

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


更新:我将前两个答案 + 最新答案(@James、@Caleb 和 @GitaarLAB)与这个答案进行了比较,以确保它们给出一致的结果,并且所有 4 个答案都返回相同的值从第 1 年到第 4000 年所有年份的所有月份:http://jsfiddle.net/TrueBlueAussie/8Lmpnpz4/6/。除了@Caleb,第 0 年对所有人都是一样的。

另一个更新:

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

function DIM(m, y) { //TrueBlueAussie
    return m===2?(y%4||!(y%100)&&(y%400))?28:29:30+(m+(m>>3)&1);
}
array = new Array(4000);
for (var y = 1; y < 4000; y++){
    array[y] = [];
    for (var m = 1; m < 13; m++)
    {
        array[y][m] = DIM(m, y);
    }
}

// This just does a lookup into the primed table - wasteful, but fast
function getDaysInMonth2(m, y){
    return array[y][m];
}

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

【讨论】:

  • 等一下..所以现在你结束了(回答 v10)我修改后的 mod8 月算法(相同)你提升到第一个三元(这是一个很好的电话!我正在复制谢谢)和较慢闰年算法。您应该可以通过使用严格相等的运算符 m===2 来获得另一个好处,就像我的回答一样,从而通知引擎我们不需要类型强制!感谢您的演奏和小提琴!很好地解释了 mod8 算法,我想任何人都可以在 wikipedia 上找到它,这就是为什么我将其省略并命名它的原因。检查我更快的算法!
  • @GitaarLAB:感谢您推荐===。不知道为什么你认为我的闰年算法比你的慢。即使没有你聪明的移位位掩码,我在 Chrome 中的速度也更快:jsperf.com/days-in-month-perf-test/6(但不是在 IE 中):)
  • 正如您对我的回答所评论的:令人惊讶的是,y % 4 实际上比y &amp; 3 快! :) (我假设它是 chrome)。这就是为什么我(也是根据经验:它在防御上曾经更快)认为它更快。回想起来,似乎我们都对此感到有些困惑。最后,大多数性能测试都以这种方式结束(由于不同的引擎)。就我个人而言,我通常会选择表现最好的平均性能(考虑到代码大小以及我想要/需要/必须支持的通常较旧的浏览器)。我也经常看到浏览器 X 版本 Y 的速度更快,突然 X 版本 Z 最慢..
  • @GitaarLAB:实际上y % 4 在 IE 上也比y &amp; 3 快(至少现在是这样)。您可能想尝试使用它来加快自己的速度。对于我们这个算法的任何版本,Chrome 的速度几乎是 IE 的两倍 :)
  • 一旦我从stackoverflow.com/questions/9852837/… 中理解了 15 和 25,它自然而然地跟着我就不得不使用它了。由于性能现在正在预编译,我发现&amp; 3 变得比% 4 更快(尝试了所有组合和&amp; 15)。我没有查看原件,但是是的,您可能应该坚持使用早期版本。如果我想出任何更快的替代方案,我也会添加它。完全归功于您聪明的位掩码,但这是在错误 [速度] 方向上迈出的一步。
【解决方案7】:

本着不为你做作业的精神,我在POVRay(对不起,不是JS)中展示了我多年前做的一个版本。

在 POVRay 中,没有布尔变量。我想出的方法是在 'm' 中创建一个多项式,它给出的答案 > 0 表示月份为 31 天,

#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

【讨论】:

    【解决方案8】:

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

    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 月、6 月、9 月和 11 月的数字写得乱七八糟?那只是 令人困惑。

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

    【讨论】:

    • 正则表达式比较慢。结合二进制和布尔数学可以更快地得到结果:jsperf.com/days-in-month-perf-test
    • @TrueBlueAussie 同意了。对于每秒 300 次此类操作还不够的场景,优化的解决方案(尽管可读性稍差)应该被证明是无价的!感谢您的基准测试。
    【解决方案9】:

    我在玩 C/C++/C# hobby project 时遇到了这个问题。因此,尽管此答案可能与 OP 无关,但其余答案/cmets 似乎与 JavaScript“高尔夫”有关;由于 JS JIT 的变幻莫测,这有点像魔法,但仍然很有趣。

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

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

    【讨论】:

      【解决方案10】:

      试试这个:

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

      【讨论】:

      • 这对我来说是最好的答案 (+1)。只有 getDate() 将返回纪元毫秒。所以你应该把它转换成日期。这效果更好: var date = new Date(); return (new Date(date.setMonth(date.getMonth() + 1, 0))).getDate();
      【解决方案11】:

      试试这个:

      function daysInMonth(year, month) {
          var isLeap = ( (!(year % 4)) && ( (year % 100) || (!(year % 400)) ) );
      
          if (month == 2)
              return (isLeap) ? 29 : 28;
          return 30 + (month % 2);
      }
      

      【讨论】:

        猜你喜欢
        • 2014-05-08
        • 1970-01-01
        • 1970-01-01
        • 2015-02-06
        • 1970-01-01
        • 2012-11-05
        • 2012-02-24
        • 1970-01-01
        • 2021-08-25
        相关资源
        最近更新 更多