БлогNot. Текущий прогресс в неделях, мини-информер

Текущий прогресс в неделях, мини-информер

Понадобилось сделать за 5 минут, но может потом пригодиться в других приложениях.

Это примерно то же самое, только без текстового вывода, который, впрочем, показывается в атрибуте title, если подвести курсор мыши к элементу progress (в листинге он не стилизован, а целый учебный "мини-фреймоворк" для замены элемента есть вот тут).

Скрипт умеет отсчитывать номер недели, начиная с недели, указанной при вызове вторым аргументом (нумерация недель по ISO, см. также номера недель в календаре) и включая количество недель, переданное третьим аргументом (нумерация производится с единицы).

Первым же аргументом безымянной самовызываемой функции передаётся идентификатор элемента progress, показывающего, какая часть периода в неделях прошла. Впрочем, элемент не обязан быть именно полоской прогресса, ему достаточно иметь javascript-свойства max и value. Индикатор должен автообновляться раз в час.

Ниже показан скрипт в работе (период работы - 17 недель, начиная с 36-й недели 2023-го года) и код приложения.

<progress id="progressBar" value="0" max="100" title="init"></progress>
<script>
(function (id,startWeek,weeks) {
 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 elem = document.getElementById(id);
 function doTimer() {
  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;
  let dt = '' + wdays[wday] + ', ' + day.toString().padStart(2,'0') + '.' + 
   (mon+1).toString().padStart(2,'0') + '.' + year.toString();
  elem.title = dt;
  if (wnum < 1) {
   elem.value = 0;
   elem.title += ', до начала периода '+(Math.abs(wnum)+1)+' нед.';
  }
  else if (wnum <= weeks) {
   let ps = Math.max(Math.min(Math.round(wnum*100/weeks),100),0);
   elem.value = ps;
   elem.title += ', '+ wnum+' нед. из '+weeks; 
  }
  else {
   elem.value = 100;
   elem.title += ', период закончен '+(wnum-weeks)+' нед. назад';
  } 
 }
 doTimer();
 setInterval(doTimer, 1000*60*60); //раз в час
}('progressBar',36,17));
</script>

Конечно, код уже не будет столь простым, если за отсчитываемый период времени возможен переход через год. Я попытался обойтись без перебора дат в цикле, поэтому для расчётов мне пришлось добавить функций:

  • getWeek (year, month, day) - Номер недели по ISO для указанной даты;
  • getWeeks (startYear, startWeek, endYear, endWeek) - Количество недель между неделями startWeek года startYear и endWeek года endYear;
  • datePlusWeeks (year, month, day, numberOfWeeks) - Дата плюс количество недель;
  • getDateOfWeek (year, week) - Начало и конец недели номер week в году year.

Скрипт ниже считает 100 недель с 8-й недели 2022 года, начавшейся 21 февраля. Можно посчитать вручную, что сегодня, 21.09.2023, идёт 83-я неделя, а 100 недель истекут с окончанием 21-го января 2024 года.

<progress id="progressBar2" value="0" max="100" title="init"></progress>
<script>
(function (id,startYear,startWeek,weeks) {
 let getWeek = function (year, month, day) { 
  //Номер недели по ISO для указанной даты
  let date = new Date(year, month, day);
  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 getWeeks = function (startYear, startWeek, endYear, endWeek) {
  //Количество недель между неделями startWeek года startYear и endWeek года endYear
  let isISOLeapYear = function (year) {
   const startsWithThursday = new Date(year, 0, 1).toString().split(' ')[0] === 'Thu'
   const endsWithThursday = new Date(year, 11, 31).toString().split(' ')[0] === 'Thu'
   return startsWithThursday || endsWithThursday ? true : false;
  }
  let diff = 0;
  for (let i = startYear; i < endYear; i++) diff += isISOLeapYear(i) ? 53 : 52;
  return (diff += endWeek - startWeek);
 }
 let datePlusWeeks = function (year, month, day, numberOfWeeks) { 
  //Дата плюс количество недель
  let dateObj = new Date (year, month, day);
  dateObj.setDate(dateObj.getDate()+ numberOfWeeks * 7);
  return dateObj;
 }
 let getDateOfWeek = function (year, week) { 
  //Начало и конец недели номер week в году year
  let d = new Date("Jan 01, " + year + " 00:00:00");
  let dayMs = (24 * 60 * 60 * 1000);
  let offSetTimeStart = dayMs * (d.getDay() - 1);
  let off = d.getDay() > 4 ? 0 : 1;
  let w = d.getTime() + 604800000 * (week - off) - offSetTimeStart;
  let n1 = new Date(w);
  let n2 = new Date(w + 604800000);
  return { dateFrom: n1, dateTo: n2 };
 }
 let elem = document.getElementById(id);
 function doTimer() {
  let date = new Date(),
   dateObj = getDateOfWeek(startYear,startWeek),
   dateFrom = dateObj.dateFrom,
   dateTo = datePlusWeeks (dateFrom.getFullYear(),dateFrom.getMonth(),dateFrom.getDate(),weeks),
   weekTo = getWeek(dateTo.getFullYear(),dateTo.getMonth(),dateTo.getDate()),
   allWeeks = getWeeks(startYear,startWeek, dateTo.getFullYear(),weekTo),
   year = date.getFullYear(), mon = date.getMonth(), day = date.getDate(), wday = date.getDay(),
   nowWeek = allWeeks - getWeeks(year,getWeek(year, mon, day),dateTo.getFullYear(),weekTo) + 1;
  let wdays = ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'];
  let dt = '' + wdays[wday] + ', ' + day.toString().padStart(2,'0') + '.' + 
   (mon+1).toString().padStart(2,'0') + '.' + year.toString(),
   dt1 = dateFrom.getDate().toString().padStart(2,'0') + '.' + 
    (dateFrom.getMonth()+1).toString().padStart(2,'0') + '.' + dateFrom.getFullYear().toString();
  dateTo.setDate(dateTo.getDate()-1);
  let dt2 = dateTo.getDate().toString().padStart(2,'0') + '.' + 
    (dateTo.getMonth()+1).toString().padStart(2,'0') + '.' + dateTo.getFullYear().toString(),
   period = '('+dt1+'-'+dt2+')';
  elem.title = dt;
  if (nowWeek < 1) {
   elem.value = 0;
   elem.title += ', период начнётся через '+(Math.abs(nowWeek)+1)+' нед. '+period;
  }
  else if (nowWeek <= allWeeks) {
   elem.title += ', '+ nowWeek+' нед. из '+allWeeks+ ' ' + period;
   let ps = Math.max(Math.min(Math.round(nowWeek*100/allWeeks),100),0);
   elem.value = ps;
  }
  else {
   elem.value = 100;
   elem.title += ', период закончен '+(nowWeek-allWeeks)+' нед. назад '+period;
  } 
 }
 doTimer();
 setInterval(doTimer, 1000*60*60); //раз в час
}('progressBar2',2022,8,100));
</script>

20.09.2023, 17:51 [275 просмотров]


теги: javascript время дата информер

К этой статье пока нет комментариев, Ваш будет первым