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

如何使用 JavaScript 来计算从1到366的一年中的某一天?

例如:

  • January 3应该是 3
  • February 1应该是 32
150884 次浏览

如果我没理解错的话,闰年是366否则就是365对吧?如果一年可以被4整除而不是100整除,那么它就是闰年,除非它也可以被400整除:

function daysInYear(year) {
if(year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {
// Leap year
return 366;
} else {
// Not a leap year
return 365;
}
}

更新后编辑:

在这种情况下,我不认为有一个内置的方法; 您需要这样做:

function daysInFebruary(year) {
if(year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {
// Leap year
return 29;
} else {
// Not a leap year
return 28;
}
}


function dateToDay(date) {
var feb = daysInFebruary(date.getFullYear());
var aggregateMonths = [0, // January
31, // February
31 + feb, // March
31 + feb + 31, // April
31 + feb + 31 + 30, // May
31 + feb + 31 + 30 + 31, // June
31 + feb + 31 + 30 + 31 + 30, // July
31 + feb + 31 + 30 + 31 + 30 + 31, // August
31 + feb + 31 + 30 + 31 + 30 + 31 + 31, // September
31 + feb + 31 + 30 + 31 + 30 + 31 + 31 + 30, // October
31 + feb + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, // November
31 + feb + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, // December
];
return aggregateMonths[date.getMonth()] + date.getDate();
}

(是的,我实际上没有复制或粘贴。如果有一个简单的方法,我会生气)

以下是 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点之前(例如00: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);

Date.prototype.dayOfYear= function(){
var j1= new Date(this);
j1.setMonth(0, 0);
return Math.round((this-j1)/8.64e7);
}


alert(new Date().dayOfYear())

这种方法考虑了时区问题和夏时制

function dayofyear(d) {   // d is a Date object
var yn = d.getFullYear();
var mn = d.getMonth();
var dn = d.getDate();
var d1 = new Date(yn,0,1,12,0,0); // noon on Jan. 1
var d2 = new Date(yn,mn,dn,12,0,0); // noon on input date
var ddiff = Math.round((d2-d1)/864e5);
return ddiff+1;
}

(取自 给你)

再看看这个 小提琴

这种方法适用于所有国家的夏令时变化(上面的“中午”一项在澳大利亚不适用) :

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

幸运的是,这个问题没有说明是否需要 目前的天数,为这个答案留下了空间。
还有一些答案(也包括其他问题)有闰年问题或使用 Date-object。虽然 javascript 的 Date object覆盖了大约285616年(100,000,000天) ,在1970年1月1日之前,我已经受够了各种不同浏览器(最显著的是0到99年)的各种意想不到的日期 前后矛盾。我也很好奇如何计算它。

所以我写了一个简单的,最重要的,很小算法来计算 正确(令人兴奋 格里高利/Astronomical/ISO 8601:2004(条款4.3.2.1) ,所以 年度 0存在,是一个闰年和 负数年份得到支持)的一年的一天,基于 白天
请注意,在 AD/BC表示法中,0AD/BC 年不存在: 相反,1 BC年是闰年!如果你需要考虑 BC 表示法,那么只需先减去一年(否则为正数)的年值即可! !

我修改了(用于 javascript 的) 短路位掩码模跳年算法,并想出了一个神奇的数字来对 补偿进行位查找(这不包括 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行(以下是解释)。

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

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

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

附加说明:
注意,如果您只需要每月最大天数,不要使用这个每月天数的算法。在这种情况下,有一个更有效的算法(因为我们只需要 leepYear,当月份是2月份) ,我回答了这个问题: 用 javascript 确定一个月的天数的最佳方法是什么?

把数学和日期函数混在一起总是让我担心(很容易错过闰年的其他细节)。假设你有:

var d = new Date();

我建议使用以下方法,在 day中可以节省天数:

for(var day = d.getDate(); d.getMonth(); day += d.getDate())
d.setDate(0);

没有任何理由说明为什么它不能正常工作(而且我也不会太担心这几个迭代,因为它不会被如此密集地使用)。

这是一个查找一年中当前日期的简单方法,它应该能够毫无问题地解释闰年:

Javascript:

Math.round((new Date().setHours(23) - new Date(new Date().getYear()+1900, 0, 1, 0, 0, 0))/1000/60/60/24);

Google Apps 中的 Javascript 脚本:

Math.round((new Date().setHours(23) - new Date(new Date().getYear(), 0, 1, 0, 0, 0))/1000/60/60/24);

此代码的主要操作是查找当前年份中已经过去的毫秒数,然后将这个数字转换为天数。当前年度已经过去的毫秒数可以通过减去当前年度第一天第一秒的毫秒数得到,这个毫秒数是用 new Date(new Date().getYear()+1900, 0, 1, 0, 0, 0)(Javascript)或者 new Date(new Date().getYear(), 0, 1, 0, 0, 0)(Google Apps Script)得到的,从当前日期第23小时的毫秒数中减去,这个毫秒数是用 new Date().setHours(23)得到的。将当前日期设置为23小时的目的是为了确保一年中的某一天被 Math.round()正确地四舍五入。

一旦你有了当前年份的毫秒数,那么你就可以把这个时间转换成天,除以1000就可以把毫秒转换成秒,然后除以60就可以把秒转换成分,再除以60就可以把分转换成小时,最后除以24就可以把小时转换成天。

注意: 本文的编辑是为了说明在 Google Apps Script 中实现的 JavaScript 和 JavaScript 之间的差异。此外,还为答案添加了更多的上下文。

((new Date () . setHours (23)-new Date (new Date () . getFullYear () ,0,1,0,0,0))/1000/86400) ;

进一步优化答案。

此外,通过将 setHours (23)或后面的 last-but-two 0更改为另一个值,可以提供与另一个时区相关的年份日。 例如,从欧洲检索位于美洲的资源。

我已经制作了一个可读的,并将做到这一点非常快,以及处理不同时区的 JS 日期对象。

我已经包括了相当多的时区,DST,闰秒和闰年的测试案例。

与 UTC 不同,ECMA-262忽略闰秒。如果要将其转换为使用实际 UTC 的语言,只需将1添加到 oneDay

// returns 1 - 366
findDayOfYear = function (date) {
var oneDay = 1000 * 60 * 60 * 24; // A day in milliseconds
var og = {                        // Saving original data
ts: date.getTime(),
dom: date.getDate(),            // We don't need to save hours/minutes because DST is never at 12am.
month: date.getMonth()
}
date.setDate(1);                  // Sets Date of the Month to the 1st.
date.setMonth(0);                 // Months are zero based in JS's Date object
var start_ts = date.getTime();    // New Year's Midnight JS Timestamp
var diff = og.ts - start_ts;


date.setDate(og.dom);             // Revert back to original date object
date.setMonth(og.month);          // This method does preserve timezone
return Math.round(diff / oneDay) + 1; // Deals with DST globally. Ceil fails in Australia. Floor Fails in US.
}


// Tests
var pre_start_dst = new Date(2016, 2, 12);
var on_start_dst = new Date(2016, 2, 13);
var post_start_dst = new Date(2016, 2, 14);


var pre_end_dst_date = new Date(2016, 10, 5);
var on_end_dst_date = new Date(2016, 10, 6);
var post_end_dst_date = new Date(2016, 10, 7);


var pre_leap_second = new Date(2015, 5, 29);
var on_leap_second = new Date(2015, 5, 30);
var post_leap_second = new Date(2015, 6, 1);


// 2012 was a leap year with a leap second in june 30th
var leap_second_december31_premidnight = new Date(2012, 11, 31, 23, 59, 59, 999);


var january1 = new Date(2016, 0, 1);
var january31 = new Date(2016, 0, 31);


var december31 = new Date(2015, 11, 31);
var leap_december31 = new Date(2016, 11, 31);


alert( ""
+ "\nPre Start DST: " + findDayOfYear(pre_start_dst) + " === 72"
+ "\nOn Start DST: " + findDayOfYear(on_start_dst) + " === 73"
+ "\nPost Start DST: " + findDayOfYear(post_start_dst) + " === 74"
      

+ "\nPre Leap Second: " + findDayOfYear(pre_leap_second) + " === 180"
+ "\nOn Leap Second: " + findDayOfYear(on_leap_second) + " === 181"
+ "\nPost Leap Second: " + findDayOfYear(post_leap_second) + " === 182"
      

+ "\nPre End DST: " + findDayOfYear(pre_end_dst_date) + " === 310"
+ "\nOn End DST: " + findDayOfYear(on_end_dst_date) + " === 311"
+ "\nPost End DST: " + findDayOfYear(post_end_dst_date) + " === 312"
      

+ "\nJanuary 1st: " + findDayOfYear(january1) + " === 1"
+ "\nJanuary 31st: " + findDayOfYear(january31) + " === 31"
+ "\nNormal December 31st: " + findDayOfYear(december31) + " === 365"
+ "\nLeap December 31st: " + findDayOfYear(leap_december31) + " === 366"
+ "\nLast Second of Double Leap: " + findDayOfYear(leap_second_december31_premidnight) + " === 366"
);

我发现很有趣的是,没有人考虑使用 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

我想提供一个解决方案,计算每个前一个月的天数相加:

function getDayOfYear(date) {
var month = date.getMonth();
var year = date.getFullYear();
var days = date.getDate();
for (var i = 0; i < month; i++) {
days += new Date(year, i+1, 0).getDate();
}
return days;
}
var input = new Date(2017, 7, 5);
console.log(input);
console.log(getDayOfYear(input));

这样你就不必管理闰年和夏令时的细节了。

如果你不想重新发明轮子,你可以使用优秀的 约会(node.js)库:

var getDayOfYear = require('date-fns/get_day_of_year')


var dayOfYear = getDayOfYear(new Date(2017, 1, 1)) // 1st february => 32

使用 UTC 时间戳的替代方案。另外,正如其他人指出的那样,每个月的第一天是1而不是0。然而,这个月从0开始。

var now = Date.now();
var year =  new Date().getUTCFullYear();
var year_start = Date.UTC(year, 0, 1);
var day_length_in_ms = 1000*60*60*24;
var day_number = Math.floor((now - year_start)/day_length_in_ms)
console.log("Day of year " + day_number);

可以在 setDate函数中将参数作为日期号传递:

var targetDate = new Date();
targetDate.setDate(1);


// Now we can see the expected date as: Mon Jan 01 2018 01:43:24
console.log(targetDate);


targetDate.setDate(365);


// You can see: Mon Dec 31 2018 01:44:47
console.log(targetDate)

我认为这更直截了当:

var date365 = 0;


var currentDate = new Date();
var currentYear = currentDate.getFullYear();
var currentMonth = currentDate.getMonth();
var currentDay = currentDate.getDate();


var monthLength = [31,28,31,30,31,30,31,31,30,31,30,31];


var leapYear = new Date(currentYear, 1, 29);
if (leapYear.getDate() == 29) { // If it's a leap year, changes 28 to 29
monthLength[1] = 29;
}


for ( i=0; i < currentMonth; i++ ) {
date365 = date365 + monthLength[i];
}
date365 = date365 + currentDay; // Done!

这对于那些需要将一年中的某一天作为字符串并且可以使用 jQueryUI 的人来说可能很有用。

你可以使用 jQuery UI Datepicker:

day_of_year_string = $.datepicker.formatDate("o", new Date())

在下面,它的工作方式与上面提到的一些答案((date_ms - first_date_of_year_ms) / ms_per_day)相同:

function getDayOfTheYearFromDate(d) {
return Math.round((new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime()
- new Date(d.getFullYear(), 0, 0).getTime()) / 86400000);
}


day_of_year_int = getDayOfTheYearFromDate(new Date())
Math.floor((Date.now() - Date.parse(new Date().getFullYear(), 0, 0)) / 86400000)

这就是我的解决办法

对于我们中那些想要快速解决方案的人来说。

(function(){"use strict";
function daysIntoTheYear(dateInput){
var fullYear = dateInput.getFullYear()|0;
// "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc)
//	except if it can be exactly divided by 100, then it isn't (2100, 2200, etc)
//		except if it can be exactly divided by 400, then it is (2000, 2400)"
// (https://www.mathsisfun.com/leap-years.html).
var isLeapYear = ((fullYear & 3) | (fullYear/100 & 3)) === 0 ? 1 : 0;
// (fullYear & 3) = (fullYear % 4), but faster
//Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0
var fullMonth = dateInput.getMonth()|0;
return ((
// Calculate the day of the year in the Gregorian calendar
// The code below works based upon the facts of signed right shifts
//    • (x) >> n: shifts n and fills in the n highest bits with 0s
//    • (-x) >> n: shifts n and fills in the n highest bits with 1s
// (This assumes that x is a positive integer)
(31 & ((-fullMonth) >> 4)) + // January // (-11)>>4 = -1
((28 + isLeapYear) & ((1-fullMonth) >> 4)) + // February
(31 & ((2-fullMonth) >> 4)) + // March
(30 & ((3-fullMonth) >> 4)) + // April
(31 & ((4-fullMonth) >> 4)) + // May
(30 & ((5-fullMonth) >> 4)) + // June
(31 & ((6-fullMonth) >> 4)) + // July
(31 & ((7-fullMonth) >> 4)) + // August
(30 & ((8-fullMonth) >> 4)) + // September
(31 & ((9-fullMonth) >> 4)) + // October
(30 & ((10-fullMonth) >> 4)) + // November
// There are no months past December: the year rolls into the next.
// Thus, fullMonth is 0-based, so it will never be 12 in Javascript


(dateInput.getDate()|0) // get day of the month


)&0xffff);
}
// Demonstration:
var date = new Date(2100, 0, 1)
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\tday "+daysIntoTheYear(date)+"\t"+date);
date = new Date(1900, 0, 1);
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\tday "+daysIntoTheYear(date)+"\t"+date);


// Performance Benchmark:
console.time("Speed of processing 65536 dates");
for (var i=0,month=date.getMonth()|0; i<65536; i=i+1|0)
date.setMonth(month=month+1+(daysIntoTheYear(date)|0)|0);
console.timeEnd("Speed of processing 65536 dates");
})();

一年中月份的大小和闰年的工作方式完全符合我们的时间与太阳的轨道。见鬼,它工作得如此完美,以至于我们所做的只是 在这里或那里调整几秒钟。我们目前的闰年制度自 1582年2月24日以来一直有效,并且在可预见的将来很可能继续有效。

然而,DST 非常容易发生变化。也许20年后,某些国家会用一整天或其他极端天气来抵消夏时制的影响。几乎可以肯定,夏令时的一整天永远不会发生,但夏令时仍然是非常悬而未决和犹豫不决。因此,上述解决方案是未来的证明,除了非常非常快。

上面的代码片段运行非常快。我的电脑可以处理65536日期在 ~ 52ms 的 Chrome。

const dayOfYear = date => {
const myDate = new Date(date);
const year = myDate.getFullYear();
const firstJan = new Date(year, 0, 1);
const differenceInMillieSeconds = myDate - firstJan;
return (differenceInMillieSeconds / (1000 * 60 * 60 * 24) + 1);
};


const result = dayOfYear("2019-2-01");
console.log(result);

这是一个避免了麻烦的 Date 对象和时区问题的解决方案,它要求输入日期的格式为“ yyyy-dd-mm”。如果要更改格式,请修改 date _ str _ to _ part 函数:

    function get_day_of_year(str_date){
var date_parts = date_str_to_parts(str_date);
var is_leap = (date_parts.year%4)==0;
var acct_for_leap = (is_leap && date_parts.month>2);
var day_of_year = 0;


var ary_months = [
0,
31, //jan
28, //feb(non leap)
31, //march
30, //april
31, //may
30, //june
31, //july
31, //aug
30, //sep
31, //oct
30, //nov
31  //dec
];




for(var i=1; i < date_parts.month; i++){
day_of_year += ary_months[i];
}


day_of_year += date_parts.date;


if( acct_for_leap ) day_of_year+=1;


return day_of_year;


}


function date_str_to_parts(str_date){
return {
"year":parseInt(str_date.substr(0,4),10),
"month":parseInt(str_date.substr(5,2),10),
"date":parseInt(str_date.substr(8,2),10)
}
}

一个简单明了的解决方案,包含完整的解释。

var dayOfYear = function(date) {
const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const [yyyy, mm, dd] = date.split('-').map(Number);


// Checks if February has 29 days
const isLeap = (year) => new Date(year, 1, 29).getDate() === 29;


// If it's a leap year, changes 28 to 29
if (isLeap(yyyy)) daysInMonth[1] = 29;


let daysBeforeMonth = 0;
// Slice the array and exclude the current Month
for (const i of daysInMonth.slice(0, mm - 1)) {
daysBeforeMonth += i;
}


return daysBeforeMonth + dd;
};


console.log(dayOfYear('2020-1-3'));
console.log(dayOfYear('2020-2-1'));

我写了这两个 javascript 函数,它们返回一年中的某一天(Jan 1 = 1)。 它们都是闰年的原因。

function dayOfTheYear() {
// for today
var M=[31,28,31,30,31,30,31,31,30,31,30,31]; var x=new Date(); var m=x.getMonth();
var y=x.getFullYear(); if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) {++M[1];}
var Y=0; for (var i=0;i<m;++i) {Y+=M[i];}
return Y+x.getDate();
}


function dayOfTheYear2(m,d,y) {
// for any day : m is 1 to 12, d is 1 to 31, y is a 4-digit year
var m,d,y; var M=[31,28,31,30,31,30,31,31,30,31,30,31];
if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) {++M[1];}
var Y=0; for (var i=0;i<m-1;++i) {Y+=M[i];}
return Y+d;
}

如果使用 Moment.js,我们可以得到甚至设置一年中的某一天。

moment().dayOfYear();
//for getting


moment().dayOfYear(Number);
//for setting

Js 使用 这个密码计算年份的天数

也许能帮到任何人

let day = (date => {
return Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24)
})(new Date())

一句话:

Array.from(new Array(new Date().getMonth()), (x, i) => i).reduce((c, p, idx, array)=>{
let returnValue =  c + new Date(new Date().getFullYear(), p, 0).getDate();
if(idx == array.length -1){
returnValue = returnValue + new Date().getDate();
}
return returnValue;
}, 0)