JavaScript oblicza dzień w roku (1 - 366)


119

Jak za pomocą JavaScript obliczyć dzień w roku od 1 do 366? Na przykład:

  • 3 stycznia powinien być 3 .
  • 1 lutego powinien być 32 .

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

Ale naprawdę nie jestem pewien, co masz na myśli. Chcesz tylko liczbę dni w roku? Albo między dwiema randkami?
— Alex Turpin,

16
fyi @ xeon06, obliczanie roku przestępnego jest nieco bardziej skomplikowane niż modyfikowanie o 4. patrz: algorytm roku przestępnego
— Matt Felzani

4
@ Xeon06: To prawda tylko w większości przypadków. Z Wikipedii : Lata podzielne równo przez 100 nie są latami przestępnymi, chyba że są one również podzielne równo przez 400, w którym to przypadku są to lata przestępne.
— Cameron

No cóż, poprawiono mnie. Wydaje się, że @minitech ma właściwą odpowiedź.
— Alex Turpin

Odpowiedzi:


138

Następująca edycja 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);

Edycja: powyższy kod nie powiedzie się, jeśli nowjest data między 26 marca a 29 października now, a godzina przypada przed 1 w nocy (np. 00:59:59). Wynika to z faktu, że kod nie uwzględnia czasu letniego. Powinieneś to zrekompensować :

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.floorkonsekwentnie dawał mi wynik o 1 dzień mniej niż oczekiwano po pewnym kwietniowym dniu. Math.ceildziałało zgodnie z oczekiwaniami, ale widziałem, że zaleca się użycie Math.roundzamiast jednego z nich.
— baacke

3
Składnik dnia to podstawa-1. tj. aby reprezentować 1 stycznia tego roku, użyjesz new Date(2014, 0, 1), a nie new Date(2014, 0, 0)tak, jak tutaj. Czy to zamierzone? Być może to właśnie wyjaśnia, że ​​jeden dzień jest wolny, co new Date(2014, 0, 0)wróci 12/31/2013.
— Kirk Woll,

1
Może użyj .setUTCHoursi Date.UTC()dla bardziej niezawodnego rozwiązania.
— Noyo

6
@AlexTurpin, @ T30: Wiem, że to trochę stare, ale jeśli się zastanawiałeś ... problem jest spowodowany tym, że czas letni zaczyna się w marcu. Dzieje się tak, ponieważ kiedy weźmiesz różnicę między północą w dniu przed czasem letnim a północą w dniu po czasie letnim, nie będziesz mieć liczby milisekund równo podzielnej przez 1000 * 60 * 60 * 24 (będzie to dokładnie jedna godzina przerwy) . Najłatwiejszym rozwiązaniem jest użycie ceilzamiast floor, to da ci system numeracji, w którym 1 stycznia = 1. Jeśli chcesz, aby 1 stycznia = 0 (jak floorbyś dał), po prostu odejmij 1 od końcowego wyniku.
— Warren R.

