Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Приемы и технологии программирования Календарные алгоритмы


Информационный Канал Subscribe.Ru

http://www.mrblack.pp.ru
Приемы и технологии программирования #8

Календарные алгоритмы

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

Хранение дат

Дата может быть представлена в конпонентном формате, т. е. в понятном человеку виде: год, месяц и число, a может быть представлена количеством дней между некоторой точкой отсчета и данной датой. Это представление называют абсолютным временем. Каждый из этих форматов имеет свои преимущества и недостатки. С абсолютными временами можно довольно просто выполнять вычитание, получая величину промежутка времени. Они занимают мало места в памяти и на диске. Однако для вывода даты на экран нужно привести ее к компонентному виду и определенному формату. Если же дата записана в виде строки, например, "2005-09-03", то выводить их можно как есть, хотя для лучше привести к общепринятому формату, и такие даты можно сравнивать и сортировать как строки.

Обработка абсолютного времени

Дата в базе данных может быть представлена строкой, уже приведенной к какому-то формату (именно так это сделано в MySQL), а может быть закодирована в виде числа, выражающего количество дней с какого-то определенного момента в прошлом. В последнем случае для плучения компонент даты необходимо произвести некоторые вычисления, а именно, определить, сначала, в каком году данная дата находится, затем, в каком месяце, и, наконец, найти число месяца. Сложность в том, что в месяцах разное количество дней, и годы бывают високосные, а бывают невисокосные. Определим год. Пусть отсчет начинается с первого января какого- нибудь високосного года. Каждые четыре года - это 4*365+1=1461 день, и теперь мы найдем год так:

 int days; // абсолютная дата
 int year = (days / 1461) * 4;
 int b = days % 1461;
 if (b > 366) {
  year++; b -= 366;
  year += b % 365;
  b = b / 365;
 }
 year += 1970; // Точка отсчета

Дальше можно найти месяц и число:

 int ml[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
 int i = 0;
 while (b >= ml[i]) b -= ml[i++];
 int dayofmonth = b+1; // Число
 int month = i+1; // Месяц

Обратный перевод:

 year -= 1970; // Точка отсчета
 int abs = year * 365 + (year+3)/4 + dayofmonth;
 for (i = 0; i < month-1; i++) abs += ml[i];

Разности дат

Промежутки времени могут быть выражены в неделях, днях, часах, минутах и секундах, и между любыми двумя из этих единиц можно выполнить перевод вне зависимости от начала промежутка времени: неделя - это всегда семь дней,в сутках всегда 24 часа и дт. Календарный промежуток времени может быть выражен в месяцах или годах, и между этими единицами тоже перевод возможен без знания конкретных дат. А вот чтобы перевести число месяцев в число дней в общем случае надо знать, какие конкретно это месяцы. Поэтому для хранения промежутков времени в базе данных нельзя приводить их к какой-либо одной единице, а необходимо хотя бы два формата, например, количество месяцев и количество дней.

Вычислить промежуток между датами в днях тривиально, если даты в базе данных хранятся в виде количества дней с какого-то момента: надо просто найти разность этих чисел. Чтобы найти этот же промежуток в месяцах, надо перевести даты в компонентный формат (год, месац и число) и применить формулу:

количество_месяцев = (год2 - год2) * 12 + месяц2 - месяц1

Количество рабочих дней

Важную проблему представляет нахождение количества рабочих дней в указанном промежутке времени. Пусть есть две даты в виде абсолютного времени, и надо узнать, сколько между ними рабочих дней. Проще всего это сделать путем вычитания их общего количества дней количества суббот и воскресений. Количество определенных дней недели в заданном промежутке находится очень просто:

количество_дней = (конец - база)/7 - (начало - база)/7

База - это какой-нибудь день, являющийся интересующим нас днем недели.

Конец - день, следующий за последним днем рпомежутка.

Начало - первый день.

Процедура нахождения этого количества может выглядеть так:

 int DayCount(int Start, int DayAfterEnd, int Day) {
  // Константа BASE зависит от базы абсолютного времени
  int base = BASE + Day;
  return (DayAfterEnd - base) / 7 - (Start - base) / 7;
 }

Процедура вычисления количества рабочих дней будет такая:

 int WorkDayCount(int Start, int DayAfterEnd) {
  return DayAfterEnd - Start -
   DayCount(Start, DayAfterEnd,
     5 /* дни нумеруются с нуля */) -
   DayCount(Start, DayAfterEnd, 6);
 } 

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


Объявление

Лето кончилось, началась учеба, и у меня теперь нет времени выпускать рассылку каждую неделю. Рассылка будет теперь выходить, предположительно, раз в месяц.

_

2005-09-03

mr. Black <mrblack@pochta.ws>
Аська: 179497623
Сайт: http://www.mrblack.pp.ru/
Программа "Голосовая Почта"
Программа "Simple RAS Dialer"
Статьи о технологиях программирования

Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.techn
Отписаться
Вспомнить пароль

В избранное