JavaScript 计算一年中的第几天 (1 - 366)

IT技术 javascript
2021-01-28 22:39:12

如何使用 JavaScript 从 1 - 366 计算一年中的哪一天?

例如:

  • January 3应该是3
  • February 1应该是32
6个回答

按照 OP 的编辑:

var now = new Date();
var start = new Date(now.getFullYear(), 0, 0);
var diff = now - start;
var oneDay = 1000 * 60 * 60 * 24;
var day = Math.floor(diff / oneDay);
console.log('Day of year: ' + day);

编辑:上面的代码会失败时,now是3月26日和10月29日之间的日期now的时间是凌晨1点之前(如0时59分59秒)。这是因为代码没有考虑夏令时。您应该对此进行补偿

var now = new Date();
var start = new Date(now.getFullYear(), 0, 0);
var diff = (now - start) + ((start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000);
var oneDay = 1000 * 60 * 60 * 24;
var day = Math.floor(diff / oneDay);
console.log('Day of year: ' + day);

也许使用.setUTCHoursDate.UTC()更可靠的解决方案。
2021-03-14 22:39:12
日分量是基数为 1。即要代表今年的 1 月 1 日,您可以使用new Date(2014, 0, 1),而不是new Date(2014, 0, 0)像这里那样。这是故意的吗?也许这就是有一天new Date(2014, 0, 0)会回来的原因12/31/2013
2021-03-16 22:39:12
@AlexTurpin,@T30:我知道这有点旧,但是如果您想知道……问题是由于 3 月开始的夏令时造成的。这是因为当您计算 DST 之前的某个日期的午夜和 DST 之后的某个日期的午夜之间的差异时,您将无法得到可被 1000 * 60 * 60 * 24 整除的毫秒数(正好是一小时) . 最简单的解决方案是使用ceil而不是floor,这将为您提供一个编号系统,其中 Jan 1st = 1。如果您想要 Jan 1st = 0(就像floor会给您的),只需从您的最终结果中减去 1。
2021-03-17 22:39:12
要考虑时区和夏令时,请将第 3 行更改为: var diff = now - start + (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
2021-03-23 22:39:12
Math.floor在 4 月的某一天之后,我一直给我的结果比预期少 1 天。Math.ceil按预期工作,但我已经看到建议您使用Math.round而不是两者之一。
2021-03-25 22:39:12

这适用于所有国家/地区的夏令时更改(上面的“中午”在澳大利亚不起作用):

Date.prototype.isLeapYear = function() {
    var year = this.getFullYear();
    if((year & 3) != 0) return false;
    return ((year % 100) != 0 || (year % 400) == 0);
};

// Get Day of Year
Date.prototype.getDOY = function() {
    var dayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
    var mn = this.getMonth();
    var dn = this.getDate();
    var dayOfYear = dayCount[mn] + dn;
    if(mn > 1 && this.isLeapYear()) dayOfYear++;
    return dayOfYear;
};
@ShyamHabarakada:您的基准测试中的代码已损坏,getDay()需要更改为getDate(). 前者返回星期几(0=Sunday..6=Saturday),而不是当天。
2021-03-14 22:39:12
同意接受的答案中存在与夏令时相关的错误。上述解决方案更好,速度更快。这是我在 jsPerf 上测试的一个变体jsperf.com/date-getdayofyear-perf
2021-03-16 22:39:12

我觉得很有趣,没有人考虑使用 UTC,因为它不受 DST 的约束。因此,我提出以下建议:

function daysIntoYear(date){
    return (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000;
}

您可以使用以下方法对其进行测试:

[new Date(2016,0,1), new Date(2016,1,1), new Date(2016,2,1), new Date(2016,5,1), new Date(2016,11,31)]
    .forEach(d => 
        console.log(`${d.toLocaleDateString()} is ${daysIntoYear(d)} days into the year`));

2016 年闰年的输出(使用http://www.epochconverter.com/days/2016验证):

1/1/2016 is 1 days into the year
2/1/2016 is 32 days into the year
3/1/2016 is 61 days into the year
6/1/2016 is 153 days into the year
12/31/2016 is 366 days into the year
我个人喜欢这种方法。避免 DST 问题的更简洁(而且非常聪明!)的方法。如果有人想知道如何从 DOY 转换回日期,那就简单多了。基本上,您只需创建一个 DOYth 的日期。例如,1 月 59 日 === 2 月 28 日。闰日处理得很好。function doyToDate(doy, year) { return new Date(year, 0, doy); }
2021-03-13 22:39:12
这应该被标记为正确答案
2021-03-19 22:39:12
这可能会溢出,因为毫秒数太大。
2021-03-26 22:39:12
@knt5784 数学可能会溢出,但他的示例显示最大可能的实际值工作正常,因此除非存在基于浏览器的整数溢出问题,否则您应该可以使用它。
2021-04-01 22:39:12
@knt5784 这段代码没有毫秒溢出的风险,至少在接下来的 200,000 年里不会。Date 支持的最大日期是new Date(8640000000000000),仍然小于Number.MAX_SAFE_INTEGER看到这个答案:stackoverflow.com/a/11526569/18511
2021-04-04 22:39:12
Date.prototype.dayOfYear= function(){
    var j1= new Date(this);
    j1.setMonth(0, 0);
    return Math.round((this-j1)/8.64e7);
}

alert(new Date().dayOfYear())
这不会通过 lint... 修改不属于您的对象,特别是全局对象。这在夏令时超过 12 小时的极端倾斜的行星中也会失败。但更现实的是,如果您正在编写允许更改 Date 对象时区的浏览器,j1 的时区可能是澳大利亚,而这个时区可能是阿拉斯加,打破了四舍五入。
2021-03-20 22:39:12

幸运的是,如果数量这个问题没有指定当前需要一天,留有余地这个答案。
还有一些答案(也在其他问题上)有闰年问题或使用了日期对象。尽管 javascriptDate object涵盖了 1970 年 1 月 1 日左右的大约 285616 年(100,000,000 天),但我已经受够了不同浏览器(最显着的是 0 到 99 年)的各种意外日期不一致我也很好奇如何计算。

所以我写了一个简单且最重要的算法来计算正确的Proleptic Gregorian / Astronomical / ISO 8601:2004 (clause 4.3.2.1),所以年份0存在并且是闰年并且支持负年)一年中的某一天基于
请注意,在AD/BC符号中,公元 0 年不存在:相反,年份1 BC是闰年!如果您需要考虑 BC 表示法,那么只需先减去(否则为正)年份值的一年!!

我修改了(对于javascript)短路位掩码模leapYear算法并提出了一个幻数来按位查找偏移量(不包括jan和feb,因此需要10 * 3位(30位小于31 位,所以我们可以安全地在位移位上保存另一个字符而不是>>>))。

请注意,月或日都不能是0这意味着,如果你需要这个公式只为当前日期(用喂养它.getMonth()),你只需要删除----m

请注意,这假定一个有效的日期(尽管错误检查只是更多的字符)。

function dayNo(y,m,d){
  return --m*31-(m>1?(1054267675>>m*3-6&7)-(y&3||!(y%25)&&y&15?0:1):0)+d;
}
<!-- some examples for the snippet -->
<input type=text value="(-)Y-M-D" onblur="
  var d=this.value.match(/(-?\d+)[^\d]+(\d\d?)[^\d]+(\d\d?)/)||[];
  this.nextSibling.innerHTML=' Day: ' + dayNo(+d[1], +d[2], +d[3]);
" /><span></span>

<br><hr><br>

<button onclick="
  var d=new Date();
  this.nextSibling.innerHTML=dayNo(d.getFullYear(), d.getMonth()+1, d.getDate()) + ' Day(s)';
">get current dayno:</button><span></span>


这是具有正确范围验证的版本

function dayNo(y,m,d){
  return --m>=0 && m<12 && d>0 && d<29+(  
           4*(y=y&3||!(y%25)&&y&15?0:1)+15662003>>m*2&3  
         ) && m*31-(m>1?(1054267675>>m*3-6&7)-y:0)+d;
}
<!-- some examples for the snippet -->
<input type=text value="(-)Y-M-D" onblur="
  var d=this.value.match(/(-?\d+)[^\d]+(\d\d?)[^\d]+(\d\d?)/)||[];
  this.nextSibling.innerHTML=' Day: ' + dayNo(+d[1], +d[2], +d[3]);
" /><span></span>

同样,一行,但我将其分成 3 行以提高可读性(以及以下解释)。

最后一行与上面的函数相同,但是(相同的)leapYear 算法移到了前面的短路部分(在天数计算之前),因为它还需要知道一个月有多少天给定(闰)年。

中间一行使用另一个幻数计算给定(闰)年中给定月份的正确偏移量(最大天数):因为31-28=33只是 2 位,那么12*2=24位,我们可以存储所有 12 个月。由于加法比减法更快,我们添加偏移量(而不是从 中减去31)。为了避免 2 月份出现闰年决策分支,我们动态修改了这个神奇的查找数。

这给我们留下了(非常明显的)第一行:它检查月份和日期是否在有效范围内,并确保我们false在范围错误时返回值(注意这个函数也不应该能够返回 0,因为 1 jan 0000仍然是第 1 天。),提供简单的错误检查:if(r=dayNo(/*y, m, d*/)){}
如果以这种方式使用(月和日可能不是0),则可以更改--m>=0 && m<12m>0 && --m<12(保存另一个字符)。
我以当前形式输入代码段的原因是,对于基于 0 的月份值,只需--要从--m.

额外:
请注意,如果您只需要每月最大天数,请不要使用这一天的每月算法。在那种情况下,有一个更有效的算法(因为我们只需要 leepYear 当月份是二月时)我发布作为回答这个问题:使用 javascript 确定一个月中天数的最佳方法是什么?.

我实际上只是在看它的时候就这样做了!我意识到我正在加一个然后立即减去它。我认为当我们2^31 - 2016从现在开始多年时,js 可能会有点过时。
2021-03-19 22:39:12
@Freshvolk:哈哈,这就是为什么我从来没有测试过超过 2^31:)但在那之前,它至少会给出可预测和一致的结果哈哈。编辑,只是为了理论上的缘故,使用较慢的传统全模算法进行闰年,范围可以扩展到 2^53。月份查找算法不是限制因素。
2021-03-21 22:39:12
很开心你喜欢。这个算法在 0 的两边都经过了 2^31-1 年的全面测试(高达 2147483647,这是 javascript 日期对象范围的 7500 倍)。(它可能适用于 2^32,但我还没有测试过然而)。另外,您可能会再次阅读我的回答:如果您删除+1from this.getMonth()+1,您可以----m. 编辑所以,我会做(对于图书馆):Date.prototype.dayNo = function(){ var y=this.getFullYear(), m=this.getMonth(); return m*31-(m>1?(1054267675>>m*3-6&7)-(y&3||!(y%25)&&y&15?0:1):0)+this.getDate(); };
2021-03-23 22:39:12
为了方便使用,我把它放到了一个日期函数中。授予 Date() 不一致仍然是一个问题,但我使用的是 2015+ 年,所以我希望它们是一致的。可怜的 JSHint 在试图验证你的代码时死了哈哈。Date.prototype.dayNo = function(){ var y = this.getFullYear(); var m = this.getMonth()+1; var d = this.getDate(); return --m*31-(m>1?(1054267675>>m*3-6&7)-(y&3||!(y%25)&&y&15?0:1):0)+d; };
2021-04-07 22:39:12