CatCoding

Lua 时间处理

2017-09-12

我需要用 Lua 处理一个与时间相关的问题,比如我们在配置文件里面配置一个日期 (北京时间),然后在 Openresty 里面判断当前时间是否在这个日期之前或者之后来做对应的逻辑。

Lua 的时间处理还有点麻烦,主要是自带的相关库函数比较少。

os.time() <== 返回当前系统的日历时间,1505181586
os.date() <== 返回本地化的时间字符串,Tue Sep 12 09:59:56 2017
os.clock() <== 返回执行该程序 CPU 花去的时钟秒数,这里是 1156.726

我首先需要一个日期字符串转换为时间戳的函数,找来找去有了这么一个函数,使用正则表达式然后组成表:

function convert_time(time_str)
  -- Assuming a date pattern like: yyyy-mm-dd hh:mm:ss
  -- Assuming timezone is Beijing
  local pattern = "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"
  local year, month, day, hour, minute, seconds = time_str:match(pattern)
  if not (year and month and day and hour and minute and seconds) then
    return nil
  end
  local converted_timestamp =
    os.time({tz = "CST", year = year, month = month,
             day = day, hour = hour, min = minute, sec = seconds})
      
    return converted_timestamp 
end

然后我们可以使用 os.time() 获取当前时间戳来对比。但是必须注意时区问题,Lua 里面要获取当前时区和 UTC 里面的 offset 可以使用一个比较笨拙的办法:

function get_timezone_offset_with_utc()
  local now = os.time()
  return os.difftime(now, os.time(os.date("!*t", now)))
end

使用这个函数获取时区的 offset 之后,对 convert_time 返回的结果做一下偏移即可和 os.time() 做对比。有个问题是上面的函数居然调用了三次系统调用,开销是比较大的。

在 OpenResty 的世界里,不推荐使用这里的标准时间函数,因为这些函数通常会引发不止一个昂贵的系统调用,同时无法为 LuaJIT JIT 编译,对性能造成较大影响。推荐使用 ngx_lua 模块提供的带缓存的时间接口,如 ngx.today, ngx.time, ngx.utctime, ngx.localtime, ngx.now, ngx.http_time,以及 ngx.cookie_time 等。

Penlight库也有很多日期相关的函数封装,不过大多也都使用了 os 相关函数。为了避免多次调用 get_timezone_offset_with_utc我使用了 Kong 里面自带的 cache 相关函数做一下缓存:

-- 缓存上面的时区差,减少系统调用
local offset_with_cst, err =
  cache.get_or_set("timezone_offset", nil, get_timezone_offset_with_utc, nil)

公号同步更新,欢迎关注👻
Tags: Lua