<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Закладки разработчика | IT Notes</title><generator>teletype.in</generator><description><![CDATA[Закладки разработчика | IT Notes]]></description><link>https://teletype.in/@itnotes?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=itnotes</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/itnotes?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/itnotes?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Tue, 22 Sep 2026 16:23:20 GMT</pubDate><lastBuildDate>Tue, 22 Sep 2026 16:23:20 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@itnotes/python-in-time</guid><link>https://teletype.in/@itnotes/python-in-time?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=itnotes</link><comments>https://teletype.in/@itnotes/python-in-time?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=itnotes#comments</comments><dc:creator>itnotes</dc:creator><title>Как не разбудить пользователя посреди ночи? Самая полная статья о работе со временем в Python.</title><pubDate>Fri, 13 May 2022 16:47:36 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/af/2e/af2ea2ae-1a28-46de-8b20-d27c56f125cf.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/40/1a/401ab4e0-fff2-4437-97e9-e2981da1561c.png"></img>Мне даже сложно описать как часто бизнес ставит программам условные ограничения связанные со временем и временными зонами наших пользователей, а ещё сложнее описать как часто программисты вообще забывают о том, что такие ограничения действительно должны существовать, дабы улучшить пользовательский опыт или, вовсе не навредить пользователю, например, отправив ему уведомление в 4 часа ночи!]]></description><content:encoded><![CDATA[
  <h2 id="3CTF">Проблематика и мотивации</h2>
  <p id="Z4Gf">Мне даже сложно описать как часто бизнес ставит программам условные ограничения связанные со временем и временными зонами наших пользователей, а ещё сложнее описать как часто программисты вообще забывают о том, что такие ограничения действительно должны существовать, дабы улучшить пользовательский опыт или, вовсе не навредить пользователю, например, отправив ему уведомление в 4 часа ночи!</p>
  <p id="4Lqs">Изучение времени и того, как оно работает, волновало людей уже сотни (если не тысячи) лет, но со “временем” человечество придумало <em>пружинные двигатели</em> (основу всех механических часов), а позже и <em>кварцевые резонаторы</em> (используются в современных электронных и кварцевых часах).</p>
  <h2 id="zBhY">Как компьютер узнаёт время?</h2>
  <p id="qptu">Этот раздел вы можете смело пропустить, ведь ответ до ужаса банален - из интернета, почти всегда. Но что компьютер делает, когда интернета нет и как понимает сколько времени прошло с того момента, как его выключили? Ведь современные компьютеры не просят вас настраивать время при каждом запуске, если подключения к интернету у них нету.</p>
  <p id="yqMA">Большинство современных девайсов использует всё те же кварцевые резонаторы (как внешний генератор тактовых сигналов) или получает сигнал из питающей электросети, но одним только резонатором дело не обходится - тактовая частота требует преобразования, чем занимается так называемая RTC (Real Time Clock) микросхема. Она преобразует тактовую частоту кварцевого резонатора в разные типы времени - календарный, по типу YY-MM-DD и HH-MM-SS, а также в бинарный, в виде счётчика единиц времени - секунд или их долей.</p>
  <p id="YSKu">Питает всё это чудо маленькая плоская батарейка, которая устанавливается в материнскую плату девайса. Даже в электронных часах есть такой “запасной” питающий элемент, который может служить определённое количество времени. Эта же батарейка питает систему хранения настроек материнской платы, в том числе BIOS’a, поэтому многим может быть известна схема сброса этих настроек - вынуть батарейку и вставить назад. Тогда и время слетит.</p>
  <figure id="obt6" class="m_original">
    <img src="https://img1.teletype.in/files/40/1a/401ab4e0-fff2-4437-97e9-e2981da1561c.png" width="640" />
    <figcaption>А вот пример этой самой RTC, которую питает плоская батарейка - CR2032.</figcaption>
  </figure>
  <h2 id="BUon">Python: всё о работе со временем!</h2>
  <p id="Wgt5">В Python существует несколько связанных со временем модулей - <strong><em>time</em></strong>, <strong><em>datetime</em></strong>, <strong><em>dateutil</em></strong>. Стоит по отдельности разобрать каждый из них, так как все они решают свои особенные проблемы, хотя и понимают друг друга на уровне абстракций.</p>
  <h3 id="Zku4">Модуль time</h3>
  <p id="vCWC">Несмотря на то, что именно модуль <strong><em>time</em></strong> является базовым средством работы со временем в Python, он часто уступает в удобстве своему младшему брату - модулю <strong><em>datetime</em></strong>, и большинству разработчиков известен скорее из за своей блокирующей функции <code>time.sleep(seconds: int)</code>, которая заставляет программу “застыть” в ожидании окончания секундного таймера.</p>
  <p id="K6wQ">Первое, что нам необходимо - узнать сколько сейчас времени. Это можно сделать при помощи функции <code>time.localtime()</code>, что мы и сделаем:</p>
  <pre id="ey5q" data-lang="python">import time

