Даты и время являются частыми элементами во множестве данных, от финансовых отчетов до экологических исследований. Для обработки этих повсеместных данных MATLAB предлагает удобные типы данных.
Тип datetime
Рассмотрим тип данных и одноименную функцию datetime
:
datetime
datetime
15-Sep-2023 09:55:05
Итак, мы начали с функции, которая имеет такое же название, как тип datetime
. Когда мы вызываем её без аргумента, как мы только что сделали, она считывает календарь и часы нашего компьютера и возвращает дату и время в виде объекта, тип которого - datetime
.
Время состоит из трех частей: часов, минут и секунд, разделенных двоеточиями. Эти двоеточия не имеют никакого отношения к оператору «двоеточие» :
, это обычная пунктуация.
Рассмотрим некоторые аргументы, которые можно передать в функцию datetime
.
datetime('yesterday'), datetime('today'), datetime('tomorrow')
ans =
datetime
14-Sep-2023
ans =
datetime
15-Sep-2023
ans =
datetime
16-Sep-2023
Вызов функции с аргументами yesterday
, today
, tomorrow
возвращает дату вчерашнего, сегодняшнего и завтрашнего дня соответственно.
Также можно получить дату и время текущего момента, передав аргумент now
.
datetime('now')
datetime
15-Sep-2023 10:05:35
Тип duration
Тип данных duration
представляет собой полезный инструмент для представления продолжительности времени. Создать переменные этого типа можно с помощью функций seconds
(секунды), minutes
(минуты), hours
(часы) или days
(дни).
hours(2.5)
ans =
duration
2.5 hr
days(2.5)
ans =
duration
2.5 days
Примечательно, что в случае работы с типом данных duration
MATLAB следит за единицами измерения. Для обычных чисел нет возможности определить единицы измерения, например, вес объекта в килограммах.
Арифметика между типами datetime и duration
Длительность можно добавить к переменным типа datetime
. Создадим переменную отображающую временную метку момента её создания:
right_now = datetime('now')
right_now =
datetime
15-Sep-2023 10:42:13
Теперь вычислим дату и время, которые будут через три года от текущей временной метки right_now
.
three_years_from_now = right_now + years(3)
three_years_from_now =
datetime
16-Sep-2026 04:09:49
Через три дня:
three_days_from_now = right_now + days(3)
three_days_from_now =
datetime
18-Sep-2023 10:42:13
Три дня назад:
three_days_ago = right_now - days(3)
three_days_ago =
datetime
12-Sep-2023 10:42:13
Самое время обратить внимание на то, что названия двух функций - years
(года) и days
(дни), а также их родственных функций - hours
(часы), minutes
(минуты), seconds
(секунды) - имеют множественное число. Они имеют букву s
на конце. Необходимо об этом помнить, потому что существуют также функции с одноименными названиями в единственном числе, такие как day
, year
и second
которые имеют совсем другие значения. О них мы поговорим позже.
Аргументы функции datetime
Сложение и вычитание - не единственный способ получить даты в далеком прошлом или будущем. Функция datetime
также принимает аргументы, с помощью которых можно напрямую перейти к любой точке во времени.
Первое публичное представление MATLAB состоялось на заседании конференции “IEEE Conference on Decision and Control”, CDC, в Лас-Вегасе в декабре 1984 года. Конференция проходила с 12 по 14 декабря. Зададим дату начала этой конференции
matlab_debut = datetime(1984, 12, 12)
matlab_debut =
datetime
12-Dec-1984
Перемирие, положившее конец боевым действиям в Первой мировой войне, было подписано в 1918 году в 11 часов 11-го дня 11-го месяца.
Armistice_WWI = datetime(1918, 11, 11, 11, 0, 0)
Но чей это был 11-й час? Если посмотреть на разные страны мира, где-то может быть 11 часов утра, а где-то — 5 часов вечера. Договор был подписан в Лондоне, и мы можем установить это время в соответствии с лондонским часовым поясом.
Чтобы установить часовой пояс, сначала нужно выяснить, в каком часовом поясе находится Лондон. Часовые пояса управляются по всему миру организацией Internet Assigned Numbers Authority, которая известна в первую очередь тем, что распределяет интернет-номера. MATLAB избавляет нас от хлопот определения часового пояса, предоставляя удобную функцию, называемую timezones
.
При вызове функции timezones
открывается таблица поиска мест и соответствующие им часовые пояса
Armistice_WWI.TimeZone = 'Europe/London'
Armistice_WWI =
datetime
11-Nov-1918 11:00:00
Ничего не изменится, потому что для этой переменной ранее не был установлен часовой пояс. Давайте сделаем копию этой переменной. Копирование переменной включает в себя все её содержимое, так что свойство часового пояса сохранится.
Изменим часовой пояс соответствующий Москве:
Armistice_WWI_Moscow = Armistice_WWI;
Armistice_WWI_Moscow.TimeZone = 'Europe/Moscow';
Armistice_WWI_Moscow
Armistice_WWI_Moscow =
datetime
11-Nov-1918 14:31:19
Чтобы просмотреть возможные поля переменной datetime
:
fieldnames(Armistice_WWI)
ans =
8×1 cell array
{'Format' }
{'TimeZone'}
{'Year' }
{'Month' }
{'Day' }
{'Hour' }
{'Minute' }
{'Second' }
Следует отметить, что имена полей чувствительны к регистру, и мы не можете добавлять новые поля.
Версальский договор был подписан спустя 228 дней с момента перемирия:
Treaty_of_Versailles = Armistice_WWI + days(228)
Treaty_of_Versailles =
datetime
27-Jun-1919 12:00:00
Мы уже несколько раз замечали, что время изменилось; это связано с учетом перехода на летнее время. В Европе впервые стали переходить на летнее время в 1916 году.
К счастью для нас, MATLAB позаботится об учете перехода на летнее время. Это является большим преимуществом, особенно, когда время суток имеет значение.
Настройка формата времени datetime
В случае с Версальским договором время не играет особенной роли. Так как нас интересует только дата, следует изменить форматирование используя поле Format
.
Treaty_of_Versailles.Format = 'dd-MMMM-yy';
Treaty_of_Versailles
Treaty_of_Versailles =
datetime
27-June-19
Теперь время не отображается. Если же мы предпочитаем более короткий формат:
Treaty_of_Versailles.Format = 'dd/MM/yy';
Treaty_of_Versailles
Treaty_of_Versailles =
datetime
27/06/19
Или, может быть, еще более длинный формат:
Treaty_of_Versailles.Format = 'eeee, MMMM dd, yyyy';
Treaty_of_Versailles
Treaty_of_Versailles =
datetime
Friday, June 27, 1919
Теперь мы знаем, что Версальский договор был подписан в пятницу.
Давайте посмотрим когда был 100 летний юбилей:
Treaty_of_Versailles + years(100)
ans =
datetime
Wednesday, June 26, 2019
Годовщина должна быть 27-го, но почему-то мы получили 26 июня.
Почему наши расчеты отличаются на один день? Все дело в високосных годах, когда в календарь вводится дополнительный день — 29 февраля. В то же время функция years
предполагает, что в каждом году ровно 365.2425
дней.
Работа с календарными промежутками времени
К счастью, MATLAB предлагает способ решить и эту проблему. Для достижения точного соответствия используйте функцию calyears
, что означает “календарные годы” (calendar years). Эта функция учитывает график високосных лет.
Treaty_of_Versailles + calyears(100)
ans =
datetime
Thursday, June 27, 2019
calyears
является функцией возвращающей тип данных calendarDuration
.
Тип данных calendarDuration
используется, когда требуется вариативный промежуток времени. Например, продолжительность 1 месяца может составлять 28, 30 или 31 день. Функции caldays
, calweeks
, calmonths
, calquarters
, calyears
возвращают тип данных calendarDuration
.
Различия между схожими по названию функциями
Обратим внимание на функции year
, month
, week
, day
, hour
, minute
, second
, и quarter
, которые не следует путать с их множественными аналогами. Зачем они нужны и что они делают?
Эти функции появились за много лет до введения типов данных datetime
и duration
в 2014 году. Изначально они были частью библиотеки для решения финансовых задач, что представляет собой одно из ключевых направлений использования MATLAB.
После добавления новых типов данных, старые функции были обновлены, чтобы принимать новые типы данных datetime
и duration
. В связи с необходимостью новой функциональности были введены отдельные функции years
, days
, hours
, minutes
и другие, названные иначе, чтобы обеспечить совместимость с предыдущими версиями MATLAB. Эти новые функции получили имена во множественном числе для отличия от оригинальных.
Давайте рассмотрим, что делают старые функции:
year(Treaty_of_Versailles)
ans =
1919
month(Treaty_of_Versailles)
ans =
6
day(Treaty_of_Versailles)
ans =
27
hour(Treaty_of_Versailles)
ans =
12
Эти функции разделяют дату на части. Вы даже можете определить к какому кварталу года относится дата.
quarter(Treaty_of_Versailles)
ans =
2
Есть и другой способ разделения информации - с помощью точечной нотации:
Armistice_WWI.Year, Armistice_WWI.Day, Armistice_WWI.Hour
ans =
1918
ans =
11
ans =
11
Также можно воспользоваться распаковкой значений, функциями yms
и hms
:
[y, mo, d] = ymd(Armistice_WWI)
y =
1918
mo =
11
d =
11
[h, min, s] = hms(Armistice_WWI)
h =
11
min =
0
s =
0
В каждом случае значения, которые мы получили в результате этих многочисленных операций извлечения, будут типа double
. Ни одна из них не вернет нам объект типа duration
.
Чтобы получить объект типа duration
необходимо использовать функции, именованные во множественном числе, такие как years
и days
. Однако существует еще одна функция, которая также может вернуть duration
, и запомнить ее название довольно легко — duration
(длительность).
long_movie = duration(3, 7, 43)
long_movie =
duration
03:07:43
Входные параметры функции duration
работают аналогично входным данным функции datetime
, за исключением того, что начинаются с часов, а не с лет. Эти входные данные составляют 3
часа, 7
минут и 43
секунды. Выходные данные также похожи, но вместо запятых значения разделяются двоеточиями.
Можно также указать четвертый аргумент для duration
, если необходима большая точность времени.
Операции с типом duration
Что возможно с объектом duration
? Например, с ним можно производить арифметические операции. Предположим, мы хотим узнать середину длительности фильма.
half_movie = long_movie/2
half_movie =
duration
01:33:51
2 * half_movie
Давайте умножим половину длительности на два, ожидаемо мы получим исходную длительность:
ans =
duration
03:07:43
Вычитание тоже работает:
long_movie - half_movie
ans =
duration
01:33:51
Что случится в случае добавления единицы?
long_movie + 1
ans =
duration
27:07:43
Неожиданно фильм стал действительно очень длинным. Напомним, что числа по умолчанию являются типом double
. Этот пример смешанной арифметики особенно интересен, при добавление 1 к продолжительности она увеличивается на 24 часа, что предполагает, что основная единица измерения продолжительности - день, аналогично системе дат в Microsoft Excel.
Тем не менее, напрямую перевести продолжительность в тип double
не получится:
double(long_movie)
Error using duration/double (line 1104)
Undefined function 'double' for input arguments of type 'duration'. To convert from durations to numeric, use the SECONDS, MINUTES, HOURS, DAYS,
or YEARS functions.
Поскольку MATLAB не может знать, в каких единицах времени мы хотим получить ответ, он не выполнит преобразование. Вместо этого необходимо использовать функции seconds
, minutes
, hours
, days
, устраняя неоднозначность намерений.
Рассмотрим пример, посчитаем продолжительность семи летней войны:
start_7_years_war = datetime(1756, 5, 17);
end_7_years_war = datetime(1763, 2, 15);
duration_7_years_war = end_7_years_war - start_7_years_war
duration_7_years_war =
duration
59160:00:00
Теперь мы знаем, что семи летняя война длилась 59 160 часов. Переведем продолжительность в годы:
year_count = years(duration_7_years_war)
year_count =
6.7489
Неполные 7 лет, узнаем насколько меньше в днях:
missing_days = days(start_7_years_war + calyears(7) - end_7_years_war)
missing_days =
91
Важно отметить, что такое вычитание учитывает вариативное количество дней в каждом месяце.
Представим, что война началась и закончилась на год позже:
missing_days = days((start_7_years_war + calyears(1) + calyears(7)) - ...
(end_7_years_war + calyears(1)))
missing_days =
92
На день больше. 1764 год был високосным, так что в феврале было 29 дней.
Но есть, что-то еще более интересное в результате, он неожиданно не содержит единицы измерения, хотя ранее мы говорили, что тип duration
указывает, например, часы, минуты или недели.
Полиморфизм функций years, days, hours, minutes…
Дело в том, что days
не возвращает duration
, а возвращает double
, в случае передачи входного аргумента типа duration
.
% missing_days = days(start_7_years_war + calyears(7) - end_7_years_war)
class(start_7_years_war + calyears(7) - end_7_years_war)
class(missing_days)
ans =
'duration'
ans =
'double'
С другой стороны при передачи типа double
вернется тип duration
.
days(2.5)
ans =
duration
2.5 days
Каким образом функция может вести себя по-разному от одного вызова к другому? Дело в полиморфизме. Полиморфизм - это способность функции обрабатывать данные разных типов. Эта функция и её «родственники» — years
, minutes
и так далее — полиморфны. В нашем случае тип их вывода зависит от типа их ввода.
Такое решение делает функции универсальными, контекстно-интеллектуальными и на самом деле более интуитивными. При передаче в эти функции типа duration
, выводится тип double
, и это закладывает основу для выполнения арифметических действий (поскольку числовые значения по умолчанию double
). Если мы передаем длительность (тип duration
) в функцию days
мы спрашиваем: «сколько дней составляет эта длительность?». Следовательно, возврат числа (тип doudle
) имеет смысл. Если же мы наоборот передадим в функцию days
число (тип doudle
), логично ожидать продолжительность (тип duration
), поскольку вы указываете «столько-то дней».
Во многом функции years
, days
, hours
, minutes
, seconds
, и milliseconds
, следует воспринимать, в первую очередь, как конверсию (перевод) между duration
и double
, и только во вторую очередь, как функции конверсии между единицами измерения времени. Полиморфизм обеспечивает возможность выполнения 12 конверсий шестью функциями.
Сравнение между временными точками
В MATLAB возможно сравнивать точки во времени с помощью операторов сравнения. Давайте рассмотрим пример. Прежде всего, давайте определим пару новых точек во времени.
catholic_christmas = datetime(2024, 12, 25);
orhtodox_christmas = datetime(2025, 01, 07);
catholic_christmas < orthodox_christmas
ans =
logical
1
string(ans)
ans =
"true"
Мы получили истинность выражения catholic_christmas < orthodox_christmas
, действительно, католическое рождество наступает раньше православного.