Javascript คำนวณวันในปี (1 - 366)


119

ฉันจะใช้จาวาสคริปต์เพื่อคำนวณวันของปีตั้งแต่ 1 - 366 ได้อย่างไร ตัวอย่างเช่น:

  • 3 มกราคมที่ควรจะเป็น3
  • 1 กุมภาพันธ์ควรจะเป็น32

4
var days = new Date().getFullYear() % 4 == 0 ? 366 : 365;
Alex Turpin

แต่จริงๆฉันไม่แน่ใจว่าคุณหมายถึงอะไร คุณต้องการจำนวนวันในปีหรือไม่? หรือระหว่างสองวัน?
Alex Turpin

16
fyi @ xeon06 การคำนวณปีอธิกสุรทินนั้นซับซ้อนกว่า mod'ing โดย 4 เล็กน้อยดู: อัลกอริทึมปีอธิกสุรทิน
Matt Felzani

4
@ Xeon06: ถูกต้องเกือบตลอดเวลา จากWikipedia : ปีที่หารด้วย 100 อย่างเท่า ๆ กันจะไม่ใช่ปีอธิกสุรทินเว้นแต่จะหารด้วย 400 ได้เท่า ๆ กันซึ่งในกรณีนี้จะเป็นปีอธิกสุรทิน
Cameron

ดีฉันยืนแก้ไข @minitech ดูเหมือนจะมีคำตอบที่ถูกต้องแล้ว
อเล็กซ์ Turpin

คำตอบ:


138

ติดตามการแก้ไขของ 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เป็นวันที่ในระหว่างวันที่ 26 มีนาคมถึง 29 ตุลาคมและnowเวลาก่อน 01:00 น. (เช่น 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);


5
Math.floorทำให้ฉันได้ผลลัพธ์น้อยกว่าที่คาดไว้ 1 วันอย่างต่อเนื่องหลังจากหนึ่งวันในเดือนเมษายน Math.ceilทำงานได้ตามที่คาดไว้ แต่ฉันได้เห็นควรให้คุณใช้Math.roundแทนอย่างใดอย่างหนึ่ง
baacke

3
องค์ประกอบของวันคือฐาน -1 กล่าวคือเพื่อเป็นตัวแทนของวันที่ 1 มกราคมของปีนี้คุณจะใช้new Date(2014, 0, 1)ไม่ใช่new Date(2014, 0, 0)ตามที่คุณมีที่นี่ นั่นคือเจตนา? บางทีนั่นอาจจะเป็นสิ่งที่บัญชีสำหรับการปิดโดยหนึ่งวันเป็นจะกลับมาnew Date(2014, 0, 0) 12/31/2013
Kirk Woll

1
อาจใช้.setUTCHoursและDate.UTC()สำหรับโซลูชันที่เชื่อถือได้มากขึ้น
โย

6
@AlexTurpin, @ T30: ฉันรู้ว่ามันเก่าไปหน่อย แต่ถ้าคุณสงสัย ... ปัญหาเกิดจากเวลาออมแสงเริ่มในเดือนมีนาคม เนื่องจากเมื่อคุณใช้ผลต่างของเที่ยงคืนของวันที่ก่อน DST และเที่ยงคืนของวันที่หลังจาก DST คุณจะไม่มีจำนวนมิลลิวินาทีหารเท่า ๆ กันด้วย 1,000 * 60 * 60 * 24 (จะเท่ากับหนึ่งชั่วโมงปิด) . วิธีแก้ปัญหาที่ง่ายที่สุดคือการใช้ceilแทนที่จะใช้ระบบfloorนี้จะให้ระบบเลขที่ 1 ม.ค. = 1 หากคุณต้องการให้ 1 ม.ค. = 0 (ตามที่floorจะให้) ให้ลบ 1 ออกจากผลลัพธ์สุดท้าย
Warren R.

