{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Дата и время\n", "\n", "Для представления времени в компьютере обычно используется так называемое [Unix-время](https://ru.wikipedia.org/wiki/Unix-%D0%B2%D1%80%D0%B5%D0%BC%D1%8F) (иногда `Epoch time`, `Posix time`, `seconds since the Epoch`, или `UNIX Epoch time`), которое \n", "> Определяется как количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года (четверг); этот момент называют «эпохой Unix» (англ. Unix Epoch). Unix-время представлено целым числом, которое увеличивается с каждой прошедшей секундой без необходимости вычислений для определения года, месяца, дня, часа или минуты для удобства восприятия человеком.\n", "\n", "## Модуль стандартной библиотеки time\n", "\n", "Функция [time.time](https://docs.python.org/3/library/time.html#time.time) модуля [time](https://docs.python.org/3/library/time.html#module-time) возвращает именно Unix-время, только не в виде целого числа, а в виде `float`, т.е. доли секунды тоже учитываются. Если доли секунды играют роль, то лучше прибегать к [time.time_ns](https://docs.python.org/3/library/time.html#time.time_ns), которое возвращает количество наносекунд с `Unix epoch`.\n", "\n", "```{tip}\n", "Для измерения промежутков времени (например, замер времени исполнения блока кода) лучше использовать функцию [time.perf_counter](https://docs.python.org/3/library/time.html#time.perf_counter) или [time.perf_counter_ns](https://docs.python.org/3/library/time.html#time.perf_counter_ns), т.к. она точнее измеряет время. Но важно знать, что возвращаемое время отсчитывается от произвольной точки отсчета, т.е. только разница между двумя значениями имеет смысл.\n", "```\n", "\n", "Функция [time.ctime](https://docs.python.org/3/library/time.html#time.ctime) преобразует Unix-время в строку в формате `'Sun Jun 20 23:21:05 1993'`, т.е. какая дата и какое время было при пройденном количестве секунд с `Unix-epoch`. Чтобы получить строку в другом формате, можно воспользоваться функцией [time.strftime](https://docs.python.org/3/library/time.html#time.strftime), которая в качестве первого аргумента принимает форматирующую строку. Как составить форматирующую строку можно прочитать [здесь](https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior) и [здесь](https://docs.python.org/3/library/time.html#time.strftime). Ниже приводится не полная таблица с допустимыми директивами в форматирующей строке.\n", "\n", "| Директива | Значение | Пример |\n", "|---|---|---|\n", "| `%a`| Аббревиатура дня недели | Sun, Mon, …, Sat (en_US)| \n", "| `%A` | Полное имя дня недели | Sunday, Monday, …, Saturday (en_US)|\n", "| `%w` | Номер дня в неделе, где 0 - воскресенье, 6 --- суббота. | 0, 1, …, 6 | \n", "| `%d` | Номер дня в месяце (двузначное десятичное число) | 01, 02, …, 31 | \n", "| `%b` | Аббревиатура месяца | Jan, Feb, …, Dec (en_US); | \n", "| `%B` | Полное название месяца. | January, February, …, December (en_US); | \n", "| `%m` | Номер месяца (двузначное десятичное число) | 01, 02, …, 12 |\n", "| `%y` | Год без учета века | 00, 01, …, 99 |\n", "| `%Y` | Год с учетом века | 0001, 0002, …, 2013, 2014, …, 9998, 9999 |\n", "| `%H` | Номер часа в 24-часовом формате | 00, 01, …, 23 | \n", "| `%I` | Номер часа в 12-часовом формате | 01, 02, …, 12 | \n", "| `%p` | До полудня или после | AM, PM (en_US); |\n", "| `%M` | Номер минуты в часе | 00, 01, …, 59 |\n", "| `%S` | Номер секунды в минуте | 00, 01, …, 59 | \n", "| `%f` | Номер микросекунды в секунде. | 000000, 000001, …, 999999 | \n", "\n", "Директивы `%a`, `%A`, `%b`, `%B`, `%p` вставляют строковые представления в соответствии с переменными среды. Хотя в стандартной библиотеке есть модуль [locale](https://docs.python.org/3/library/locale.html#module-locale), который позволяет локализовать вывод таких значений методом [locale.setlocale](https://docs.python.org/3/library/locale.html#locale.setlocale) (`locale.setlocale(locale.LC_ALL, \"ru_RU\")` для русского), рекомендуется использовать для таких целей библиотеку [babel](http://babel.pocoo.org/en/latest/index.html) (подробнее [здесь](http://babel.pocoo.org/en/latest/dates.html))." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1638382039.2391078\n", "Wed Dec 1 21:07:19 2021\n", "Wed Dec 1 21:07:20 2021\n", "time.struct_time(tm_year=2021, tm_mon=12, tm_mday=1, tm_hour=21, tm_min=7, tm_sec=19, tm_wday=2, tm_yday=335, tm_isdst=0)\n", "time.struct_time(tm_year=2021, tm_mon=12, tm_mday=1, tm_hour=21, tm_min=7, tm_sec=20, tm_wday=2, tm_yday=335, tm_isdst=0)\n", "01.12.2021 21:07:19\n", "01.12.2021 21:07:20\n" ] } ], "source": [ "import time\n", "\n", "\n", "t0 = time.time()\n", "time.sleep(1.0) # in seconds\n", "print(t0)\n", "\n", "print(time.ctime(t0))\n", "print(time.ctime())\n", "\n", "print(time.localtime(t0))\n", "print(time.localtime())\n", "\n", "print(time.strftime(r\"%d.%m.%Y %H:%M:%S\", time.localtime(t0)))\n", "print(time.strftime(r\"%d.%m.%Y %H:%M:%S\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Модуль стандартной библиотеки datetime\n", "\n", "[datetime](https://docs.python.org/3/library/datetime.html#module-datetime) --- модуль стандартной библиотеки для работы с датами и временем, который предназначен для работы с временем в разном формате (парсинг и форматирование), а так же предусматривает арифметические операции над моментами во времени.\n", "\n", "Основные типы объектов в модуле `datetime`:\n", "- `datetime.date` --- дата с аттрибутами `year`, `month` и `day`;\n", "- `datetime.time` --- время с аттрибутами `hour`, `minute`, `second`, `microsecond`;\n", "- `datetime.datetime` --- комбинация datetime.date и datetime.time (и те и те аттрибуты);\n", "- `datetime.timedelta` --- промежуток времени между двумя `datetime.datetime` (или просто `time` и `date`);\n", "- `datetime.tzinfo` и `datetime.timezone` --- информация о часовом поясе.\n", "\n", "```{note}\n", "Объекты `datetime` и `time` опционально могут содержать информацию о часовом поясе. Тогда их называют `aware`. Объекты без информации о часовом поясе называют `naive`. Здесь будут рассматриваться только `naive` объекты.\n", "```\n", "\n", "Кроме того, что объекты классов `date`, `time`, `datetime` можно сразу печатать функцией `print` (по умолчанию `YYYY-MM-DD`), они ещё имеют метод `strftime`, который работает аналогично функции из модуля `time`. \n", "\n", "```{note}\n", "Минимальный возможный год для объектов этого модуля --- 1 год нашей эры, а максимальный --- 9999 год нашей эры (все согласно григорианскому календарю). Для работы с датами до нашей эры или, например, для дат в астрономическом масштабе, этот модуль не подходит.\n", "```" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import datetime" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### datetime.date\n", "\n", "`datetime.date` --- объект с атрибутами\n", "- `year` $\\in \\{1, 2, ..., 9999\\}$ --- год (2021, например);\n", "- `month` $\\in \\{1, 2, ..., 12\\}$ --- месяц;\n", "- `day` $\\in \\{1, 2, ..., N\\}$ --- номер дня в месяце ($N$ --- количество дней в месяце);\n", "\n", "Создать объект `datetime.date` можно несколькими способами. \n", "\n", "Например, явно вызвав конструктор." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Первый полёт в космос: 04/12/1963\n" ] } ], "source": [ "the_date = datetime.date(year=1963, month=4, day=12)\n", "the_date_formatted = the_date.strftime(\"%m/%d/%Y\")\n", "print(f\"Первый полёт в космос: {the_date_formatted}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Из `Unix-времени` методом [datetime.date.fromtimestamp](https://docs.python.org/3/library/datetime.html#datetime.date.fromtimestamp)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Unix-epoch соответствует дате 1970-01-01.\n" ] } ], "source": [ "unix_epoch = datetime.date.fromtimestamp(0)\n", "print(f\"Unix-epoch соответствует дате {unix_epoch}.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Или методом [datetime.date.today](https://docs.python.org/3/library/datetime.html#datetime.date.today), если нужна дата на сегодня (согласно часам компьютера).\n", "\n", "```{note}\n", "Этот метод аналогичен `datetime.date.fromtimestamp(time.time())`.\n", "```" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2021-12-01\n", "День: 1, месяц: 12, год 2021\n", "3 день недели: Wednesday\n" ] } ], "source": [ "\n", "today = datetime.date.today()\n", "print(today)\n", "print(f\"День: {today.day}, месяц: {today.month}, год {today.year}\")\n", "print(f\"{today.weekday() + 1} день недели: {today.strftime('%A')}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### datetime.time\n", "\n", "[datetime.time](https://docs.python.org/3/library/datetime.html#datetime.time) --- объект для хранения времени внутри суток, т.е. он имеет поля\n", "- `hour` $\\in \\{0, 1, ..., 23\\}$ --- час,\n", "- `minute` $\\in \\{0, 1, ..., 59\\}$ --- минута,\n", "- `second` $\\in \\{0, 1, ..., 59\\}$ --- секунда, \n", "- `microsecond` $\\in \\{0, 1, ..., 999999\\}$ --- микросекунда,\n", "- `fold` $\\in [0, 1]$ --- параметр, который используется для разрешения противоречий, если есть данные о часовом поясе (переводы часов назад и так далее)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Старт корабля Восток 1 состоялся в 09:07 по Московскому времени.\n" ] } ], "source": [ "the_time = datetime.time(hour=9, minute=7) \n", "the_time_formatted = the_time.strftime(\"%H:%M\")\n", "print(f\"Старт корабля Восток 1 состоялся в {the_time_formatted} по Московскому времени.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Редко возникают ситуации, когда нужно только время внутри суток без даты, поэтому `datetime.time` используется значительное реже, чем `datetime.datetime`. \n", "\n", "### datetime.datetime\n", "\n", "[datetime.datetime](https://docs.python.org/3/library/datetime.html#datetime.datetime) --- по сути дела комбинация предыдущих двух типов `datetime.date` и `datetime.time`, т.е. имеет аттрибуты и того и того:\n", "- `year`;\n", "- `month`;\n", "- `day`;\n", "- `hour`;\n", "- `minute`;\n", "- `second`;\n", "- `microsecond`;\n", "- `fold`;\n", "\n", "Конструктор `datetime.datetime` в качестве входных параметров принимает параметры обоих классов `datetime.date` и `datetime.time`. Также, можно предварительно создать объекты `datetime.date` и `datetime.time` и объединить их в `datetime.datetime` методом [combine](https://docs.python.org/3/library/datetime.html#datetime.datetime.combine)." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Церемония открытия московской олимпиады началась 19.07.1980 в 16:00.\n", "Старт корабля Восток 1 состоялся 12/04/63 в 09:07 по Московскому времени.\n" ] } ], "source": [ "olympics = datetime.datetime(year=1980, month=7, day=19, hour=16)\n", "print(olympics.strftime(\"Церемония открытия московской олимпиады началась %d.%m.%Y в %H:%M.\"))\n", "\n", "the_date_the_time = datetime.datetime.combine(the_date, the_time)\n", "print(the_date_the_time.strftime(\"Старт корабля Восток 1 состоялся %d/%m/%y в %H:%M по Московскому времени.\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Также есть и метод [fromtimestamp](https://docs.python.org/3/library/datetime.html#datetime.datetime.fromtimestamp), который в данном случае учитывает часовой пояс системы, т.е. по количеству секунд, прошедших с полуночи по Гринвичу 1 января 1970 года, он возвращает дату и время, которые были в это время в этом часовом поясе. Метод [utcfromtimestamp](https://docs.python.org/3/library/datetime.html#datetime.datetime.utcfromtimestamp) делает то же самое, но не учитывая часовой пояс." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Мировое Unix-время отсчитывается с 03:00 по московскому времени\n", "Мировое Unix-время отсчитывается с 00:00 UTC\n" ] } ], "source": [ "unix_epoch_moscow = datetime.datetime.fromtimestamp(0)\n", "print(unix_epoch_moscow.strftime(\"Мировое Unix-время отсчитывается с %H:%M по московскому времени\"))\n", "\n", "unix_epoch_UTC = datetime.datetime.utcfromtimestamp(0)\n", "print(unix_epoch_UTC.strftime(\"Мировое Unix-время отсчитывается с %H:%M UTC\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но чаще всего `datetime.datetime` образуются методом [datetime.datetime.strptime](https://docs.python.org/3/library/datetime.html#datetime.datetime.strptime) из текстового представления. Формате текстового представления для времени разный для разных стран/учреждений/организаций и т.д. Например, где-то 26 января записывается в виде \"26.01\", а где-то в \"01/26\". Чтобы модуль `datetime` смог разобраться, где в строке расположены значения, соответствующие номеру месяца, а где номеру дня, году, времени и т.п., необходимо передать в метод `strptime` форматирующую строку в качестве второго аргумента." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1879-03-14 00:00:00\n", "2019-05-18 15:17:08.132263\n", "2063-11-12 00:00:00\n" ] } ], "source": [ "t1 = \"14 March 1879\"\n", "t2 = \"2019-05-18T15:17:08.132263\"\n", "t3 = \"11/12/63\"\n", "\n", "\n", "print(datetime.datetime.strptime(t1, \"%d %B %Y\"))\n", "print(datetime.datetime.strptime(t2, \"%Y-%m-%dT%H:%M:%S.%f\"))\n", "print(datetime.datetime.strptime(t3, \"%m/%d/%y\"))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Разберем примеры выше:\n", "1. В строковом представлении `t1` идут сначала две цифры, обозначающие день, т.е. используем директива `%d`. Затем идёт полное название месяца (директива `%B`, а затем идёт год с указанием всех 4 цифр (директива `%Y`)). Т.к. все эти элементы разделены пробелом, то форматирующая строка равна `\"%d %B %Y\"`;\n", "2. `t1` соответствует [iso-формату](https://ru.wikipedia.org/wiki/ISO_8601). Полный год (`%Y`), месяц (`%m`), день `%d`, символ `\"T\"`, час (`%H`), минута (`%M`), секунда (`%S`), микро секунда (`%f`). Месяц отделяется от дня и год символом дефиса `\"-\"`, минута отделяется от часа и секунды символом двоеточия (`\"-\"`), и микросекунда отделяется точкой перед ней. Комбинируя это всё, получаем форматирующую строку `\"%Y-%m-%dT%H:%M:%S.%f\"`\n", "3. Дата разделенная символами `/`, в годе не указан век. Подразумевается, что дата представлена в формате, используемом в `USA`, т.е. сначала идёт месяц, а затем день (день убийства Кеннеди). Хочется использоваться форматирующую строку `\"11/12/63\"`, но она возвращает 2063 год. Т.к. такая строка не содержит достаточно информации, чтобы определить правильный век, то её необходимо обрабатывать отдельно. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### timedelta\n", "\n", "Объекты [datetime.timedelta](https://docs.python.org/3/library/datetime.html#datetime.timedelta) позволяют производить арифметику с объектами `datetime.datetime`, `datetime.date` и `datetime.time`. Пусть `t` --- объект `datetime` или его аналог, а `dt` --- объект `deltatime`, тогда определенны следующие операции:\n", "- `dt` = `t2` - `t1`;\n", "- `t2` = `t1` $\\pm$ `dt`;\n", "- `t1` < `t2`;\n", "- др.\n", " " ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "datetime.timedelta(days=8005)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "today - datetime.date(2000, 1, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Между сегодня и началом века прошло 8005 дней." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Вчера: 2021-11-30,\n", "Сегодня: 2021-12-01,\n", "Завтра: 2021-12-02\n", "True\n" ] } ], "source": [ "a_day = datetime.timedelta(days=1)\n", "yesterday = today - a_day\n", "tomorrow = today + a_day\n", "print(f\"Вчера: {yesterday},\\nСегодня: {today},\\nЗавтра: {tomorrow}\")\n", "print(yesterday < today)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "datetime.timedelta(seconds=1, microseconds=509398)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dt = datetime.datetime.now() - datetime.datetime.fromtimestamp(t0)\n", "dt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## pandas.DateTime\n", "\n", "Для работы со временем `pandas` опирается на типы данных [np.datetime64 и np.timedelta64](https://numpy.org/doc/stable/reference/arrays.datetime.html#datetime-units) из библиотеки `NumPy`. Например, сделать `pd.Series` типа `datetime64[ns]` можно методом [pd.to_datetime](https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html). " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2022-01-01', '2022-01-02'], dtype='datetime64[ns]', freq=None)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "t = [\"2022-1-1\", \"2022-1-2\"]\n", "t = pd.to_datetime(t)\n", "t\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если аргумент метода `pd_todatetime` --- список, то создаётся объект [pd.DateTimeIndex](https://pandas.pydata.org/docs/reference/api/pandas.DatetimeIndex.html), а если `pd.Series`, то создаётся другая `pd.Series` типа `datetime64[ns]`. \n", "\n", "По умолчанию `pandas` парсит в формате `\"%Y-%m-%d\"`, но если данные представлены в другом формате, то можно его указать параметром format." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 2022-01-01 12:00:00\n", "1 2022-01-02 11:00:00\n", "dtype: datetime64[ns]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = pd.Series([\"12:00 1.1.22\", \"11:00 2.1.22\"])\n", "t = pd.to_datetime(t, format=\"%H:%M %d.%m.%y\")\n", "t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Иногда необходимо задавать точки времени с определенной периодичностью. Для это удобно использовать функцию [pd.date_range](https://pandas.pydata.org/docs/reference/api/pandas.date_range.html)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2022-01-01 12:00:00', '2022-01-01 13:00:00',\n", " '2022-01-01 14:00:00', '2022-01-01 15:00:00',\n", " '2022-01-01 16:00:00', '2022-01-01 17:00:00',\n", " '2022-01-01 18:00:00', '2022-01-01 19:00:00',\n", " '2022-01-01 20:00:00', '2022-01-01 21:00:00',\n", " '2022-01-01 22:00:00', '2022-01-01 23:00:00',\n", " '2022-01-02 00:00:00', '2022-01-02 01:00:00',\n", " '2022-01-02 02:00:00', '2022-01-02 03:00:00',\n", " '2022-01-02 04:00:00', '2022-01-02 05:00:00',\n", " '2022-01-02 06:00:00', '2022-01-02 07:00:00',\n", " '2022-01-02 08:00:00', '2022-01-02 09:00:00',\n", " '2022-01-02 10:00:00', '2022-01-02 11:00:00'],\n", " dtype='datetime64[ns]', freq='H')" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "datetimerange = pd.date_range(start=t.iloc[0], end=t.iloc[1], freq=\"H\")\n", "datetimerange" ] } ], "metadata": { "interpreter": { "hash": "cd49b4596bae9c980ff74fdf93e8fe80e447435ae307c062fad6c4f9ef2eb47f" }, "kernelspec": { "display_name": "Python 3.8.10 64-bit ('venv': venv)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }