Задача
Вам нужно выполнить вычисление даты, которое принимало бы во внимание наличие високосных годов. Например, длина месяца или года зависит от того, относится ли дата к високосному году.

Решение
Научитесь определять, является ли год високосным, и учтите результат этой проверки при выполнении вычислений.

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

Как определить, относится ли дата к високосному году
Чтобы узнать, относится ли дата d к високосному году, выделите составляющую года с помощью функции YEAR() и проверьте результат. Часто для проверки на високосный год применяют правило делимости на четыре, которое можно реализовать при помощи оператора деления по модулю:

Однако такая проверка технически некорректна (например, 1900 год делится на четыре, но не является високосным).

Для того чтобы год был високосным, он должен удовлетворять сразу двум условиям:

• Год должен делиться на четыре.
• Год не должен делиться на 100, если только он не делится и на 400.

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

(YEAR(d) % 4 = 0) AND ((YEAR(d) % 100 != 0) OR (YEAR(d) % 400 = 0))

Проверим правила високосности для данных таблицы date_val и получим такие результаты:

mysql> SELECT
-> d,
-> YEAR(d) % 4 = 0
-> AS "rule-of-thumb test",
-> (YEAR(d) % 4 = 0) AND ((YEAR(d) % 100 != 0) OR (YEAR(d) % 400 = 0))
-> AS "complete test"
-> FROM date_val;

Как видите, полная проверка выводит иные результаты, нежели предыдущая.

Первый тест некорректно обрабатывает 1900 год, второй же учитываетЕсли вы работаете со значениями дат в программе, то можете выполнять проверки на високосный год не на уровне SQL, а в своем языке API. Выделите первые четыре разряда строки даты для получения года и проверьте его.

Если язык осуществляет автоматическое преобразование значения года из строки в число, все просто. В противном случае придется дополнительно преобразовать значение года в число, прежде чем проверять его.

В Perl и PHP проверка на високосный год имеет такой формат:

Синтаксис Python аналогичен, но еще требуется операция преобразования типа:

year = int (date[0:4])
is_leap = (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)

Преобразование типа необходимо и в Java:

int year = Integer.valueOf (date.substring (0, 4)).intValue ();
boolean is_leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);

Использование проверки на високосный год для вычисления длины года
Обычно в году 365 дней, но високосный год имеет один дополнительный день.

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

$year = substr ($date, 0, 4);
$is_leap = ($year % 4 == 0) && ($year % 100 != 0 || $year % 400 == 0);
$days_in_year = ($is_leap ? 366 : 365);

Есть и другой способ вычисления длины года: можно вычислить дату последнего дня года и передать ее в функцию DAYOFYEAR():

mysql> SET @d = ‘2003-04-13’;
mysql> SELECT DAYOFYEAR(DATE_FORMAT(@d,’%Y-12-31′));

mysql> SET @d = ‘2004-04-13’;
mysql> SELECT DAYOFYEAR(DATE_FORMAT(@d,’%Y-12-31′));

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

mysql> SELECT d,
-> ELT(MONTH(d),
-> 31,
-> IF((YEAR(d)%4 = 0) AND ((YEAR(d)%100 != 0) OR (YEAR(d)%400 = 0)),29,28),
-> 31,30,31,30,31,31,30,31,30,31)
-> AS ‘days in month’
-> FROM date_val;

Функция ELT() оценивает свой первый аргумент для определения значения n, затем возвращает n-е значение следующих аргументов. Все просто для всех месяцев, кроме февраля, для которого ELT() должна вернуть 29 или 28 в зависимости от того, является ли год високосным.

В языке API вы можете написать функцию, которая, получив в качестве аргумента дату в формате ISO, возвращала бы количество дней месяца, к которому относится дата. Приведем версию на Perl:

sub days_in_month
<
my $date = shift;
my $year = substr ($date, 0, 4);
my $month = substr ($date, 5, 2); # month, 1-based
my @days_in_month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
my $days = $days_in_month[$month-1];
my $is_leap = ($year % 4 == 0) && ($year % 100 != 0 || $year % 400 == 0);
$days++ if $month == 2 && $is_leap; # add a day for Feb of leap years
return ($days);
>

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

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