2
หากต้องการพิจารณาเขตเวลาและการออมแสงให้เปลี่ยนบรรทัดที่ 3 เป็น: var diff = now - start + (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
Marlon

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

1
ยอมรับว่ามีจุดบกพร่องที่เกี่ยวข้องกับการประหยัดเวลากลางวันในคำตอบที่ยอมรับ วิธีแก้ปัญหาข้างต้นดีกว่าและเร็วกว่า นี่คือรูปแบบที่ฉันทดสอบบน jsPerf jsperf.com/date-getdayofyear-perf
Shyam Habarakada

@ShyamHabarakada: รหัสในเกณฑ์มาตรฐานของคุณเสียgetDay()ต้องเปลี่ยนเป็นgetDate(). อดีตส่งคืนวันในสัปดาห์ (0 = Sunday..6 = วันเสาร์) ไม่ใช่วัน
CodeManX

32

ฉันคิดว่ามันน่าสนใจมากที่ไม่มีใครพิจารณาใช้ 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

ซึ่งอาจมากเกินไปเนื่องจากจำนวนมิลลิวินาทีใหญ่เกินไป
knt5784

@ knt5784 คณิตศาสตร์อาจล้นได้ แต่ตัวอย่างของเขาแสดงให้เห็นว่าค่าในโลกแห่งความเป็นจริงที่ใหญ่ที่สุดที่เป็นไปได้นั้นทำงานได้ดีดังนั้นคุณควรจะใช้สิ่งนี้ได้เว้นแต่จะมีปัญหาตามเบราว์เซอร์ที่มีจำนวนเต็มล้น
Per Fagrell

ผมชอบแนวทางนี้เป็นการส่วนตัว วิธีที่สะอาดกว่า (และฉลาดมาก!) เพื่อหลีกเลี่ยงปัญหา DST หากใครสงสัยว่าจะแปลงจาก DOY กลับไปเป็นปัจจุบันได้ง่ายกว่ามาก โดยทั่วไปคุณเพิ่งสร้างวันที่ DOYth มกราคม เช่นวันที่ 59 มกราคม === 28 กุมภาพันธ์วันอธิกสุรทินก็จัดการได้ดี function doyToDate(doy, year) { return new Date(year, 0, doy); }
กีบ

@ knt5784 ไม่มีความเสี่ยงที่จะล้นในมิลลิวินาทีด้วยรหัสนี้อย่างน้อยก็ไม่เกิน 200,000 ปี วันที่ใหญ่ที่สุดได้รับการสนับสนุนโดยวันที่ซึ่งยังคงน้อยกว่าnew Date(8640000000000000) Number.MAX_SAFE_INTEGERดูคำตอบนี้: stackoverflow.com/a/11526569/18511
Kip

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())

3
สิ่งนี้จะไม่ผ่านผ้าสำลี ... การปรับเปลี่ยนวัตถุที่ไม่ใช่ของคุณโดยเฉพาะสิ่งที่เป็นสากล สิ่งนี้จะล้มเหลวในดาวเคราะห์ที่มีความเอียงมากซึ่งเวลาออมแสงนานกว่า 12 ชั่วโมง แต่ตามความเป็นจริงมากขึ้นหากคุณเขียนโค้ดสำหรับเบราว์เซอร์ที่อนุญาตให้เปลี่ยนเขตเวลาของวัตถุวันที่เขตเวลาของ j1 อาจเป็นออสเตรเลียและเขตเวลานี้อาจเป็นอะแลสกาซึ่งทำลายการปัดเศษ
Ray Foss

11

โชคดีที่คำถามนี้ไม่ได้ระบุว่าจำเป็นต้องใช้จำนวนวันปัจจุบันหรือไม่โดยออกจากที่ว่างสำหรับคำตอบนี้
นอกจากนี้บางคำตอบ (รวมถึงคำถามอื่น ๆ ) มีปัญหาปีอธิกสุรทินหรือใช้วัตถุวันที่ แม้ว่าจาวาสคริปต์จะDate objectครอบคลุมประมาณ 285616 ปี (100,000,000 วัน) ในทั้งสองด้านของวันที่ 1 มกราคม 1970 แต่ฉันก็เบื่อหน่ายกับความไม่สอดคล้องกันของวันที่ที่ไม่คาดคิดทุกประเภทในเบราว์เซอร์ต่างๆ (โดยเฉพาะอย่างยิ่งคือปี 0 ถึง 99) ฉันยังอยากรู้วิธีคำนวณ