2
Aby wziąć pod uwagę strefy czasowe i czas letni, zmień wiersz 3 na: var diff = now - start + (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
— Marlon

46

Działa to niezależnie od zmian czasu letniego we wszystkich krajach (opcja „południe” powyżej nie działa w Australii):

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
Zgadzam się, że w zaakceptowanej odpowiedzi są błędy związane z czasem letnim. Powyższe rozwiązanie jest lepsze i szybsze. Oto jego odmiana, którą przetestowałem na jsPerf jsperf.com/date-getdayofyear-perf
— Shyam Habarakada,

@ShyamHabarakada: kod w twoim benchmarku jest uszkodzony, getDay()należy go zmienić na getDate(). Ta pierwsza zwraca dzień tygodnia (0 = niedziela..6 = sobota), a nie dzień.
— CodeManX

32

Uważam za bardzo interesujące, że nikt nie rozważał używania UTC, ponieważ nie podlega on DST. Dlatego proponuję co następuje:

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

Możesz to przetestować, wykonując następujące czynności:

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

Jakie dane wyjściowe za rok przestępny 2016 (zweryfikowano na stronie 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

Może to spowodować przepełnienie, ponieważ liczba milisekund jest zbyt duża.
— knt5784

@ knt5784 Matematyka może się przepełniać, ale jego przykład pokazuje największą możliwą wartość rzeczywistą, więc powinieneś być w porządku z tym, chyba że występuje problem z przeglądarką z przepełnieniem całkowitych.
— Per Fagrell

Osobiście podoba mi się to podejście. Znacznie czystszy (i bardzo sprytny!) Sposób na uniknięcie problemów z czasem letnim. Jeśli ktoś się zastanawia, jak przekonwertować z DOY do tej pory, jest to znacznie prostsze. Zasadniczo po prostu tworzysz datę stycznia DOY. Na przykład 59 stycznia === 28 lutego. Dzień przestępny jest obsługiwany dobrze. function doyToDate(doy, year) { return new Date(year, 0, doy); }
— Kip

@ knt5784 Nie ma ryzyka przepełnienia milisekund z tym kodem, przynajmniej nie przez następne 200 000 lat. Największa data obsługiwana przez Date to new Date(8640000000000000), która wciąż jest mniejsza niż Number.MAX_SAFE_INTEGER. zobacz odpowiedź: 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
To nie przejdzie kłaczków ... Modyfikowanie obiektu, który nie jest twój, szczególnie globalnego. To również zawiodłoby na planetach o ekstremalnym nachyleniu, gdzie czas letni przekracza 12 godzin. Ale bardziej realistycznie, jeśli kodujesz przeglądarkę, która umożliwia zmianę strefy czasowej obiektu Date, strefą czasową j1 może być Australia, a strefą czasową może być Alaska, co przerywa zaokrąglanie.
— Ray Foss

11

Na szczęście to pytanie nie określa, czy wymagany jest numer bieżącego dnia, pozostawiając miejsce na tę odpowiedź.
Również niektóre odpowiedzi (również na inne pytania) zawierały problemy z rokiem przestępnym lub wykorzystywały obiekt Date. Chociaż javascript Date objectobejmuje około 285616 lat (100 000 000 dni) po obu stronach 1 stycznia 1970 r., Miałem dość wszelkiego rodzaju nieoczekiwanych niespójności dat w różnych przeglądarkach (zwłaszcza w latach od 0 do 99). Byłem też ciekawy, jak to obliczyć.

Napisałem więc prosty i przede wszystkim mały algorytm do obliczania prawidłowego ( Proleptic Gregorian / Astronomical / ISO 8601: 2004 (punkt 4.3.2.1), więc rok0 istnieje i jest rokiem przestępnym, a lata ujemne są obsługiwane ) dzień roku na podstawie roku , miesiąca i dnia .
Zauważ, że w AD/BCnotacji rok 0 AD / BC nie istnieje: zamiast tego rok 1 BCjest rokiem przestępnym! JEŚLI musisz uwzględnić notację BC, po prostu odejmij najpierw jeden rok (w przeciwnym razie dodatniej) wartości roku !!

Zmodyfikowałem (dla javascript) algorytm zwarcia bitmask-modulo leapYear i wymyśliłem magiczną liczbę, aby wykonać bitowe wyszukiwanie przesunięć (które wykluczają jan i feb, a zatem potrzebuję 10 * 3 bitów (30 bitów to mniej niż 31 bitów, dzięki czemu możemy bezpiecznie zapisać inny znak na przesunięciu bitu zamiast >>>)).

Pamiętaj, że nie może być ani miesiąc ani dzień 0. Oznacza to, że jeśli potrzebujesz tego równania tylko na bieżący dzień (karmienie go za pomocą .getMonth()), wystarczy usunąć --from --m.

Zauważ, że zakłada to prawidłową datę (chociaż sprawdzanie błędów to tylko kilka znaków więcej).

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>


Oto wersja z poprawną walidacją zakresu.

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>

Znowu jedna linia, ale podzieliłem ją na 3 linie dla większej czytelności (i po wyjaśnieniu).

Ostatni wiersz jest identyczny z powyższą funkcją, jednak (identyczny) algorytm roku przestępnego zostaje przeniesiony do poprzedniej sekcji zwarciowej (przed obliczeniem numeru dnia), ponieważ trzeba również wiedzieć, ile dni w miesiącu ma dany rok (przestępny).

Środkowa linia oblicza poprawną liczbę przesunięcia (dla maksymalnej liczby dni) dla danego miesiąca w danym (przestępnym) roku przy użyciu innej magicznej liczby: ponieważ 31-28=3i 3wynosi tylko 2 bity, to 12*2=24bity, możemy przechowywać wszystkie 12 miesięcy. Ponieważ dodawanie może być szybsze niż odejmowanie, dodajemy przesunięcie (zamiast odejmować 31). Aby uniknąć gałęzi decyzyjnej roku przestępnego w lutym, modyfikujemy tę magiczną liczbę wyszukiwania w locie.

To pozostawia nam (dość oczywistą) pierwszą linię: sprawdza, czy miesiąc i data mieszczą się w prawidłowych granicach i zapewnia nam falsezwrot wartości w przypadku błędu zakresu (zwróć uwagę, że ta funkcja również nie powinna być w stanie zwrócić 0, ponieważ 1 stycznia 0000 jest jeszcze dzień 1.), zapewniając łatwy sprawdzanie błędów: if(r=dayNo(/*y, m, d*/)){}.
Jeśli jest używany w ten sposób (gdzie miesiąc i dzień mogą nie występować 0), można zmienić --m>=0 && m<12na m>0 && --m<12(zapisując kolejny znak).
Powodem, dla którego wpisałem fragment w jego aktualnej formie, jest to, że dla wartości miesiąca opartych na 0 wystarczy usunąć --from --m.

Dodatkowo:
Uwaga, nie używaj algorytmu tego dnia na miesiąc, jeśli potrzebujesz tylko maksymalnej liczby dni w miesiącu. W takim przypadku istnieje bardziej wydajny algorytm (ponieważ potrzebujemy leepYear tylko wtedy, gdy miesiąc jest luty) jako odpowiedź na pytanie: Jaki jest najlepszy sposób określenia liczby dni w miesiącu za pomocą javascript? .


1
Wrzuciłem to do funkcji daty, aby ułatwić korzystanie z niej. Niespójności dotyczące daty () nadal stanowią problem, ale używam lat 2015+, więc mam nadzieję, że są spójne. Biedny JSHint zginął próbując zweryfikować twój kod hahah. 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

Cieszę się, że Ci się podobało. Ten algorytm jest w pełni testowany przez 2 ^ 31-1 lat (imponujące 2147483647, to jest> 7500 razy więcej niż zakres obiektu daty w JavaScript) po obu stronach 0. ( Może działać dla 2 ^ 32, ale nie testowałem tego jeszcze). Ponadto, można ponownie przeczytać moją odpowiedź: można golić się +1z this.getMonth()+1jeśli usunąć --z --m. EDYCJA Tak więc zrobiłbym (dla biblioteki):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(); };
— GitaarLAB

Właściwie to właśnie to zrobiłem, kiedy na to patrzyłem! Zdałem sobie sprawę, że dodaję jeden, a potem natychmiast go odejmuję. Myślę, że zanim dojdziemy do 2^31 - 2016lat, js prawdopodobnie będzie trochę przestarzały.
— freshvolk

@Freshvolk: lol, dlatego nigdy nie testowałem go powyżej 2 ^ 31 :)Ale do tego czasu da przynajmniej przewidywalne i spójne wyniki haha. EDYTUJ , tylko dla teorii, używając wolniejszego konwencjonalnego algorytmu all-modulo dla leapyear, zakres można rozszerzyć do 2 ^ 53. Algorytm wyszukiwania miesiąca nie jest czynnikiem ograniczającym.
— GitaarLAB