Многие современные программы так или иначе связаны с текущим временем, причем пользователь может об этом и не знать: текущая дата часто используется программами в служебных целях. Поэтому существует понятие "времени жизни" программы. Например, сегодняшний вариант наиболее популярных операционных систем семейства Microsoft Windows рассчитан на работу до 2099 года.

Совсем другой вопрос — с каким диапазоном дат (в качестве обрабатываемых ею данных) может работать прикладная программа. Допустим, сегодня вы захотели забронировать билет на самолет в 2010 году. Наверняка программа, которая используется для решения этой задачи, к тому времени уже "умрет" (будет заменена на новую версию), но сделку она должна обработать уже сегодня, то есть уметь работать с датами как минимум до 2010 года включительно. Другой пример — программа, определяющая день недели по заданной дате. Например, для историка может быть важным ответ: в какой день недели родился Александр Пушкин?

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

Дата хранится в виде числа. (Чтобы не усложнять пример, будем считать, что в формате целого. На самом деле там используется вещественное число, что позволяет учитывать и время с точностью до секунд.) Нулевая дата — 30 декабря 1899 года, суббота. Задав число суток от начала отсчета, вы получаете дату и день недели. И наоборот. Например, 31 декабря 2099 года будет четвергом, 73500-ым днем от 30.12.1899. А минус тысячный день (-1000) от той же точки отсчета — 4 апреля 1897 года, воскресенье. Этот алгоритм рассчитан на ведение календаря в диапазоне от 01.01.100 до 31.12.9999.

Казалось бы, все очень просто, и если вы имеете дело с бизнес-задачами — можете не волноваться: там, вроде бы, все в порядке. Но если вы историк и работаете с историческими датами — вам следует дочитать эту статью до конца.

Зададим себе вопрос: какой был день накануне пятницы 15 октября 1582 года? Скорее всего, каждый даст очевидный ответ (а программы Microsoft его дают однозначно): четверг 14 октября 1582 года. И будут неправы. Если считать по григорианскому календарю (новый стиль), то это действительно четверг, но 4 октября. Если же имеется в виду юлианский календарь (старый стиль), то 15 октября был понедельник, а накануне — 14 октября, воскресенье.

Напомним кратко историю календарей (подробнее см. "Наука и жизнь" № 1, 1999 г.). Основа современного календаря была заложена реформой Юлия Цезаря в 46 г. до н. э. После ряда уточнений спустя 50 лет календарь принял современный вид. За исключением порядка определения високосного года. В юлианском календаре средняя длина года была принята 365,25 суток, поэтому високосным считался каждый четвертый год. Исследования астрономов еще в средние века позволили определить, что в этой величине имеется погрешность (солнечный год меньше на 11 мин 14 сек), из-за чего за каждые 128 лет накапливалась ошибка в 1 сутки.

В результате получилось, что к концу 16 века дата весеннего равноденствия приходилась не на 21 марта (как это было в 325 году, когда христианская церковь приняла юлианский календарь в качестве официального), а на 11 марта. А день весеннего равноденствия издавна является ключевой точкой для счета времени в истории человечества.

Ошибка в календаре была исправлена папой римским Григорием XIII, который в 1582 году своей буллой передвинул счет дней на 10 дней вперед: день после четверга 4 октября предписывалось считать пятницей, но не 5, а 15 октября. Чтобы избежать накопления новой ошибки в будущем, был также изменен порядок вычисления високосных годов: из их числа исключаются вековые годы (с двумя нулями в конце), кроме тех, число сотен которых делится без остатка на четыре. Поэтому 2000 год — високосный, причем во всех календарях: и юлианском, и григорианском!

Вывод из этого можно сделать следующий: нельзя использовать алгоритм вычисления даты по григорианскому календарю по временной шкале вниз от 15 октября 1582 года. Но именно так делается в алгоритмах Microsoft.

