БлогNot. JavaScript: номер недели и дня в году, чётная/нечётная недели и другое

JavaScript: номер недели и дня в году, чётная/нечётная недели и другое

Сделал в спешке по практической необходимости, может, ещё кому пригодится. Скрипт позволяет не только вывести корректный номер недели по ИСО, но и скажет, сколько дней осталось до Нового года или определит номер недели в учебном заведении. Для последней цели его, конечно, может понадобиться изменить. Ясно также, что, как любой Javascript, скрипт время берёт с часов компьютера, они должны идти правильно.

В отличие от решения на PHP, где есть готовый формат, или в Excel, умеющем складывать дату и дни, в Яваскрипте возможности по операциям над датами существенно более ограничены.

Правда, в инете можно часто встретить мифический код вроде

var today = new Date();
var weekNumber = today.toLocaleFormat("%U");
alert (weekNumber);

Но такого метода как toLocaleFormat нет в распространённом стандарте Javascript. К примеру, под IE8, Chrome и Opera toLocaleFormat вообще отсутствует. Тем более, даже если метод есть (в Firefox), то он локализует строку даты согласно настройкам операционной системы, которые могут не совпадать с региональными настройками браузера и приводить к непредсказуемым результатам. Так что напишем всё "ручками".

Из приведённого ниже листинга имеет смысл обратить внимание на следующие функции:

  • delta (year) - разница в днях текущей даты с новым годом для года year. Для 1 января даст значение 365 или 366, для 31 декабря - 1 день, т.к. округляет разницу меток времени вниз. Считая "рассстояние" от минувшего нового года и прибавляя к ответу 1, функцией можно выводить номер дня в году.
  • getWeekNum (day,month,year) - корректно, то есть, по стандарту ИСО определяет номер недели в году из диапазона 01-52 (описано тут; учтите, что 1 января вполне может быть 52-й неделей предыдущего года, это правильно).
  • weekInfo () - после вывода информации о номере недели по ИСО пытается определять номер недели в учебном заведении, возможно, эта часть нуждается в переписывании, так как в неё заложены конкретные правила: изменены! см. ниже
    • нечётная/чётная (первая/вторая) неделя определяется номером недели в году по ИСО, то есть, первая по порядку неделя семестра может быть и "второй" (чётной) в смысле расписания;
    • осенний семестр начинается 1 сентября, если это не Сб или Вс, тогда со следующего Пн;
    • весенний семестр начинается через 20 недель после осеннего (15 недель учёбы + 3 сессии + 2 новогодних каникул).

Всё это можно поменять, почитав комментарии в теле функции.

Вот скрипт в работе и листинг:

В некоторые дни выводятся не все 4 строки, а только 2 или 3 (например, когда "осталось 0 дней" или не идёт никакой семестр).

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <meta http-equiv="content-type" content="text/html; charset=Windows-1251">
 <title>Номер недели</title>
</head><body>


<script type="text/javascript">

function goodwordform(k,w,o1,o2,o5) { //Корректное склонение окончания слова
 if ( (k%100>10 && k%100<20) || k%10>4 || k%10==0) w+=o5;
 else if (k%10==1) w+=o1;
 else w+=o2;
 return w;
}

function delta (year) { //Разница в днях текущей даты с новым годом для года year
 var date = new Date();
 var newYear = new Date(year, 0, 1);
 return (Math.floor((date.getTime() - newYear.getTime())/1000/60/60/24));
}

function newYearDays () { //Выводим инфо о ближайших Новых годах
 var date = new Date();
 var Y = date.getFullYear();
 var delta1 = delta (Y);
 document.getElementById('info').innerHTML = '';
 if (delta1>0) document.getElementById('info').innerHTML += 'С Нового года '+goodwordform(delta1,'прош','ёл','ло','ло')+
  ' '+delta1+' '+ goodwordform(delta1,'д','ень','ня','ней')+'<br>'+"\n";
 var delta2 = Math.abs(delta (Y+1));
 document.getElementById('info').innerHTML += 'До следующего Нового года '+goodwordform(delta2,'остал','ся','ось','ось')+
  ' '+delta2+' '+ goodwordform(delta2,'д','ень','ня','ней')+'<br>'+"\n";
}

var calStartDOW = 1; //С чего начинать неделю, в США день 0 (Вс), в мире день 1 (Пн)

function getWeekNum (day,month,year) { //Корректно определяем номер недели в году
 if (calStartDOW == 0) day++; //Чтоб работало и для САЩ :)
 month++; //в JS месяцы нумеруются с нуля!
 var a = Math.floor((14-month) / 12);
 var y = year + 4800 - a;
 var m = month + 12 * a - 3;
 var J = day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y/4) - 
  Math.floor(y/100) + Math.floor(y/400) - 32045;
 d4 = (((J + 31741 - (J % 7)) % 146097) % 36524) % 1461;
 var L = Math.floor(d4 / 1460);
 var d1 = ((d4 - L) % 365) + L;
 var week = Math.floor(d1/7) + 1;
 if (week<10) week='0'+week; //Лидирующий ноль для недель 1-9
 return week;
}

function numWeekSep (Y) { //Найти номер недели начала учебного года для года Y
 var date1 = new Date(Y,9-1,1);
 var wd1=date1.getDay();
 var nw1=getWeekNum(1,9-1,Y);
 if (wd1==0 || wd1==6) nw1++; //Если 1 сент. - Сб или Вс, начнём со след. Пн
 return nw1;
}