5

Jeśli nie chcesz wymyślać koła na nowo, możesz skorzystać z doskonałej biblioteki 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

Cóż, jeśli dobrze cię rozumiem, chcesz 366 w roku przestępnym, 365 w przeciwnym razie, prawda? Rok jest rokiem przestępnym, jeśli jest podzielny równo przez 4, ale nie przez 100, chyba że jest również podzielny przez 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;
    }
}

Edytuj po aktualizacji:

W takim przypadku nie sądzę, że istnieje wbudowana metoda; musisz to zrobić:

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

(Tak, faktycznie zrobiłem to bez kopiowania ani wklejania. Jeśli istnieje łatwy sposób, oszaleję)


Mój łatwy sposób to po prostu użycie magicznej liczby 1054267675 i nazwanie go dniem, jestem :)
— zbyt

4

Jest to prosty sposób na znalezienie bieżącego dnia w roku i powinien bez problemu uwzględniać lata przestępne:

JavaScript:

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

JavaScript w Google Apps Script:

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

Podstawowym działaniem tego kodu jest znalezienie liczby milisekund, które upłynęły w bieżącym roku, a następnie przeliczenie tej liczby na dni. Liczbę milisekund, które upłynęły w bieżącym roku, można znaleźć, odejmując liczbę milisekund pierwszej sekundy pierwszego dnia bieżącego roku, którą uzyskuje się za pomocą new Date(new Date().getYear()+1900, 0, 1, 0, 0, 0)(Javascript) lub new Date(new Date().getYear(), 0, 1, 0, 0, 0)(Google Apps Script), od milisekund 23 godziny bieżącego dnia, które znaleziono z new Date().setHours(23). Ustawienie bieżącej daty na 23. godzinę ma na celu zapewnienie prawidłowego zaokrąglenia dnia w roku o Math.round().