ดังนั้นฉันจึงเขียนอัลกอริทึมขนาดเล็กที่เรียบง่ายและเหนือสิ่งอื่นใดเพื่อคำนวณค่าที่ถูกต้อง ( Proleptic Gregorian / Astronomical / ISO 8601: 2004 (ข้อ 4.3.2.1) ดังนั้นปี0จึงมีอยู่และเป็นปีอธิกสุรทินและรองรับปีเชิงลบ ) วันของปี ขึ้นอยู่กับปี , เดือนและวัน
โปรดทราบว่าในAD/BCสัญกรณ์ไม่มีปีค. ศ. 0 / ปีก่อนคริสตกาล: แทนปี1 BCคือปีอธิกสุรทิน! หากคุณต้องการบันทึกสัญกรณ์ BC ให้ลบหนึ่งปีของค่าปี (เป็นบวก) ก่อน !!

ฉันแก้ไข (สำหรับ javascript) อัลกอริธึม leapYear bitmask-modulo ลัดวงจรและสร้างตัวเลขวิเศษเพื่อทำการค้นหาออฟเซ็ตอย่างชาญฉลาด(ซึ่งไม่รวมมกราคมและกุมภาพันธ์ดังนั้นจึงต้องใช้ 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 (เหมือนกัน) จะถูกย้ายไปยังส่วนการลัดวงจรก่อนหน้า (ก่อนการคำนวณเลขวัน) เนื่องจากจำเป็นต้องทราบด้วยว่าใน a ให้ (อธิกสุรทิน) ปี

เส้นตรงกลางจะคำนวณหมายเลขออฟเซ็ตที่ถูกต้อง(สำหรับจำนวนวันสูงสุด) สำหรับเดือนที่กำหนดในปีที่กำหนด (อธิกสุรทิน) โดยใช้เลขวิเศษอื่น: ตั้งแต่31-28=3และ3เป็นเพียง 2 บิตจากนั้น12*2=24บิตเราสามารถจัดเก็บได้ทั้ง 12 เดือน เนื่องจากการบวกสามารถทำได้เร็วกว่าการลบเราจึงเพิ่มค่าชดเชย (แทนที่จะลบออก31 ) เพื่อหลีกเลี่ยงสาขาการตัดสินใจอธิกสุรทินในเดือนกุมภาพันธ์เราแก้ไขหมายเลขการค้นหาเวทย์มนตร์นั้นได้ทันที

นั่นทำให้เรามีบรรทัดแรก (ค่อนข้างชัดเจน): ตรวจสอบว่าเดือนและวันที่นั้นอยู่ในขอบเขตที่ถูกต้องและทำให้เรามั่นใจว่ามีfalseค่าส่งคืนตามข้อผิดพลาดของช่วง (โปรดทราบว่าฟังก์ชั่นนี้ไม่ควรคืนค่า 0 เนื่องจาก 1 ม.ค. 0000 ยังคงเป็นวันที่ 1) ให้ตรวจสอบข้อผิดพลาดได้ง่าย: if(r=dayNo(/*y, m, d*/)){}.
หากใช้วิธีนี้ (โดยที่เดือนและวันอาจไม่ใช่0) ก็สามารถเปลี่ยน--m>=0 && m<12เป็นm>0 && --m<12(บันทึกถ่านอื่น) ได้
เหตุผลที่ผมพิมพ์ข้อมูลโค้ดในรูปแบบปัจจุบันก็เป็นที่สำหรับ 0 ตามค่าเดือนหนึ่งก็ต้องเอาจาก----m

พิเศษ:
หมายเหตุอย่าใช้อัลกอริทึมของวันต่อเดือนนี้หากคุณต้องการเพียงวันสูงสุดต่อเดือน ในกรณีนั้นมีอัลกอริทึมที่มีประสิทธิภาพมากขึ้น (เพราะเราต้องการ leepYear เมื่อเดือนกุมภาพันธ์เท่านั้น) ฉันโพสต์เป็นคำตอบคำถามนี้: วิธีที่ดีที่สุดในการกำหนดจำนวนวันในหนึ่งเดือนด้วยจาวาสคริปต์คืออะไร? .


1
ฉันโยนมันลงในฟังก์ชันวันที่เพื่อความสะดวกในการใช้งาน ได้รับความไม่สอดคล้องกันของ 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; };
freshvolk

