Germany | Finland | Saint Petersburg | Drive

Работа с временем в Lua

Опубликовано в QLua

Давайте сразу определимся. В Lua нет понятия «Календарная дата». Есть понятие «время», которое включает в себя как цифру в календаре, так и показания стрелок будильника. Поэтому в дальнейшем будем оперировать термином «время», подразумевая, что оно включает в себя и день/месяц/год и все остальное.

 

Для хранения времени в Lua 5.1 используются 2 типа данных.

Первый – это таблица с полями

  • year (год, четыре цифры)
  • month (месяц, 1 – 12)
  • day (день, 1 – 31)
  • hour (час, 0 – 23)
  • min (минуты, 0 – 59)
  • sec (секунды, 0 – 61)
  • wday (день недели, воскресенью соответствует 1)
  • yday (день года)
  • isdst (флаг дневного времени суток, тип boolean).

Давайте в дальнейшем такую таблицу именовать как таблицу datetime. Эта таблица может использоваться как в качестве параметра для функций даты/времени языка lua, так и в качестве возвращаемого результата. Взависимости от контекста не все поля требуют заполнения.

Второй формат хранения даты и времени – это так называемый Posix формат времени (или Unix-время). Изобретать определение не смысла, Википедия нам в помощь:

UNIX-время (англ. Unixtime) или POSIX-время — система описания моментов во времени, принятая в UNIX и других POSIX-совместимых операционных системах. Определяется как количество секунд, прошедших с полуночи (00:00:00 UTC1 января 1970 года (четверг); время с этого момента называют «эрой  (эпохой) UNIX» (англ. UnixEpoch).

Время UNIX согласуется с UTC — в частности, при объявлении високосных секунд UTC соответствующие номера секунд повторяются, то есть високосные секунды не учитываются.

Представление времени в виде количества секунд удобно использовать для сравнения и хранения дат (дата и время в этом формате занимают всего 4 или 8 байтов). При необходимости обращения к элементам дат (день, месяц, год) секунды можно преобразовать в любой подходящий формат (и наоборот), но если такие преобразования выполняются часто, они снижают производительность.

 

Я отдельно подчеркну удобство posix формата времени. Мы можем легко прибавлять, вычитать, сравнивать и находить разницу между двумя временными моментами путем простейших арифметических операций.

Теперь о функциях работы со временем, которые нам предоставляет Lua 5.1. Собственно, основных две:

  • os.time ([table]) – преобразует таблицу datetime в posix
  • os.date ([format [, time]]) – преобразует posix в таблицу datetime.

Как видно, они комплементарные. Однако на самом деле все намного интереснее. Давайте остановимся на каждой из них подробнее. В примерах ниже я буду использовать функцию print(). Если у Вас не установлен пакет Lua 5.1, а только терминал QUIK, замените print() на message()

  

datetime в posix

datetime = { year = 2013,
                   month = 09,
                   day = 13,
                   hour = 21,
                   min = 40,
                   sec = 15
                  }
seconds_since_epoch = os.time(datetime)
print(tostring(seconds_since_epoch))

1379094015

 


Надо иметь ввиду, что os.time() выдает не локальное время, а UTC. Это время отличается от локального на величину смещения часового пояса. Пример был выполнен на компьютере с установленным часовым поясом +4 часа (+0400)


Замечу, что поля year, month и day обязательны, а остальные – нет. Если некотрые из них пропущены, то интерпретируются как нулевые.

posix в datetime

seconds_since_epoch  = 1379094015
datetime = os.date("!*t",seconds_since_epoch)
print( "year = "           .. tostring(datetime.year) .. " " ..
        " month = "        .. tostring(datetime.month) .. " " ..
        "day = "             .. tostring(datetime.day) .. " " ..
        "hour = "            .. tostring(datetime.hour) .. " " ..
        "min = "             .. tostring(datetime.min) .. " " ..
        "sec = "              .. tostring(datetime.sec) .. " " ..
        "weekday = "      .. tostring(datetime.wday) .. " " ..
        "day of year = "  .. tostring(datetime.yday) .. " " ..
        " iddst = "          .. tostring(datetime.isdst))

year = 2013 month = 9 day = 13 hour = 17 min = 40 sec = 15 weekday = 6 day of year = 256 iddst = false



Первым параметром os.date() идет строка "!*t". Расшифрую. Если первый параметр начинается с '!', то время форматируется в соответствии с универсальным глобальным временем (по Гринвичу). Далее если следуют символы "*t", то os.date() возвращает таблицу datetime.

 

Получение текущего времени в формате posix

print(os.time())

1379151677


Вызов os.time() без параметров позволяет получить системное время компьютера.

Текущее времени в формат datetime

datetime = os.date("!*t",os.time())
print( tostring(datetime.year) .. " " ..
         tostring(datetime.month) .. " " ..
         tostring(datetime.day) .. " " ..
         tostring(datetime.hour) .. " " ..
         tostring(datetime.min) .. " " ..
         tostring(datetime.sec) .. " " ..
         tostring(datetime.wday) .. " " ..
         tostring(datetime.yday) .. " " ..<
         tostring(datetime.isdst))

2013 9 14 10 28 11 7 257 false

 

Варианты работы os.date()

Собственно, про os.time() сказать больше нечего. А вот у второй функции еще масса возможностей.

Первый параметр функции os.date(), как мы видели выше, должен быть текстовой строкой, которая управляет поведением функции и возвращаемым значением. Вот полный список возможных строк и, соответственно, возвращаемых функцией значений:

%a abbreviated weekday name (e.g., Wed)
%A full weekday name (e.g., Wednesday)
%b abbreviated month name (e.g., Sep)
%B full month name (e.g., September)
%c  date and time (e.g., 09/16/98 23:48:10)
%d day of the month (16) [01-31]
%H hour, using a 24-hour clock (23) [00-23]
%I hour, using a 12-hour clock (11) [01-12]
%M minute (48) [00-59]
%m month (09) [01-12]
%p either "am" or "pm" (pm)
%S second (10) [00-61]
%U sunday week of the year 00 (53)
%w weekday (3) [0-6 = Sunday-Saturday]
%W monday week of the year, from 00 (48)
%x date (e.g., 09/16/98)
%X time (e.g., 23:48:10)
%Y full year (1998)
%y two-digit year (98) [00-99]
%z  timezone string
%% the character %

Какой сегодня месяц?

print(os.date("%B",os.time())

September

В какой день недели родилась моя дочь?

print(os.date("%w",os.time({ year=1997, month = 11, day = 10}))

1

Номер недели в году для 10 ноября 1997 года?

print(os.date("%W",os.time({ year=1997, month = 11, day = 10}) ))
45

Текущая дата строкой

print(os.date("%d.%m.%Y"))

14.09.2013

Текущее время строкой

print(os.date("%X",os.time()))

14:41:35

или

 

print(os.date("Сейчас %H часов %M минут %S секунд",os.time()))

Сейчас 14 часов 41 минут 35 секунд

 

Текущие время и дата строкой

print(os.date())
09/14/2013  22:35:36

Какой день предшествовал 1 марта 2013 года?

datetime = { year = 2013,
month = 3,
day = 1
}
seconds_since_epoch = os.time(datetime) - 24 * 60 * 60
datestr = os.date("%x",seconds_since_epoch )
print(datestr)

02/28/13

Когда будет следующий рабочий день?

В posix можно легко прибавлять и вычитать любые промежутки времени, выраженные в секундах

datetime = os.time()
weekday = os.date("%w",datetime)
print(os.date("Сегодня %d %B  %Y, день недели " .. weekday,datetime))
if weekday == "6"  then                     -- суббота
               diff_time = 2 * 24 * 60 * 60
else
               diff_time = 24 * 60 * 60
end<
print(os.date("Следующий рабочий день %d %B %Y, день недели %w" ,datetime + diff_time))

Сегодня 14 September  2013, день недели 6

Следующий рабочий день 16 September 2013, день недели 1

Как получить название дня недели?

datetime = os.time()
weekday = os.date("%w",datetime)
weekdays = { 
["0"] = "воскресенье",                     ["1"] = "понедельник",                     ["2"] = "вторник",                     ["3"] = "среда",                     ["4"] = "четверг",                     ["5"] = "пятница",                     ["6"] = "суббота"             } print(weekdays[weekday])

 суббота

Сейчас больше 12:44?

epoch_time = os.time()
datetime = os.date("!*t",epoch_time)
datetime.hour = 12
datetime.min = 44
datetime.sec = 0
print(tostring(os.time(datetime) > epoch_time))

Преобразование строки даты в datetime

В терминале Quik строковое представление даты бывает в двух форматах. Любой из них легко преобразуется в формат datetime.


Для формата DD.MM.YYYY:

datetime = {}
datetime.day,datetine.month,datetime.year = string.match("13.09.2013","(%d*)\.(%d*)\.(%d*)")
Для формата YYYYMMDD:

datetime = {}
datetime.year,datetime.month,datetime.day = string.match("20130913","(%d%d%d%d)(%d%d)(%d%d)")

Замечу, что в обоих случаях string.match() выдает текстовые строки, а не числовые значения, которые сохраняются в datetime. Тем не менее os.time() прекрасно это понимает.

Преобразование строки времени в datetime

Строка, представляющая время в формате 21:17:43 преобразуется в формат datetime следующим образом: 
datetime={}
datetime.hour,datetime.min,datetime.sec = string.match("21:17:43","(%d%d)%p(%d%d)%p(%d%d)") 
Замечу, что поля year, month и day должны быть также заполнены.

 

Нужно четко понимать, что вычисления идут в локальном времени Гринвича. Москва находится в другом часовом поясе, который еще и меняется в зависимости от текущего кремлёвского рулевого персонажа.

 См. также:

Комментарии   
# Илья245 21.03.2017 17:14
Спасибо большое! Вы мне очень помогли.

Искал как узнать последний день месяца в Lua и нашел у вас.
Ответить | Ответить с цитатой | Цитировать
# admin 21.03.2017 17:36
"просто мы работаем для вас!" :lol:
Ответить | Ответить с цитатой | Цитировать
# AlexLan 17.06.2017 14:03
Михаил, спасибо за сайт :lol:
Ответить | Ответить с цитатой | Цитировать
# Михаил147 18.05.2017 10:42
isdst - флаг летнего времени.
Ответить | Ответить с цитатой | Цитировать
Добавить комментарий


Архив QLua