Простой пример: в григорианском календаре просто нет дат с 6 по 14 октября 1582 года, и вся информация об исторических событиях в предыдущий период приводится по старому стилю. С сегодняшней точки зрения разница в несколько дней, казалось бы, является пустяком, но для историка вопрос, допустим, привязки даты к дню недели может быть принципиально важным (когда произошла Куликовская битва 08.09.1380 — до выходного дня или после?). Не говоря уже о том, что в истории человечества был, например, день 29 февраля 1500 года — недопустимая дата с точки зрения Visual Basic.

Еще внимательнее нужно относиться к датам с конца 16 до начала 20 века. Ведь переход разных стран со старого на новый стиль был не одновременным. Например, Франция, Италия и Испания перешли на григорианский календарь в 16 веке, Германия — в 17-м, Англия — в 18-м, Япония — в 19-м, Россия и Китай — в начале 20-го. Это привело к появлению огромного числа исторических мифов: о том, что граф Калиостро в один день мог побывать и в Петербурге, и в Берлине; что парусники с фантастической скоростью переплывали Атлантику, и прочих.

Вывод из сказанного таков. При работе с историческими датами после 4 октября 1582 года просто необходима возможность работы как в новом, так и в старом стиле. А использование алгоритма григорианского календаря в обратную сторону от даты его введения — скорее всего, ошибка. Например, пользуясь алгоритмом Microsoft, мы получим, что даты Рождества Христова (25 декабря), то есть собственно момент рождения, совпадают в юлианском и григорианском календарях только в третьем веке нашей эры, а не в первом, как это должно быть.

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

Каждые 4 года мы сталкиваемся с тем, что в феврале вместо стандартных 28 дней бывает 29. Наступает високосный год. Это связано с тем, что Земля вращается вокруг солнца немного быстрее, чем за 365 дней. Для компенсации этого явления придуман и введен в практику високосный год, который продолжается 366 суток за счет добавления лишнего дня в феврале.

Когда високосный год, как определить

Способы определения достаточно просты и доступны любому человеку. Они не требуют какой-то серьезной математической подготовки:

1. Посмотреть в любом календаре, есть ли 29 февраля в исследуемом году.
2. Високосный год всегда делится на 4 без остатка. Предположим, 2012 год – високосный, поскольку 2012 : 4 = 503.00, то есть остаток от деления равен 0.
3. Можно посмотреть календарь на исследуемый год и сравнить его со следующим по дням недели. Если разбежка составит 1 день, то анализируемый временной интервал не является високосным – его длина 52 недели и 1 день, если разбежка 2 дня, то, соответственно, год високосный.

Существуют ли исключения ?

Да, существуют. Для более полного согласования астрономического и календарного года было принято решение не делать високосными те года, которые заканчиваются на 00, то есть начало каждого века. Но и здесь есть свое исключение: каждый 4-й из этих годов (заканчивающихся на 00 – 400, 800, 1200, 1600, 2000, 2400, …) также являются високосными.

Список високосных годов

1764, 1768, 1772, 1776, 1780, 1784, 1788, 1792, 1796, 1804, 1808, 1812, 1816, 1820, 1824, 1828, 1832, 1836, 1840, 1844, 1848, 1852, 1856, 1860, 1864, 1868, 1872, 1876, 1880, 1884, 1888, 1892, 1896, 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032,2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2080, 2084, 2088, 2092, 2096, 2104, 2108, 2112, 2116, 2120, 2124, 2128, 2132

История появления

Само понятие високосного года появилось в те времена, когда ученые доказали, что в астрономическом году 365 дней и еще приблизительно 5 часов и 49 минут. Случилось это при Юлии Цезаре, который слыл достаточно прогрессивным правителем. С тех пор к календарю стали добавлять 1 лишний день.

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

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

Суеверия и страхи

В народе принято считать, что високосные года весьма неудачны, а порой, трагичны. Плохим этот год стали считать еще римляне, у которых из-за "шестого дня" на один день увеличивался пост (длился до начала марта). Добавил мистики одиозный праздник "Касьянов день", который отмечали уже наши предки. Он был определен на 29 февраля.

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

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