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

107 javascript

如何使用javascript计算一年中的某一天,从1到366?例如:

  • 1月3日应该是3.
  • 2月1日应该是32.

Ale*_*pin 122

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);
Run Code Online (Sandbox Code Playgroud)

编辑:上述代码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);
Run Code Online (Sandbox Code Playgroud)

  • @AlexTurpin,@ T30:我知道这有点旧,但如果你想知道......这个问题是由于3月开始的夏令时造成的.这是因为当您在DST之前的一个日期的午夜和DST之后的一个日期的午夜之间取得差异时,您将无法将数字毫秒平均整除1000*60*60*24(它将正好一小时关闭) .最简单的解决方案是使用`ceil`而不是`floor`,这将为你提供一个编号系统,其中1月1日= 1.如果你想要1月1 = 0(如`floor`会给你),只需从你的减去1最后结果. (6认同)
  • 在4月的某一天之后,"Math.floor"一直给我的结果比预期的少了1天.`Math.ceil`按预期工作,但我已经看到建议您使用`Math.round`而不是其中之一. (4认同)
  • 日期组件是base-1.即代表今年1月1日,你将使用`new Date(2014,0,1)`,而不是'new Date(2014,0,0)`.这是故意的吗?也许这就是因为"新日期(2014年,0,0)"将返回"2013年12月31日"的原因. (3认同)
  • 也许使用 [`.setUTCHours`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCHours) 和 [`Date.UTC()`](https://developer. mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC)以获得更可靠的解决方案。 (2认同)
  • 要考虑时区和夏令时,请将第3行更改为:`var diff = now - start +(start.getTimezoneOffset() - now.getTimezoneOffset())*60*1000;` (2认同)

Joe*_*ost 46

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

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;
};
Run Code Online (Sandbox Code Playgroud)


小智 25

我觉得很有意思没有人考虑使用UTC,因为它不受DST的限制.因此,我提出以下建议:

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

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

[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`));
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • 这应该被标记为正确答案 (5认同)
  • 我个人喜欢这种方法。避免 DST 问题的更干净(而且非常聪明!)的方法。如果有人想知道如何从 DOY 转换回最新版本,那就简单多了。基本上,您只需创建 1 月 DOYth 的日期。例如,1 月 59 日 === 2 月 28 日。闰日处理得很好。`函数 doyToDate(doy, 年) { return new Date(年, 0, doy); }` (2认同)
  • @knt5784 这段代码不存在毫秒溢出的风险,至少在接下来的 20 万年里不会。Date 支持的最大日期是 new Date(8640000000000000) ,它仍然小于 Number.MAX_SAFE_INTEGER 。看到这个答案:/sf/answers/806859861/ (2认同)

ken*_*bec 18

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

alert(new Date().dayOfYear())
Run Code Online (Sandbox Code Playgroud)

  • 这不会传递lint ...修改不属于你的对象,特别是全局对象.在夏令时超过12小时的极端倾斜的行星中,这也会失败.但更现实的是,如果你编写的浏览器允许更改Date对象的时区,j1的时区可能是澳大利亚,这个时区可能是阿拉斯加,打破了四舍五入. (3认同)

Git*_*LAB 10

幸运的是,如果数量这个问题没有指定当前需要一天,留有余地这个答案.
还有一些答案(也在其他问题上)有闰年问题或使用Date-object.虽然javascript Date object在1970年1月1日的两边都覆盖了大约285616年(100,000,000天),但我厌倦了不同浏览器中的各种意外日期不一致(最明显的是0到99年).我也很好奇如何计算它.

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

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

请注意,无论是月份还是日期都不是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;
}
Run Code Online (Sandbox Code Playgroud)
<!-- 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>
Run Code Online (Sandbox Code Playgroud)


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

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;
}
Run Code Online (Sandbox Code Playgroud)
<!-- 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>
Run Code Online (Sandbox Code Playgroud)

同样,一行,但我把它分成3行以便于阅读(以及下面的解释).

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

中间线使用另一个幻数计算给定(跳跃)年中给定月份的正确偏移数(对于最大天数):因为31-28=3并且3只是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的月份值,只需要删除--from --m.

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


vus*_*san 7

如果使用 moment.js,我们可以获取甚至设置一年中的某一天。

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

moment().dayOfYear(Number); 
//for setting 
Run Code Online (Sandbox Code Playgroud)

moment.js 使用此代码进行一年中的某一天的计算


Ama*_*iet 5

如果您不想重新发明轮子,可以使用出色的date-fns(node.js)库:

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

var dayOfYear = getDayOfYear(new Date(2017, 1, 1)) // 1st february => 32
Run Code Online (Sandbox Code Playgroud)


小智 5

Math.floor((Date.now() - Date.parse(new Date().getFullYear(), 0, 0)) / 86400000)
Run Code Online (Sandbox Code Playgroud)

这是我的解决方案


use*_*530 5

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);
Run Code Online (Sandbox Code Playgroud)