ดีใจที่คุณชอบมัน. อัลโกนี้ได้รับการทดสอบอย่างเต็มที่เป็นเวลา 2 ^ 31-1 ปี (มหันต์ 2147483647 นั่นคือ> 7500 เท่าของช่วงวันที่ - วัตถุของจาวาสคริปต์) ทั้งสองด้านของ 0 ( อาจใช้ได้กับ 2 ^ 32 แต่ฉันยังไม่ได้ทดสอบ ยัง). นอกจากนี้คุณอาจจะอ่านคำตอบของฉันอีกครั้ง: คุณสามารถโกน+1จากthis.getMonth()+1ถ้าคุณลบจาก-- แก้ไขดังนั้นฉันจะทำ (สำหรับห้องสมุด):--mDate.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(); };
GitaarLAB

ที่จริงฉันเพิ่งทำแบบนั้นตอนที่ดู! ฉันรู้ว่าฉันกำลังบวกหนึ่งแล้วลบมันทันที ฉันคิดว่าเมื่อถึงเวลา2^31 - 2016หลายปีนับจากนี้ js อาจจะล้าสมัยไปหน่อย
freshvolk

@Freshvolk: ฮ่า ๆ นั่นคือเหตุผลที่ฉันไม่เคยทดสอบมันเกิน 2 ^ 31 :)แต่ถึงตอนนั้นอย่างน้อยก็จะให้ผลลัพธ์ที่คาดเดาได้และสอดคล้องกันฮ่าฮ่า แก้ไขเพื่อประโยชน์ทางทฤษฎีโดยใช้ all-modulo algo แบบเดิมที่ช้ากว่าสำหรับการก้าวกระโดดช่วงนี้สามารถขยายได้ถึง 2 ^ 53 การค้นหาเดือนไม่ได้เป็นปัจจัย จำกัด
GitaarLAB

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

4

ถ้าฉันเข้าใจคุณถูกต้องคุณต้องการ 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();
}

(ใช่ฉันทำแบบนั้นจริงๆโดยไม่ได้คัดลอกหรือวางถ้ามีวิธีง่ายๆฉันจะบ้า)


วิธีง่ายๆของฉันก็แค่ใช้เวทมนตร์หมายเลข1054267675และเรียกมันว่าวันฉันขี้เกียจพิมพ์ทั้งหมด:)
GitaarLAB

4

นี่เป็นวิธีง่ายๆในการค้นหาวันปัจจุบันของปีและควรคำนึงถึงปีอธิกสุรทินโดยไม่มีปัญหา:

javascript:

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

Javascript ใน Google Apps Script:

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()คือเพื่อให้แน่ใจว่าวันของปีจะถูกปัดเศษอย่างถูกต้องโดย

เมื่อคุณมีจำนวนมิลลิวินาทีของปีปัจจุบันแล้วคุณสามารถแปลงเวลานี้เป็นวันโดยหารด้วย 1,000 เพื่อแปลงมิลลิวินาทีเป็นวินาทีจากนั้นหารด้วย 60 เพื่อแปลงวินาทีเป็นนาทีจากนั้นหารด้วย 60 เพื่อแปลงนาทีเป็นชั่วโมง และสุดท้ายหารด้วย 24 เพื่อแปลงชั่วโมงเป็นวัน

หมายเหตุ: โพสต์นี้ได้รับการแก้ไขเพื่อพิจารณาความแตกต่างระหว่าง JavaScript และ JavaScript ที่ใช้งานใน Google Apps Script นอกจากนี้ยังมีการเพิ่มบริบทเพิ่มเติมสำหรับคำตอบ


คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหมว่ามันใช้งานได้
Stephen Reindl

ใช่ ขออภัยที่ไม่ได้ให้รายละเอียดเพิ่มเติมในตอนแรก นิพจน์ภายในคำสั่ง "Math.round" จะค้นหาจำนวนมิลลิวินาทีในปีปัจจุบันโดยการลบวันแรกของปีออกจากมิลลิวินาทีของชั่วโมงสุดท้ายของวันปัจจุบันของปี จากนั้นจำนวนมิลลิวินาทีจะหารด้วย 1,000 เพื่อแปลงเป็นวินาที 60 ในการแปลงเป็นนาที 60 เพื่อแปลงเป็นชั่วโมงและ 24 เพื่อแปลงเป็นวัน นิพจน์จะรวมอยู่ในฟังก์ชัน "Math.round ()" เพื่อให้ปัดเศษเป็นจำนวนเต็มสำหรับวันของปี
Liz Page-Gould

