Дата и время
Значения типа datetime, предназначенные для хранения даты и/или времени, как правило, подвергаются нескольким видам конвертации:
- в строки и обратно для отображения пользователю и чтения из внешних источников данных;
- в специальные структуры MqlDateTime (см. ниже) для работы с отдельными компонентами даты и времени;
- в количество секунд, прошедшее с 01.01.1970, что соответствует внутреннему представлению datetime, и эквивалентно целому типу long.
Для последнего пункта используйте приведение datetime к (long) или обратно long к (datetime), но учтите, что поддерживаемый диапазон дат — от 1-го января 1970 года (значение 0) до 31 декабря 3000 года (32535215999 секунд).
Для первых двух пунктов MQL5 API предоставляет нижеследующие функции.
string TimeToString(datetime value, int mode = TIME_DATE | TIME_MINUTES)
Функция TimeToString преобразует значение типа datetime в строку с компонентами даты и времени, в соответствии с параметром mode, в котором можно задать произвольное сочетание флагов:
- TIME_DATE — дата в формате "YYYY.MM.DD";
- TIME_MINUTES — время в формате "hh:mm", то есть с часами и минутами;
- TIME_SECONDS — время в формате "hh:mm:ss", то есть с часами, минутами и секундами.
Для полного вывода даты и времени можно задать mode равный TIME_DATE | TIME_SECONDS (вариант TIME_DATE | TIME_MINUTES | TIME_SECONDS тоже сработает, но является избыточным). Это эквивалентно приведению значения типа datetime к (string).
Примеры использования сведены в файл ConversionTime.mq5.
#define PRT(A) Print(#A, "=", (A))
|
Скрипт выведет в журнал:
(string)time=2021.01.21 23:00:15
|
datetime StringToTime(string value)
Функция StringToTime преобразует строку, содержащую дату и/или время, в значение типа datetime. Строка может содержать только дату, только время или дату и время вместе.
Для дат распознаются следующие форматы:
- "YYYY.MM.DD"
- "YYYYMMDD"
- "YYYY/MM/DD"
- "YYYY-MM-DD"
- "DD.MM.YYYY"
- "DD/MM/YYYY"
- "DD-MM-YYYY"
Для времени поддерживаются следующие форматы:
- "hh:mm"
- "hh:mm:ss"
- "hhmmss"
Между датой и временем должен быть как минимум один пробел.
Если в строке присутствует только время, в результат будет подставлена текущая дата. Если в строке присутствует только дата, время будет равно 00:00:00.
Если поддерживаемый синтаксис в строке нарушен, результатом будет текущая дата.
Примеры использования функции приведены в скрипте ConversionTime.mq5.
void OnStart()
|
В журнале мы увидим примерно следующее (####.##.## — текущая дата запуска скрипта):
timeonly=21:01
|
Помимо StringToTime для преобразования строк в дату и время можно пользоваться оператором приведения к типу (datetime). Однако плюс функции в том, что при обнаружении некорректной исходной строки она устанавливает внутреннюю переменную с кодом ошибки _LastError (доступную также через функцию GetLastError). В зависимости от того, какая часть строки содержит неинтерпретируемые данные, код ошибки может быть ERR_WRONG_STRING_DATE (5031), ERR_WRONG_STRING_TIME (5032) или другой из списка относящихся к получению даты и времени из строки.
bool TimeToStruct(datetime value, MqlDateTime &struct)
Для анализа компонентов даты и времени по отдельности MQL5 API предоставляет функцию TimeToStruct, которая преобразует значение типа datetime в структуру MqlDateTime:
struct MqlDateTime
|
Дни недели нумеруются на американский манер: 0 — воскресенье, 1 — понедельник, и так далее вплоть до 6 — суббота. Для их идентификации существует встроенное перечисление ENUM_DAY_OF_WEEK.
Функция возвращает true в случае успеха, и false — в случае ошибки, в частности, если передана некорректная дата.
Проверим работу функции с помощью скрипта ConversionTimeStruct.mq5. Для этого создадим массив time типа datetime с тестовыми значениями, и в цикле вызовем TimeToStruct для каждого из них.
Результаты будем складывать в массив структур MqlDateTime mdt[]. Предварительно мы его проинициализируем нулями, но поскольку встроенная функция ArrayInitialize не умеет обрабатывать структуры, нам придется написать её перегрузку (в будущем мы познакомимся с более простым способом заполнить массив нулями: в разделе Обнуление объектов и массивов будет представлена функция ZeroMemory).
int ArrayInitialize(MqlDateTime &mdt[], MqlDateTime &init)
|
После процесса мы выведем массив структур в журнал с помощью встроенной функции ArrayPrint — это самый простой способ красиво отформатировать данные (его можно использовать даже если структура одна: достаточно положить её в массив размером 1).
void OnStart()
|
В результате получим следующие строки в журнале:
time[i]=2021.01.28 23:00:15
|
Можно убедиться, что все поля получили соответствующие значения. Для некорректных исходных дат мы сохраняем код ошибки в поле year (в данном случае, такая ошибка одна: 4010, ERR_INVALID_DATETIME).
Напомним, что для максимального значения даты в MQL5 заведена константа DATETIME_MAX, равная целочисленному значению 0x793406fff, что соответствует 23:59:59 31 декабря 3000.
Наиболее часто встречающаяся задача, которую решают при помощи функции TimeToStruct, — это получение значения конкретной компоненты даты/времени. Поэтому имеет смысл подготовить вспомогательный заголовочный файл (MQL5Book/DateTime.mqh) с готовым вариантом реализации. В файле имеется класс DateTime.
class DateTime
|
К классу прилагается несколько макросов, упрощающих вызов его методов.
#define TimeDayOfWeek(T) DateTime::assign(T).timeDayOfWeek()
|
В классе есть поле mdtstruct типа структуры MqlDateTime. Именно оно используется во всех внутренних преобразованиях. Поля структуры читаются с помощью методов-"геттеров": для каждого поля выделен соответствующий метод.
Внутри класса определен единственный статический экземпляр _DateTime (одного объекта достаточно, потому что все MQL-программы однопоточные). Конструктор является закрытым, поэтому создать другие объекты DateTime не удастся.
С помощью макросов можно удобно выделить из datetime, например, год (TimeYear(T)), месяц (TimeMonth(T)), число (TimeDay(T)) или день недели (TimeDayOfWeek(T)).
Если из одного значения datetime необходимо выделить несколько полей, то более экономичный способ предполагает во всех вызовах кроме первого использовать аналогичные макросы без параметра и начинающиеся с символа подчеркивания: они читают нужное поле из структуры без повторной установки даты/времени и вызова функции TimeToStruct. Например:
// используем класс DateTime из MQL5Book/DateTime.mqh:
|
В журнале должны появиться такие строки.
EnumToString(DateTime::_DateTime.assign(time[0]).__TimeDayOfWeek())=THURSDAY
|
Встроенная функция EnumToString преобразует элемент любого перечисления в строку и будет описана в отдельном разделе.
datetime StructToTime(MqlDateTime &struct)
Функция StructToTime выполняет конвертацию из структуры MqlDateTime (см. выше описание функции TimeToStruct), содержащей компоненты даты и времени, в значение типа datetime. Поля day_of_week и day_of_year в процессе не участвуют.
Если состояние остальных полей некорректное (соответствует несуществующей или неподдерживаемой дате), функция может вернуть, в зависимости от серьезности проблемы, либо некоторое исправленное значение, либо значение WRONG_VALUE (-1 в представлении типа long). Поэтому следует проверять наличие ошибки по состоянию глобальной переменной _LastError. Успешная конвертация заканчивается с кодом 0. Перед конвертацией следует сбросить возможное ошибочное состояние в _LastError (сохранившееся как артефакт исполнения каких-то предыдущих инструкций) с помощью функции ResetLastError.
Проверка функции StructToTime также приведена в скрипте ConversionTimeStruct.mq5. Массив структур parts конвертируется в datetime в цикле.
MqlDateTime parts[] =
|
Для каждого элемента выводится получившееся значение и код ошибки.
[year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
|
Обратите внимание, что некоторые корректировки функция выполняет, не взводя флаг ошибки. Так, в элементе под номером 2 мы передавали в функцию 30 февраля 2021 года, которое было преобразовано во 2-е марта 2021, и _LastError = 0.