Gdy masz już liczbę milisekund bieżącego roku, możesz przekonwertować ten czas na dni, dzieląc przez 1000, aby zamienić milisekundy na sekundy, a następnie dzieląc przez 60, aby zamienić sekundy na minuty, a następnie przez 60, aby zamienić minuty na godziny, i na koniec dzieląc przez 24, aby zamienić godziny na dni.

Uwaga: ten post został zredagowany w celu uwzględnienia różnic między JavaScriptem a JavaScriptem zaimplementowanym w Google Apps Script. Do odpowiedzi dodano również szerszy kontekst.


Czy możesz wyjaśnić trochę więcej niż tylko powiedzenie, że działa?
— Stephen Reindl

Tak! Przepraszam, że przede wszystkim nie podałem więcej szczegółów. Wyrażenie w instrukcji „Math.round” znajduje liczbę milisekund w bieżącym roku, odejmując pierwszy dzień roku od milisekund ostatniej godziny bieżącego dnia roku. Liczba milisekund jest następnie dzielona przez 1000 w celu konwersji na sekundy, 60 w celu konwersji na minuty, 60 w celu konwersji na godziny i 24 w celu konwersji na dni. Wyrażenie jest opakowane w funkcję „Math.round ()”, dzięki czemu zaokrągla do liczby całkowitej określającej dzień roku.
— Liz Page-Gould

Powinienem też dodać, że to rozwiązanie jest zasadniczo takie samo jak rozwiązanie „zaakceptowane”, z wyjątkiem tego, że robi wszystko w jednej linii kodu.
— Liz Page-Gould

Jak to wpływa na czas letni? Czy to by tego trochę nie zepsuło?
— Dan Oswalt

Nie, czas letni nie wpłynie na te obliczenia, ponieważ czas letni ma rozdzielczość godzin, a ten kod oblicza liczbę dni w roku. Ponadto, ponieważ zastosowałem .setHoursmetodę na pierwszym obiekcie daty i określiłem porę dnia w drugim obiekcie daty, zmiana o 1 godzinę od czasu letniego nie wpłynie na czasy obiektów Date użytych w tych obliczeniach.
— Liz Page-Gould

4

Myślę, że jest to prostsze:

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

Ta metoda bierze pod uwagę kwestię strefy czasowej i czasu letniego

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

(wzięte stąd )

Zobacz też to skrzypce


Jest to trochę nieefektywne, ale działa doskonale. Możesz po prostu uzyskać czas dla obiektów i ustawićDate zmienić datę na dzień 1. Oba są doskonale obsługiwane. Chodzi o to, że nie będziesz się poruszać po strefach czasowych. Zakłada się również, że czas letni to mniej niż 12 godzin ... co jest bezpiecznym założeniem na Ziemi.
— Ray Foss

2

Math.round ((new Date (). SetHours (23) - new Date (new Date (). GetFullYear (), 0, 1, 0, 0, 0)) / 1000/86400);