ฉันควรเพิ่มด้วยว่าโดยพื้นฐานแล้วโซลูชันนี้เหมือนกับโซลูชันที่ "ยอมรับ" ยกเว้นว่าจะทำทุกอย่างในโค้ดบรรทัดเดียว
Liz Page-Gould

บัญชีนี้ใช้เวลาออมแสงได้อย่างไร? จะไม่ทำให้มันหลุดออกไปสักหน่อยเหรอ?
Dan Oswalt

ไม่เวลาออมแสงจะไม่ส่งผลต่อการคำนวณนี้เนื่องจากเวลาออมแสงมีความละเอียดของชั่วโมงและรหัสนี้กำลังคำนวณจำนวนวันในปีนั้น ยิ่งไปกว่านั้นเนื่องจากฉันใช้.setHoursวิธีการบนวัตถุวันที่แรกและระบุเวลาของวันด้วยวัตถุวันที่สองการเปลี่ยนแปลงจากเวลาออมแสง 1 ชั่วโมงจะไม่ส่งผลต่อเวลาของวัตถุวันที่ที่ใช้ในการคำนวณนี้
Liz Page-Gould

4

ฉันคิดว่าสิ่งนี้ตรงไปตรงมามากกว่า:

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!

3

วิธีนี้คำนึงถึงปัญหาเขตเวลาและเวลาออมแสง

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

(เอามาจากที่นี่ )

ดูซอนี้ด้วย


สิ่งนี้ไม่มีประสิทธิภาพเล็กน้อย แต่ทำงานได้อย่างสมบูรณ์ คุณสามารถกำหนดเวลาให้วัตถุและ setDate เปลี่ยนวันที่เป็นวันที่ 1 ซึ่งทั้งสองได้รับการสนับสนุนอย่างสมบูรณ์แบบ แนวคิดคือคุณจะไม่เคลื่อนที่ไปตามเขตเวลา นอกจากนี้ยังถือว่าเวลาออมแสงน้อยกว่า 12 ชั่วโมง ... ซึ่งเป็นสมมติฐานที่ปลอดภัยบนโลก
Ray Foss

2

Math.round ((วันที่ใหม่ () setHours (23) - วันที่ใหม่ (วันที่ใหม่ () getFullYear (), 0, 1, 0, 0, 0)) / 1000/86400);

เพิ่มประสิทธิภาพคำตอบเพิ่มเติม

ยิ่งไปกว่านั้นการเปลี่ยน setHours (23) หรือศูนย์สุดท้าย แต่สองในภายหลังเป็นค่าอื่นอาจให้วันของปีที่เกี่ยวข้องกับเขตเวลาอื่น ตัวอย่างเช่นในการดึงทรัพยากรจากยุโรปที่อยู่ในอเมริกา


1
Math.floor((Date.now() - Date.parse(new Date().getFullYear(), 0, 0)) / 86400000)

นี่คือทางออกของฉัน


ฉันใช้วันที่ 31 ธันวาคมของปีก่อนเพื่อสร้าง 1 มกราคม = DOY 01 นอกจากนี้ฉันพบว่าวันที่ 10 มีนาคมมี DOY เหมือนกับวันที่ 11 มีนาคมเพราะหนึ่งคือ 69 และอีก 69.9 บางอย่างทั้งสองปูพื้นเป็น 69 การทำ Math.round แทนแก้ไขสิ่งนี้ . ฉันเชื่อว่าวันที่ 10 มีนาคมหรือ 11 มีนาคมเป็นการเปลี่ยนเวลาออมแสงซึ่งทำให้วันที่ 11 มีนาคมไม่ค่อยเป็น DOY 70
user3015682

0

ฉันได้สร้างสิ่งที่สามารถอ่านได้และจะทำเคล็ดลับอย่างรวดเร็วรวมถึงจัดการวัตถุ JS Date ที่มีเขตเวลาที่แตกต่างกัน

ฉันได้รวมกรณีทดสอบไว้ค่อนข้างน้อยสำหรับโซนเวลา DST วินาทีอธิกสุรทินและ Leap years

