Javascript рассчитать день года (1 - 366) - PullRequest
101 голосов
/ 23 декабря 2011

Как мне использовать javascript для вычисления дня года, от 1 до 366? Например:

  • 3 января должно быть 3 .
  • 1 февраля должно быть 32 .

Ответы [ 20 ]

111 голосов
/ 23 декабря 2011

После редактирования 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до 1: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);
46 голосов
/ 17 октября 2014

Это работает при изменениях летнего времени во всех странах (вышеуказанный «полдень» в Австралии не работает):

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;
};
21 голосов
/ 05 декабря 2016

Мне очень интересно, что никто не рассматривал возможность использования 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
17 голосов
/ 24 декабря 2011
Date.prototype.dayOfYear= function(){
    var j1= new Date(this);
    j1.setMonth(0, 0);
    return Math.round((this-j1)/8.64e7);
}

alert(new Date().dayOfYear())
9 голосов
/ 06 января 2015

К счастью, этот вопрос не определяет, требуется ли номер текущего дня, оставляя место для этого ответа.
Также у некоторых ответов (также на другие вопросы) были проблемы високосного года илииспользовал объект Date.Хотя javascript Date object охватывает примерно 285616 лет (100 000 000 дней) по обе стороны от 1 января 1970 года, я был сыт по горло всевозможными неожиданными несоответствиями в разных браузерах (особенно в год 0–99).Мне также было любопытно, как его вычислить.

Поэтому я написал простой и, прежде всего, маленький алгоритм для вычисления правильного ( Proleptic Григорианский / Астрономический / ISO 8601: 2004 (пункт 4.3.2.1), поэтому год 0 существует и является високосным годом и поддерживается отрицательных лет )день года на основе год , месяц и день .
Обратите внимание, что в обозначениях AD/BC год 0 нашей эры / BC не существует:вместо этого год 1 BC является високосным годом!ЕСЛИ вам нужно учесть нотацию BC, то просто сначала вычтите один год из (в противном случае положительного) значения года !!

Я изменил (для javascript) алгоритм short-circuit bitmask-modulo leapYear и придумал магическое число, чтобы выполнить побитовый поиск смещений (который исключает 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 строки для удобства чтения (и последующего объяснения).

Последняя строка идентична функции, описанной выше, однако (идентичный) алгоритм leapYear перемещен в предыдущую секцию короткого замыкания (до вычисления числа дня), поскольку также необходимо знать, сколькодней в месяце в данном (високосном) году.

Средняя линия вычисляет правильное смещение число (для максимального количества дней) для данного месяца в данном (високосном) году, используя другое магическое число: с 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.

Extra:
Примечание. Не используйте алгоритм дня за месяцесли вам нужен только максимальный день в месяц.В этом случае есть более эффективный алгоритм (потому что нам нужен только leepYear, когда месяц февраль). Я написал в ответ на этот вопрос: Как лучше всего определить количество дней в месяце с помощью javascript? .

4 голосов
/ 23 декабря 2011

Ну, если я вас правильно понимаю, вы хотите 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();
}

(Да, я действительно сделал это без копирования или вставки. Если есть простой способ, я буду злиться)

4 голосов
/ 07 марта 2015

Это простой способ найти текущий день в году, и он должен без проблем учитывать високосные годы:

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Скрипт), из миллисекунд 23-го часа текущего дня, который был найден с new Date().setHours(23).Целью установки текущей даты на 23-й час является обеспечение правильного округления дня года на Math.round().

Если у вас есть количество миллисекунд текущего года, вы можете преобразовать этовремя в днях путем деления на 1000 для преобразования миллисекунд в секунды, затем деления на 60 для преобразования секунд в минуты, затем деления на 60 для преобразования минут в часы и, наконец, деления на 24 для преобразования часов в дни.

Примечание. Это сообщение было отредактировано с учетом различий между JavaScript и JavaScript, реализованными в скрипте Google Apps.Также был добавлен дополнительный контекст для ответа.

4 голосов
/ 02 июля 2017

Если вы не хотите заново изобретать колесо, вы можете использовать превосходную библиотеку date-fns (node.js):

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

var dayOfYear = getDayOfYear(new Date(2017, 1, 1)) // 1st february => 32
3 голосов
/ 16 октября 2014

Этот метод учитывает проблему часового пояса и перехода на летнее время

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

(взято из здесь )

См. Также скрипка

2 голосов
/ 01 мая 2018

Я думаю, что это более просто:

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!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...