dodatkowo optymalizuje odpowiedź.

Ponadto, zmieniając setHours (23) lub przedostatnie dwa zera później na inną wartość, można podać dzień roku związany z inną strefą czasową. Na przykład, aby pobrać z Europy zasób znajdujący się w Ameryce.


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

to jest moje rozwiązanie


Używam 31 grudnia poprzedniego roku, aby zrobić 01 stycznia = DOY 01. Również odkryłem, że 10 marca ma taki sam DOY jak 11 marca, ponieważ jeden miał 69, a drugi 69,9 coś, obie podłogi to 69. Wykonanie Math.round zamiast tego naprawiło . Uważam, że albo 10 marca, albo 11 marca to zmiana czasu letniego, co powoduje, że 11 marca nie jest całkiem DOY 70.
— user3015682

0

Stworzyłem taki, który jest czytelny i bardzo szybko załatwi sprawę, a także obsłuży obiekty JS Date z różnymi strefami czasowymi.

Dołączyłem sporo przypadków testowych dla stref czasowych, czasu letniego, sekund przestępnych i lat przestępnych.

PS ECMA-262 ignoruje sekundy przestępne, w przeciwieństwie do UTC. Jeśli miałbyś przekonwertować to na język, który używa prawdziwego UTC, możesz po prostu dodać 1 do 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

Chciałbym przedstawić rozwiązanie, które wykonuje obliczenia, dodając dni z każdego poprzedniego miesiąca:

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

W ten sposób nie musisz zarządzać szczegółami lat przestępnych i czasu letniego.


0

Alternatywa wykorzystująca znaczniki czasu UTC. Również, jak zauważyli inni, dzień wskazujący jako pierwszy miesiąc to 1 zamiast 0. Miesiąc zaczyna się jednak od 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

Możesz przekazać parametr jako numer daty w setDatefunkcji:

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

Może to być przydatne dla tych, którzy potrzebują dnia roku jako ciągu znaków i mają dostępny interfejs użytkownika jQuery.

Możesz użyć jQuery UI Datepicker:

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

Pod spodem działa tak samo, jak niektóre z już wymienionych odpowiedzi ( (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

Dla tych z nas, którzy chcą szybkiego alternatywnego rozwiązania.

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

Rozmiar miesięcy w roku i sposób, w jaki działają lata przestępne, doskonale wpisują się w utrzymywanie naszego czasu zgodnie ze słońcem. Do licha, działa tak doskonale, że wszystko, co robimy, to po prostu dostosowujemy tylko kilka sekund tu i tam . Nasz obecny system lat przestępnych obowiązuje od 24 lutego 1582 roku i prawdopodobnie pozostanie w mocy w dającej się przewidzieć przyszłości.

Czas letni może się jednak bardzo zmienić. Może się zdarzyć, że za 20 lat jakiś kraj może przesunąć czas o cały dzień lub inną skrajność dla czasu letniego. Cały dzień czasu letniego prawie na pewno nigdy się nie wydarzy, ale mimo to czas letni jest nadal bardzo aktywny i niezdecydowany. Dlatego powyższe rozwiązanie jest przyszłościowe, a ponadto jest bardzo szybkie.

Powyższy fragment kodu działa bardzo szybko. Mój komputer może przetwarzać 65536 dat w ~ 52 ms w przeglądarce 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

Jest to rozwiązanie, które pozwala uniknąć kłopotliwych problemów z obiektem Date i strefą czasową, wymaga, aby data wejściowa była w formacie „rrrr-dd-mm”. Jeśli chcesz zmienić format, zmodyfikuj funkcję 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

Zawsze się martwię, gdy mieszam matematykę z funkcjami daty (tak łatwo jest przegapić jakiś rok przestępny i inne szczegóły). Powiedz, że masz:

var d = new Date();

Sugerowałbym użycie następujących, dni zostaną zapisane w day:

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

Nie widzę powodu, dla którego to nie działałoby dobrze (i nie martwiłbym się tak kilkoma iteracjami, ponieważ nie będzie to używane tak intensywnie).


-1

/ * UŻYJ TEGO SKRYPTU * /

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");
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.