time.localtime()
# time.struct_time(tm_year=2022, tm_mon=5, tm_mday=10, tm_hour=23, tm_min=0, tm_sec=18, tm_wday=2, tm_yday=131, tm_isdst=0)
# В ответе мы получили обьект типа time.struct_time, это точное время, когда я пишу эту статью!

time.localtime(0)
# time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
# Мы получаем обьект с установленным временем в 00:00:00 1 Января 1970-го
# Эта дата - начало отсчёта UNIX-времени!

time.ctime()  # Wed May 11 23:00:00 2022
# Удобочитаемый формат времени

time.ctime(0)  # Thu Jan  1 00:00:00 1970
# Если в вашей системе время настроено на Московский или любой другой отличный от UTC часовой пояс
# то вы получите немного отличный результат, обратите на это внимание!</pre>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="sSUF">В примерах может присутствовать несущественное расхождение в результатах выполнения функций! Для удобочитаемости я округлял результаты работы функций, а также приводил все даты в примерах к одной.</p>
  </section>
  <p id="092m">Иногда вместо объекта времени нам необходимо получить так называемый <em>timestamp</em> (тайм-cтемп) - количество секунд прошедших с 00:00:00 (для UTC) 1 Января 1970-го года:</p>
  <pre id="9fq6" data-lang="python">time.time()  # 1652300066.808788
# Это количество секунд прошедших с полуночи 1 Января 1970-го!</pre>
  <p id="uf2w">А теперь немного поиграемся с часовыми поясами: попробуем вывести текущее время в виде строки и в виде тайм-cтемпа, а потом сменим часовой пояс и повторим то же самое:</p>
  <pre id="cnzp" data-lang="python"># Узнаем текущий часовой пояс
time.tzname    # (&#x27;MSK&#x27;, &#x27;MSK&#x27;)
time.timezone  # -10800 | Смещение часового пояса в секундах (10800 секунд = 3 часа)

# Выводим текущее время в системе
time.time()   # 1652300626.184014
time.ctime()  # Wed May 10 23:30:00 2022