function weekInfo () { //Выводим инфо о номере недели в году и семестре
 var date = new Date();
 var Y = date.getFullYear();
 var M = date.getMonth();
 var D=date.getDate();
 var NW=getWeekNum(D,M,Y);
 document.getElementById('info').innerHTML += 'Номер недели в году по стандарту ИСО: '+NW+'<br>'+"\n";
 //Ниже - "неуниверсальная" часть функции
 //Определяем неделю начала учебного года и номер недели в осеннем семестре
 if (M>8-1) { //осенний семестр - с 1 сентября, если оно не Сб или Вс, тогда со след. Пн
  var nw1 = numWeekSep (Y);
  var num=NW-nw1+1; //номер недели семестра
  if (num>0 && num<16) { //Показываем не дольше 15 недель
   document.getElementById('info').innerHTML += 'Номер недели в осеннем семестре: '+num;
   if (NW%2==0) document.getElementById('info').innerHTML += ' (нижняя)'; //Верхняя/нижняя (нечетная/четная)
   else document.getElementById('info').innerHTML += ' (верхняя)'; //определяется номером недели по ISO
   document.getElementById('info').innerHTML += '<br>'+"\n"; //так что 1-я по порядку неделя может быть и "нижней"
  }
 }
 else if (M<7-1) { //весенний семестр - NED недель спустя, но не раньше января и кончится не позже июля
  var NED=20;
  var nw1 = numWeekSep (Y-1);
  //Ищем, когда прошло NED недель с начала учебного года (следующий Пн):
  var nw2=getWeekNum(28,12-1,Y-1); //28 дек. гарантированно относится к прошлому году
  var w28 = nw2-nw1+1;
  var date2 = new Date(Y-1,12-1,28);
  var wd28 = date2.getDay();
  var t28 = date2.getTime();
  while (!(wd28==1 && w28==NED)) { //ищем Пн, наступивший NED недель спустся после начала осеннего семестра
   t28+=1000*60*60*24; //прибавить сутки
   date2.setTime(t28);   
   wd28 = date2.getDay();
   if (wd28 == 1) w28++;
  }
  //Это будет начало весеннего семестра:
  var date3 = new Date();
  date3.setTime(t28);
  var y2 = date3.getFullYear();
  var m2 = date3.getMonth();
  var d2 = date3.getDate();
  var nw2=getWeekNum(d2,m2,y2);
  var num=NW-nw2+1; //номер недели семестра
  if (num>0 && num<21) { //Показываем не дольше 20 недель
   document.getElementById('info').innerHTML += 'Номер недели в весеннем семестре: '+num;
   if (NW%2==0) document.getElementById('info').innerHTML += ' (нижняя)';
   else document.getElementById('info').innerHTML += ' (верхняя)';
   document.getElementById('info').innerHTML += '<br>'+"\n";
  }
 }
}

function main () {
 newYearDays (); 
 weekInfo (); 
 window.setTimeout('main()',60000); //Обновлять раз в минуту
}

document.writeln ('<p><small><span id="info"></span></small></p>');
main();
</script>
<noscript>Извините, для работы приложения требуется включённый Javascript</noscript>

</body></html>

P.S. Когда семестры стали опять 17(18) и 16(17) недель, поменялось только следующее и только в функции weekInfo:

***** было
  if (num>0 && num<16) { //Показываем не дольше 15 недель
***** стало
  if (num>0 && num<18) { //Показываем не дольше 17 недель
*****

***** было
  var NED=20;
***** стало
  var NED=23;
*****

***** было
  if (num>0 && num<21) { //Показываем не дольше 20 недель
***** стало
  if (num>0 && num<18) { //Показываем не дольше 17 недель
*****

P.P.S. Вот совсем короткий вариант кода, где мы для получения номера учебной недели отнимаем от номера недели в году стартовый номер недели по ISO (в нашем случае значение startWeek) и пишем вывод в текстовый элемент HTML (у нас с id="weekinfo"). Скрипт будет работать weeks недель, эти 2 настройки приведены в начале кода.

<span id="weekinfo" style="font-size: 80%;"></span>
<script>
 let startWeek = 4;
 let weeks = 17;
 Date.prototype.getWeek = function(year, month, day) { //Номер недели по ISO
  let date = new Date(year, month, day, 0, 0, 0, 0);
  date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
  let week1 = new Date(date.getFullYear(), 0, 4);
  return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
 }
 let date = new Date(); 
 let year = date.getFullYear();
 let mon = date.getMonth();
 let day = date.getDate();
 let wday = date.getDay();
 let wdays = ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'];
 let wnum = date.getWeek(year, mon, day) - startWeek + 1;
 if (wnum > 0 && wnum <= weeks) {
  document.getElementById('weekinfo').innerText = 'Сегодня ' + 
   wdays[wday] + ', ' + day.toString().padStart(2,'0') + '.' + 
   (mon+1).toString().padStart(2,'0') + '.' + year.toString() + 
   ', '+ wnum  + ' неделя';
 }
</script>

02.10.2013, 10:37 [19651 просмотр]


теги: javascript дата

показать комментарии (1)