Как узнать день недели для любой даты н.э.


Подвернулась интересная задача. Написать программу, которая по заданной любой дате выводит день недели. Первый вариант алгоритма:
1.    Посчитать сколько дней прошло до заданной даты с момента начала отсчета.
2.    Взять остаток от деления на 7 (количество дней в неделе).
3.    Полученный результат и соответствует дню недели. 0 – воскресение, 1 – понедельник, 2 – вторник и т.д.


По сути, всего-то 2 шага. Третий уже обработка результатов. Предлагаю реализовать.
Итак, первый шаг – посчитать сколько прошло дней до заданной даты. Что для этого нужно? Что бы было проще возьмем какой-нибудь пример. Пусть это будет 31.12.2011. Сразу можем сказать, что до 2011 года прошло 2010 полных лет. Знаем так же, что в году 365 дней. Значит умножим 2010 на 365. Но сразу же возникает проблема, Каждый високосный год содержит не 365, а 366 дней. Что же делать? Конечно, проще всего 2010 разделить на 4 (мы же знаем, что каждый четвертый год високосный), взять целое от деления, и это число прибавить к результату умножения 2010*365. Получим  2 010 / 4 = 502, 5; 2 010 * 365 = 733 650; 733 650+502 = 734 152;
Значит до начала 2011 года прошло 734 152 дня. Теперь нужно прибавить к этому числу сколько дней пошло в 2011 году до 31 декабря. Мы, конечно же, сразу понимаем, что рас это последний день в году и год не високосный, то он 365й. Но нам же нужен алгоритм в общем виде, поэтому забудем о том что мы это знаем и будем считать.
Количество дней в месяцах не постоянно , значит, просто 30*12 не поучится. Единственный выход – завести массив, где индексу массива соответствует номер месяца, а соответствующий элемент равен количеству дней в этом месяце. Значения массива приведены в таблице. Нас интересует из нее только второй и третий столбец. Нумерация начинается с 0, что соответствует языкам программирования С и  др.
Месяц
дней
Январь
0
31
Февраль
1
28
Март
2
31
Апрель
3
30
Май
4
31
Июнь
5
30
Июль
6
31
Август
7
31
Сентябрь
8
30
Октябрь
9
31
Ноябрь
10
30
Декабрь
11
31








Тогда алгоритм выглядит следующим образом. Складываем количество дней в месяце до заданного месяца, не включая его. Значит в нашем случае: 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 =  334.
Осталось только прибавить к этой сумме номер введенного дня  и это все прибавить к количеству дней до нового года. В нашем случае:  734 152 + 334 + 31 = 734 517. Это число должно соответствовать количеству дней с начал отсчета до 31 декабря 2011 года. Не думаю, что мы сможем это как то проверить. Если только найти все календари за 2011 лет и посчитать. Думаю, у нас не получится.
Возьмемся за следующий шаг. Разделим это число на 7 и возьмем остаток.  В нашем случае остаток 0. Значит, новый год в 2011 году выпадает на воскресение. Вот и всё! Задача решена. Но давайте проверим. Не составит труда открыть календарь  2011 и посмотреть.  

Выходит, мы ошиблись.  Просмотрев еще раз ход решения, понимаем, что ошибиться мы могли только в вычислении количества дней прошедших до этой даты. Более того, Скорее всего ошибка в подсчете дней до 2011 года, потому что в 2011 мы очевидно посчитали все верно.  И это тоже можно проверить. Тогда возьмемся за пересчет дней до 2011 года. Обратимся в Сети. Оказывается, что все не так просто. Современный григорианский календарь накладывает больше требований к високосным годам, чем мы думали. А именно:  високосным считаются годы, номера которых кратны 400 и номера которых кратны 4 и не кратны 100. А так же в момент принятия этого календаря текущая дата сдвигалась на 10 дней вперед. В России этот сдвиг произошел в 1918 году. После 31 января следовало 14 февраля. Другие страны переходили на Григорианский календарь в другое время. Например Испания Италия  Португалия и Речь Посполитая в 1582году, а Египет в 1928 году. Это значит, что в ряде стран, в том числе, например, в России 1900 году был день 29 февраля, а в ряди других стран не было. В Интернете полно информации по этой теме. Кому интересно – читайте. Я не специалист по календарям, поэтому будем полагаться на википедию и другие первые результаты запросов в гугле. 
Итак, у нас есть новое правило определения високосного года.  Високосный год если его номер кратен 400 или если номер кратен 4 и не кратен 100. И это действует только… будем считать, с 1918 года.
Приведу сразу код. Эта проверка не самая быстрая, но точно работает.




 Здесь реализован совсем другой подход. Теперь мы не прибавляем количество високосных лет до заданной даты, а считаем сумму в дней в цикле. Если год високосный, то  прибавляем 366, иначе – 365. Теперь sum равно количество дней до года year.
 Прибавляем количество дней прошедших с начала года и номер введенного дня. Для этого сначала проверим является ли введенный год високосным, а так  введенный месяц больше 2. Ведь даже если год високосный, но месяц январь, то прибавлять 1 не надо, а если месяц больше января, то надо. В приведенном листинге i означает введенный месяц.


Ну а теперь вспомним про сдвиги.





Теперь мы посчитали количество дней с учетом всех правил, которые я нашли при беглом просмотре Интернета.
Вот и всё! Делим sum на 7 и проверяем остаток. Вот листинг:

















Осталось проверить. Введем наш пример 31 декабря 2011 года.  


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

Комментарии

  1. int Date( int D, int M, int Y )
    {
    int a, y, m, R;
    a = ( 14 - M ) / 12;
    y = Y - a;
    m = M + 12 * a - 2;
    R = 7000 + ( D + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12 );
    return R % 7;
    }

    ОтветитьУдалить

Отправить комментарий

Популярные сообщения из этого блога

Начало

Complete move of remote Git repository