PS ECMA-262 ละเว้นวินาทีอธิกสุรทินซึ่งแตกต่างจาก UTC หากคุณต้องการแปลงเป็นภาษาที่ใช้ 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"
);


0

ฉันต้องการจัดเตรียมโซลูชันที่คำนวณโดยเพิ่มวันสำหรับแต่ละเดือนก่อนหน้า:

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

วิธีนี้ทำให้คุณไม่ต้องจัดการรายละเอียดของปีอธิกสุรทินและการออมแสง


0

อีกทางเลือกหนึ่งโดยใช้การประทับเวลา UTC เช่นเดียวกับที่คนอื่น ๆ ระบุว่าวันที่ 1 ของเดือนคือ 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);

0

คุณสามารถส่งพารามิเตอร์เป็นหมายเลขวันที่ใน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)

0

สิ่งนี้อาจเป็นประโยชน์สำหรับผู้ที่ต้องการวันของปีเป็นสตริงและมี jQuery UI

คุณสามารถใช้ 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())

0

สำหรับผู้ที่ต้องการโซลูชันทางเลือกที่รวดเร็ว

(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");
})();

ขนาดของเดือนของปีและวิธีการทำงานของ Leap Years เหมาะสมอย่างยิ่งในการรักษาเวลาของเราให้สอดคล้องกับดวงอาทิตย์ เฮ็คมันทำงานได้อย่างสมบูรณ์แบบที่เราเคยทำก็แค่ปรับเพียงไม่กี่วินาทีตรงนี้และตรงนั้น ระบบปีอธิกสุรทินปัจจุบันของเรามีผลบังคับใช้ตั้งแต่วันที่ 24 กุมภาพันธ์ 1582และมีแนวโน้มที่จะมีผลบังคับใช้ในอนาคตอันใกล้

อย่างไรก็ตาม DST อาจมีการเปลี่ยนแปลงได้มาก อาจเป็นไปได้ว่า 20 ปีนับจากนี้บางประเทศอาจหักล้างเวลาโดยทั้งวันหรือช่วงเวลาอื่น ๆ สำหรับ DST DST ทั้งวันแทบจะไม่เกิดขึ้นอย่างแน่นอน แต่ DST ก็ยังคงอยู่ในอากาศและไม่เด็ดขาด ดังนั้นวิธีแก้ปัญหาข้างต้นจึงเป็นข้อพิสูจน์ในอนาคตนอกเหนือจากความรวดเร็วมาก

ข้อมูลโค้ดด้านบนทำงานเร็วมาก คอมพิวเตอร์ของฉันสามารถประมวลผลวันที่ 65536 ใน ~ 52ms บน Chrome


0

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


0

นี่เป็นวิธีแก้ปัญหาที่หลีกเลี่ยงปัญหา Date object และ timezone ที่เป็นปัญหาโดยกำหนดให้วันที่ป้อนข้อมูลของคุณอยู่ในรูปแบบ "yyyy-dd-mm" หากคุณต้องการเปลี่ยนรูปแบบให้แก้ไขฟังก์ชัน date_str_to_parts:

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

-1

มันทำให้ฉันกังวลเสมอเมื่อผสมคณิตศาสตร์กับฟังก์ชันวันที่ (มันง่ายมากที่จะพลาดรายละเอียดอื่น ๆ ในปีอธิกสุรทิน) สมมติว่าคุณมี:

var d = new Date();

ฉันขอแนะนำให้ใช้วันต่อไปนี้จะถูกบันทึกไว้ในday:

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

ไม่เห็นเหตุผลใด ๆ ว่าทำไมสิ่งนี้ถึงไม่ได้ผล (และฉันจะไม่กังวลเกี่ยวกับการทำซ้ำสองสามครั้งเนื่องจากจะไม่ถูกใช้อย่างเข้มข้น)


-1

/ * ใช้สคริปต์นี้ * /

var today = new Date();
var first = new Date(today.getFullYear(), 0, 1);
var theDay = Math.round(((today - first) / 1000 / 60 / 60 / 24) + .5, 0);
alert("Today is the " + theDay + (theDay == 1 ? "st" : (theDay == 2 ? "nd" : (theDay == 3 ? "rd" : "th"))) + " day of the year");
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.