# Для установки часового пояса понадобится модуль os, импортируйте его к себе
os.environ[&#x27;TZ&#x27;] = &#x27;UTC&#x27;
time.tzset()

# Снова выведем время
time.time()   # 1652300626.184296
time.ctime()  # Wed May 10 20:30:00 2022</pre>
  <p id="lYaH">У неопытного разработчика это может вызвать диссонанс - время разное, но тайм-стемп одинаковый. Это потому, что тайм-стемп всегда считается с учётом часового пояса, поэтому для МСК отсчёт секунд будет начинаться не в полночь (00:00:00 1 Января 1970-го), а в 3 часа ночи!</p>
  <p id="zXbf">Также в модуле <em><strong>time</strong></em> есть функция позволяющая замерить количество процессорного времени, затраченного процессом до времени замера - <code>time.process_time()</code>, но я вернусь к этой функции в своей следующей статье о тестировании Python приложений на производительность!</p>
  <h3 id="b4xV">Модуль datetime</h3>
  <p id="Ejsq">Несмотря на всю практичную простоту модуля <strong><em>time</em></strong>, иногда нам нужно что-то более <s>комплексное</s> умное. Об этом также подумали разработчики модуля <strong><em>datetime</em></strong>, создав удобную обёртку вокруг модуля <em><strong>time</strong></em>. Узнать об этом можно открыв сам файлик <em>datetime.py </em>и просмотрев какие модули он импортирует:</p>
  <pre id="2tPM" data-lang="python">import time as _time  # &lt;- Вот он заветный!
import math as _math
import sys
from operator import index as _index</pre>
  <p id="k5px">Остальные модули, которые импортирует <em><strong>datetime</strong></em> нам не интересны.</p>
  <p id="7AAg">Основной функционал данного модуля предоставляется через четыре класса - <code>date</code>, <code>datetime</code> и <code>timedelta</code>. Названия первых двух классов говорят сами за себя, но третий часто вызывает вопросы у тех, кто впервые с ним сталкивается. Давайте разберём каждый из классов по отдельности и сравним их работу с модулем <em><strong>time</strong></em>!</p>
  <blockquote id="o36n">Если изучить исходники модулей <strong><em>time</em></strong> и <strong><em>datetime</em></strong> и сравнить их, то можно увидеть, что модуль <strong><em>time</em></strong> вообще не предоставляет никакого функционала на Python, он использует написанные на C++ модули для возвращения результата, тогда как модуль <strong><em>datetime</em></strong> написан именно на Python.</blockquote>
  <p id="dyj0"><strong>Работа с датами</strong></p>
  <pre id="Gwwd" data-lang="python">import time as _time
from datetime import date

# Попробуем вывести в читаемом виде сегодняшнюю дату
_time.ctime(_time.time())                      # Thu May 10 19:00:30 2022
date.today().ctime()                           # Thu May 12 00:00:00 2022

# А теперь решим классическую задачу: узнаем является ли сегодняшний день 9-м мая
_time.localtime().tm_mday == 9 and _time.localtime().tm_mon == 5  # False
date.today().day == 9 and date.today().month == 5                 # False
# Увы, но сегодня не 9-е мая...

# Попробуем получить дату из строки
_time.strptime(&#x27;2022/05/10&#x27;, &#x27;%Y/%m/%d&#x27;) # time.struct_time(tm_year=2022, tm_mon=5, tm_mday=10)
date.strptime(&#x27;2022/05/10&#x27;, &#x27;%Y/%m/%d&#x27;)  # AttributeError...
# Да, через класс date мы не сможем получить дату из строки
# Но мы сможем сделать это используя класс datetime, об этом ниже!

# Зато получить дату через таймстемп мы можем
ts = _time.time()  # Получаем количество секунд (мы могли бы передать их числом)
_time.localtime(ts)         # time.struct_time(tm_year=2022, tm_mon=5...)
date.fromtimestamp(ts)      # 2022-05-10</pre>
  <p id="lTWa">Несмотря на то, что модуль time в некоторых местах показывает себя лучше, чем функционал класса <code>datetime.date</code>, сам класс всё же бывает полезным в случаях, когда нам приходится работать с датой отдельно от времени.</p>
  <p id="KFdl"><strong>Лучшее, что есть &quot;из коробки&quot;</strong></p>
  <p id="o4TS">Заранее извиняясь за тавтологию, перейду к рассмотрению жемчужины модуля <strong><em>datetime</em></strong> - классу <code>datetime</code>! Он предоставляет куда более гибкий интерфейс для работы с датой и временем, чем модуль <em><strong>time</strong></em>:</p>
  <pre id="EtYX" data-lang="python">import time as _time
from datetime import datetime

# Начнём с того, что снова узнаем сколько сейчас времени
_time.localtime()  # time.struct_time(tm_year=2022, tm_mon=5...)
datetime.now()     # 2022-05-10 19:00:00.746417

# Снова попробуем получить дату и время из строки
_time.strptime(&#x27;2022/05/10&#x27;, &#x27;%Y/%m/%d&#x27;)     # time.struct_time(tm_year=2022, tm_mon=5...)
datetime.strptime(&#x27;2022/05/10&#x27;, &#x27;%Y/%m/%d&#x27;)  # 2022-05-10 00:00:00
# Теперь мы получаем не ошибку, а обьект класса datetime

# А можно и наоборот, преобразовать обьекты в строку нужного нам вида
_time.strftime(&#x27;Year: %Y, Month: %m, Day: %d&#x27;, _time.localtime())  
# Year: 2022, Month: 05, Day: 10
datetime.now().strftime(&#x27;Year: %Y, Month: %m, Day: %d&#x27;)  
# Year: 2022, Month: 05, Day: 10

# Сравним как получить таймстемп через datetime
_time.time()                # 1652372410.6999412
datetime.now().timestamp()  # 1652372410.700101

# Попробуем изменить день в обьекте datetime и снова получить таймстемп
datetime.now().timestamp()                  # 1652372637.027454
datetime.now().replace(day=11).timestamp()  # 1652286237.027475
# Мы потеряли приблезительно сутки - 86399 секунд = 23.9 часа</pre>
  <p id="Z5l5"><strong>Арифметика дат и времени</strong></p>
  <p id="Y2RE">Нередко бывает так, что для реализации бизнес-логики нам необходимо не только знать конкретные даты и время, но и работать с их арифметикой - вычитать из даты часы, узнавать разницу между двумя датами, хранить временные отрезки и так далее. Для решения всех этих задач был разработан класс <code>timedelta</code>, представляющий собой некий временной промежуток, который можно интерпретировать как дни, секунды и микросекунды (одна миллионная секунды).</p>
  <pre id="SDvu" data-lang="python">from datetime import datetime, timedelta

# Простой пример того, как timedelta упрощает вычитание дня из определённой даты
datetime.now().replace(day=datetime.today().day - 1)  # 2022-05-9 20:15:00
datetime.now() - timedelta(days=1)                    # 2022-05-9 20:15:00

# Но нагляднее всего timedelta проявляет себя, когда надо вычесть первый день месяца
# или наоборот прибавить одинь день к 31-му декабря
datetime.today().replace(day=1) - timedelta(days=1)        # 2022-04-30 20:30:00
datetime(year=2021, month=12, day=31) + timedelta(days=1)  # 2022-01-01 00:00:00

# В случае вычитания дат друг из друга мы тоже получим обьект timedelta
time = (datetime.today() + timedelta(days=1)) - datetime.today()
type(time)  # &lt;class &#x27;datetime.timedelta&#x27;&gt;

# Сравнивать даты можно также как и любые другие обьекты в Python
datetime.now() == datetime.now()  # False
datetime.now() &lt; datetime.now()   # True
# Это может сбивать с толку, т.к. кажется, что функции выполняются одновременно,
# но по правилам алгебры логики выражения выполняются слева-направо

# Сами обьекты timedelta тоже можно сравнивать
timedelta(days=1) == timedelta(seconds=86400)  # True
timedelta(seconds=1) &lt; timedelta(seconds=1)    # False</pre>
  <h2 id="UPSc">Проблема часовых поясов</h2>
  <p id="42AL">Встроенные модули, о которых мы сейчас говорили, умеют работать с разными часовыми поясами, но для этого нужно глобально выставлять часовой пояс системы или процесса через переменные окружения, или вовсе передавать часовой пояс при создании обьекта времени.</p>
  <p id="Tg1c">Вместо того, чтобы шутить тут про Мишу из Москвы и Юру из Уфы, я лучше попытаюсь вам обьяснить, насколько всё в действительности сложно. Для начала, нам нужно определиться с понятиями, с тем, что такое часовой пояс (timezone) и с тем, что такое отступ (offset) в контексте часовых поясов.</p>
  <p id="bCKB">Отступом называют разницу между часовыми поясами и обозначают как “+N” или “-N” относительно Всемирного координированного времени (UTC). Таким образом Москва живёт с отступом UTC+3, а Уфа с отступом UTC+5. Но существуют страны, которые не придерживаются стандартного 24-х часового круга часовых поясов и используют свои уникальные отступы. Так в одной только Австралии встречаются часовые пояса с отступами: UTC+8:45, UTC+9:30 и UTC+10:30. В столице Либерии (Африка) до 1979-го года часовой пояс шел с отступом в 44 минуты и 30 секунд, а записывался как UTC+00:44:30!</p>
  <figure id="KwtG" class="m_original">
    <img src="https://img2.teletype.in/files/18/38/18384fd4-6bc6-410b-8536-d9829b59d7df.png" width="4000" />
  </figure>
  <p id="4uJ4">Но отступ и часовой пояс - не одно и то же. Часовой пояс - территория, именуемая в зависимости от своего географического местонахождения. Москва живёт по часовому поясу “Europe/Moscow”, а Нью-Йорк по поясу “America/New_York”. Часовых поясов существует неимоверное множество, они ежегодно обновляются и меняются с учётом различных обстоятельств: скорости вращения Земли, смены правительства, разделения территорий, религиозных взглядов и т.д. Найти полный список можно <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" target="_blank">тут</a>.</p>
  <p id="Jclo">Думаю к этому моменту вы уже осознали всю боль, с которой сталкивались разработчики первых международных программ, но именно благодаря им нам не приходится ежедневно разбирать гневные письма со всех уголков земли о том, что наша программа работает неправильно, ведь у нас есть множество готовых решений по работе с часовыми поясами, одним из которых является пакет <strong><em>dateutil</em></strong>.</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="z32X">Сам по себе пакет dateutil представляет очень мощное расширение стандартного модуля datetime, предоставляя набор инструментов для работы с часовыми поясами, парсингом времени, генераторами дат. Очень подробно об этом написано в документации модуля, правда она на английском - <a href="https://dateutil.readthedocs.io" target="_blank">https://dateutil.readthedocs.io</a></p>
  </section>
  <h2 id="oxuQ">Пакет dateutil</h2>
  <p id="Kfbo">Думаю мне не стоит растягивать материал обьяснением виртуальных окружений и установкой сторонних пакетов, поэтому я просто попрошу вас установить нужный нам пакет командой <code>pip install python-dateutil</code> и просто приступить у его рассмотрению:</p>
  <pre id="cvAx" data-lang="python">from datetime import datetime
from dateutil import tz  # &lt;- Этот модуль отвечает за работу с таймзонами

# Начнём с того, что узнаем в каком часовом поясе мы находимся
dt = datetime.now(tz.tzlocal())  # 2022-05-10 23:00:00
dt.tzname()                      # MSK

# А теперь попробуем поменять наш часовой пояс, но не меняя окружение
dt = datetime.now(tz.gettz(&#x27;Europe/London&#x27;))  # 2022-05-10 21:00:00
dt.tzname()                                   # BST (British Summer Time)

# Со временем по UTC всё ещё проще
datetime.now(tz.UTC)  # 2022-05-10 20:00:00

# А теперь решим простую задачу с приведением времени по МСК ко времени по UTC
dt = datetime.now(tz.tzlocal())  # 2022-05-10 23:30:00+03:00 MSK
dt.astimezone(tz.UTC)            # 2022-05-10 20:30:00+00:00 UTC</pre>
  <p id="VbeP">Чуть выше мы рассматривали класс <code>timedelta</code> из модуля <strong><em>datetime</em></strong>, который был невероятно полезен при выполнении арифметических операций над объектами даты и времени, но он мог показаться вам не слишком удобным. Класс <code>timedelta</code> предоставлял нам возможность интерпретировать временной отрезок только как дни, секунды и микросекунды, что ограничивало удобство его применения. Разработчики <em><strong>dateutil</strong></em> создали расширенный класс представляющий более гибкий временной отрезок - <code>relativedelta</code>. Давайте подробно посмотрим насколько удобно с ним работать:</p>
  <pre id="oNfm" data-lang="python">from datetime import datetime
from dateutil.relativedelta import relativedelta

# Попробуем прибавить к сегодняшней дате год
datetime.now() + relativedelta(days=365)   # 2023-05-10 00:00:00
datetime.now() + relativedelta(months=12)  # 2023-05-10 00:00:00
datetime.now() + relativedelta(years=1)    # 2023-05-10 00:00:00

# А теперь попробуем получить временной отрезок между сегодня и завтра
relativedelta(td, td + relativedelta(days=1))  # relativedelta(days=-1)

# Проробуем заодно узнать, какой сегодня день недели и когда будет понедельник
import calendar  # &lt;- Это встроенный модуль для работы с Григорианским календарём

calendar.day_name[td.weekday()]              # Friday (Пятница)
td + relativedelta(weekday=calendar.MONDAY)  # 2022-05-16

# А что насчёт узнать какой день в году будет 260-м?
datetime(2003, 1, 1) + relativedelta(yearday=260)  # 2003-09-17
datetime(2000, 1, 1) + relativedelta(yearday=260)  # 2000-09-16
# 2000-й был високосным годом, поэтому дата отличается на 1 сутки
# попробуйте то же самое с 2004-м годом, он также был високосным</pre>
  <p id="iJBz">Надеюсь пакет <strong><em>dateutil</em></strong> уже вселил в вас желание начать использовать его в своих проектах, но впереди ещё много всего интересного. Выше мы уже сталкивались с необходимостью парсить строки и превращать их в обьекты времени, но <strong><em>dateutil</em></strong> выводит парсинг на новый уровень:</p>
  <pre id="jOUO" data-lang="python">from dateutil import parser

parser.parse(&#x27;18th November of 2000 at 12:35&#x27;)  # 2000-11-18 12:35:00
parser.parse(&#x27;11/18/2000 12:35:00&#x27;)             # 2000-11-18 12:35:00
parser.parse(&#x27;2000-11-18 12:35&#x27;)                # 2000-11-18 12:35:00
# Вся магия происходит под капотом, просто наслаждайтесь</pre>
  <p id="bBjd">Говоря о <strong><em>dateutil</em></strong> также нельзя не упомянуть модуль <em>rrule</em> предоставляющий функционал “правил повторения”, описанных в <a href="https://tools.ietf.org/html/rfc5545" target="_blank">iCalendar RFC</a> (В спецификации это называется “Периодами времени”). Его удобно использовать, чтобы создать последовательность дат по определённым правилам, допустим: Все 31-е числа в году или дни с определённым интервалом:</p>
  <pre id="ktjI" data-lang="python">from datetime import datetime
from dateutil.rrule import rrule, YEARLY

# Попробуем узнать 5 високосных лет с 2000-го
start = datetime(2000, 2, 29)  # 29-е Февраля в 2000-м
list(rrule(freq=YEARLY, count=5, dtstart=start))
# [datetime.datetime(2000, 2, 29, 0, 0), 
#  datetime.datetime(2004, 2, 29, 0, 0),
#  datetime.datetime(2008, 2, 29, 0, 0), 
#  datetime.datetime(2012, 2, 29, 0, 0),
#  datetime.datetime(2016, 2, 29, 0, 0)]

# Можно получить даты между двумя другими датами
list(rrule(YEARLY, dtstart=parse(&quot;19980101&quot;), until=parse(&quot;20220131&quot;)))
# [datetime.datetime(1998, 1, 1, 0, 0),
#  ...
#  datetime.datetime(2022, 1, 1, 0, 0)]</pre>
  <p id="LmpL">Увы, чтобы полностью раскрыть потенциал модуля rrule не хватит этой статьи, поэтому я рекомендую ознакомиться с <a href="https://dateutil.readthedocs.io/en/stable/rrule.html" target="_blank">официальной документацией</a>, если вас заинтересовала тема генерации периодов времени.</p>
  <h3 id="CdAE">И про пасху не забудем</h3>
  <p id="PbVF">Пусть вам кажется, что пасха не имеет никакого отношения к этой статье - это не так. Пасха является самым важным христианским праздником, но наш интерес вызывает не история праздника, не мифология и религия, а то, когда пасха празднуется. Википедия говорит следующее:</p>
  <blockquote id="PcU2">Общее правило для расчёта даты Пасхи: «Пасха празднуется в первое воскресенье после весеннего полнолуния».<br />Весеннее полнолуние — первое полнолуние, наступившее не ранее дня весеннего равноденствия.</blockquote>
  <p id="dTHc">Попробуем вычислить пасху используя чистый Python:</p>
  <pre id="Hcn0" data-lang="python">def _easter(factor):
    &quot;&quot;&quot; Метод вычисляющий месяц и день пасхи из фактора&quot;&quot;&quot;
    d = 1 + (factor + 27 + (factor + 6) // 40) % 31
    m = 3 + (factor + 26) // 30
    return m, d

def easter(year = 2000):
    &quot;&quot;&quot; Метод вычисляющий точную дату пасхи &quot;&quot;&quot;
    g, e = year % 19, 10  # Лунный цикл в годах и разница календарей
    i = (19*g + 15) % 30  # Количество дней от 21-го марта до полнолуния
    j = (year + year//4 + i) % 7  # День недели полнолуния
    if year &gt; 1600:
				# Получаем разницу между Юлианским и Григорианским календарём
        e = e + year // 100 - 16 - (year // 100 - 16) // 4
    m, d = _easter(i - j + e) # Точные месяц и день пасхи :)
    return datetime(year, int(m), int(d))

easter(2022)  # 2022-04-24 00:00:00</pre>
  <p id="F5VH">Сам алгоритм вычисления пасхи является достаточно простым, но особенность этого замечательного праздника в том, что разные религии празднуют его в разные даты. Способ определения точной даты пасхи в разных типах христианства называется пасхалика, он определяет по какому календарю будет выбираться дата празднования.</p>
  <p id="EyXp">Пакет <em><strong>dateutil</strong></em> предоставляет нам универсальную функцию вычисления пасхи с учётом всех особенностей этого праздника:</p>
  <pre id="ZIoD" data-lang="python">from dateutil.easter import easter  # &lt;- Функция вычисления пасхи
from dateutil.easter import (
    EASTER_JULIAN,    # &lt;- Алгоритм вычисления по Юлианскому календарю  
    EASTER_ORTHODOX,  # &lt;- Алгоритм вычисления по Григорианскому календарю
    EASTER_WESTERN    # &lt;- Более современный алгоритм
)

easter(EASTER_JULIAN)    # 2022-04-11
easter(EASTER_ORTHODOX)  # 2022-04-24
easter(EASTER_WESTERN)   # 2022-04-17</pre>
  <p id="Ihez">Чтобы лишний раз убедиться в том, что дата вычисляется правильно, мы может воспользоваться сайтом <a href="http://google.com/" target="_blank">google.com</a> или встроенной в UNIX-подобные системы календарной утилитой:</p>
  <pre id="lZ2v" data-lang="shell">$ ncal -o
&gt;&gt; 24 апреля 2022</pre>
  <h2 id="6tjF">Заключение</h2>
  <p id="sWyo">Работа с датой и временем в мире современного IT всё ещё остаётся важной задачей, скрытой под множеством фреймворков и утилит, вычисляющих все производные за нас, но это всё ещё не значит, что мы не должны знать и понимать основ работы со временем.</p>
  <p id="vmLr">Спасибо, что уделили время и прочли этот непростой материал. Я искренне надеюсь, что он улучшит опыт использования вашего программного обеспечения вашими пользователями. Заходите на чашечку чая в мой блог - <a href="https://t.me/+B2irZkLDibZmODQy" target="_blank">Закладки разработчика</a> 🔥.</p>

]]></content:encoded></item></channel></rss>