Luckily this question doesn't specify if the number of the current day is required, leaving room for this answer.
Also some answers (also on other questions) had leap-year problems or used the Date-object. Although javascript's Date object
covers approximately 285616 years (100,000,000 days) on either side of January 1 1970, I was fed up with all kinds of unexpected date inconsistencies across different browsers (most notably year 0 to 99). I was also curious how to calculate it.
So I wrote a simple and above all, small algorithm to calculate the correct (Proleptic Gregorian / Astronomical / ISO 8601:2004 (clause 4.3.2.1), so year 0
exists and is a leap year and negative years are supported) day of the year based on year, month and day.
Note that in AD/BC
notation, year 0 AD/BC does not exist: instead year 1 BC
is the leap-year! IF you need to account for BC notation then simply subtract one year of the (otherwise positive) year-value first!!
I modified (for javascript) the short-circuit bitmask-modulo leapYear algorithm and came up with a magic number to do a bit-wise lookup of offsets (that excludes jan and feb, thus needing 10 * 3 bits (30 bits is less than 31 bits, so we can safely save another character on the bitshift instead of >>>
)).
Note that neither month or day may be 0
. That means that if you need this equation just for the current day (feeding it using .getMonth()
) you just need to remove the --
from --m
.
Note this assumes a valid date (although error-checking is just some characters more).
function dayNo(y,m,d){_x000D_
return --m*31-(m>1?(1054267675>>m*3-6&7)-(y&3||!(y%25)&&y&15?0:1):0)+d;_x000D_
}
_x000D_
<!-- some examples for the snippet -->_x000D_
<input type=text value="(-)Y-M-D" onblur="_x000D_
var d=this.value.match(/(-?\d+)[^\d]+(\d\d?)[^\d]+(\d\d?)/)||[];_x000D_
this.nextSibling.innerHTML=' Day: ' + dayNo(+d[1], +d[2], +d[3]);_x000D_
" /><span></span>_x000D_
_x000D_
<br><hr><br>_x000D_
_x000D_
<button onclick="_x000D_
var d=new Date();_x000D_
this.nextSibling.innerHTML=dayNo(d.getFullYear(), d.getMonth()+1, d.getDate()) + ' Day(s)';_x000D_
">get current dayno:</button><span></span>
_x000D_
Here is the version with correct range-validation.
function dayNo(y,m,d){_x000D_
return --m>=0 && m<12 && d>0 && d<29+( _x000D_
4*(y=y&3||!(y%25)&&y&15?0:1)+15662003>>m*2&3 _x000D_
) && m*31-(m>1?(1054267675>>m*3-6&7)-y:0)+d;_x000D_
}
_x000D_
<!-- some examples for the snippet -->_x000D_
<input type=text value="(-)Y-M-D" onblur="_x000D_
var d=this.value.match(/(-?\d+)[^\d]+(\d\d?)[^\d]+(\d\d?)/)||[];_x000D_
this.nextSibling.innerHTML=' Day: ' + dayNo(+d[1], +d[2], +d[3]);_x000D_
" /><span></span>
_x000D_
Again, one line, but I split it into 3 lines for readability (and following explanation).
The last line is identical to the function above, however the (identical) leapYear algorithm is moved to a previous short-circuit section (before the day-number calculation), because it is also needed to know how much days a month has in a given (leap) year.
The middle line calculates the correct offset number (for max number of days) for a given month in a given (leap)year using another magic number: since 31-28=3
and 3
is just 2 bits, then 12*2=24
bits, we can store all 12 months. Since addition can be faster then subtraction, we add the offset (instead of subtract it from 31
). To avoid a leap-year decision-branch for February, we modify that magic lookup-number on the fly.
That leaves us with the (pretty obvious) first line: it checks that month and date are within valid bounds and ensures us with a false
return value on range error (note that this function also should not be able to return 0, because 1 jan 0000 is still day 1.), providing easy error-checking: if(r=dayNo(/*y, m, d*/)){}
.
If used this way (where month and day may not be 0
), then one can change --m>=0 && m<12
to m>0 && --m<12
(saving another char).
The reason I typed the snippet in it's current form is that for 0-based month values, one just needs to remove the --
from --m
.
Extra:
Note, don't use this day's per month algorithm if you need just max day's per month. In that case there is a more efficient algorithm (because we only need leepYear when the month is February) I posted as answer this question: What is the best way to determine the number of days in a month with javascript?.