Lua ISO 8601日期时间解析模式

我正在尝试从 Lua 中的 JSON 数据中解析完整的 ISO8601 日期时间。 我对匹配模式感到困惑。

到目前为止,这是我拥有的:

-- 示例日期时间字符串 2011-10-25T00: 29: 55.503-04: 00
local datetime = "2011-10-25T00:29:55.503-04:00"
local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)%.(%d+)"
local xyear, xmonth, xday, xhour, xminute,
        xseconds, xmillies, xoffset = datetime:match(pattern)

local convertedTimestamp = os.time({year = xyear, month = xmonth,
        day = xday, hour = xhour, min = xminute, sec = xseconds})

我卡在了如何处理模式上的时区,因为没有逻辑或可以处理 - 或 + 或 无。 虽然我知道Lua不支持 os.time函数中的时区,但至少我会知道它需要如何进行调整。

我考虑剥离“.”(毫秒和时区)后的所有内容,但那么我就真的没有一个有效的日期时间了。毫秒并不那么重要,我不介意失去它,但时区改变了事情。

注:有人可能有更好的代码来完成这项任务,而我并没有固定的代码,我只需要从日期时间字符串中得到一些有用的东西 :)

原文链接 https://stackoverflow.com/questions/7911322

点赞
stackoverflow用户312586
stackoverflow用户312586

完整的 ISO 8601 格式不能通过一个模式匹配来完成,因为其中变化太多了。

一些例子(来自维基百科):

  • 有一个不分隔数字的“压缩”格式:YYYYMMDDYYYY-MM-DD
  • 日可以省略:YYYY-MM-DDYYYY-MM 都是合法的日期
  • 序数日期也是合法的:YYYY-DDD,其中 DDD 是一年中的日期(1-365/6)
  • 在表示时间时,分钟和秒钟可以省略:hh:mm:sshh:mmhh 都是合法的时间
  • 此外,时间还有一个压缩版:hhmmsshhmm
  • 最后,时间也允许小数,_可以使用点或逗号表示时间段中较低时间元素的小数部分_。14:30,51430,514:30.51430.5 都表示 14 小时、30 分钟和一半。
  • 最后,时区部分是可选的。当存在时,可为字母 Z、±hh:mm、±hh 或 ±hhmm。

因此,如果你要根据完整规范解析日期时间,就需要考虑大量的可能性。在这种情况下,你的初始代码可能是这样的:

function parseDateTime(str)
  local Y,M,D = parseDate(str)
  local h,m,s = parseTime(str)
  local oh,om = parseOffset(str)
  return os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s})
end

然后你需要创建 parseDateparseTimeparseOffset。后者应该返回对于 UTC 的时间偏移量,而前两个则必须考虑到这样的事情,如压缩格式、时间小数、逗号或点分隔符等等。

parseDate 可能会在其模式匹配的开头使用 "^" 字符,因为日期必须在字符串开头。parseTime 的模式可能会以 "T" 开头。而 parseOffset 的模式会以 "$" 结尾,因为时间偏移量在存在时,它们在结尾处。

一个“完整 ISO”parseOffset函数可能看起来像这样:

function parseOffset(str)
  if str:sub(-1)=="Z" then return 0,0 end -- 以 Z 结尾,表示 Zulu 时间

  -- 匹配 ±hh:mm、±hhmm 或 ±hh;否则返回 nils
  local sign, oh, om = str:match("([-+])(%d%d):?(%d?%d?)$")
  sign, oh, om = sign or "+", oh or "00", om or "00"

  return tonumber(sign .. oh), tonumber(sign .. om)
end

顺便说一句,我假设你的计算机正在使用 UTC 时间。如果不是这样的话,你将不得不包括一个额外的偏移量来计算它。

function parseDateTime(str)
  local Y,M,D =   parseDate(str)
  local h,m,s =   parseTime(str)
  local oh,om =   parseOffset(str)
  local loh,lom = getLocalUTCOffset()
  return os.time({year=Y, month=M, day=D, hour=(h+oh-loh), min=(m+om-lom), sec=s})
end

获取你的本地偏移量,你可能需要查看 http://lua-users.org/wiki/TimeZone

希望这能有所帮助。敬礼!

2011-10-27 08:20:17
stackoverflow用户19212
stackoverflow用户19212

还有一个名为luadate的包,支持iso8601格式。(您可能需要修补后的版本

2015-03-27 18:59:16
stackoverflow用户333296
stackoverflow用户333296

这里是一个用于解析 ISO 日期的简单的 parseDate 函数。请注意,我把 "now" 作为备用选项。这可能适用于您,也可能不适用。你最好自己试试。

--[[
    解析给定的日期格式。

    注意!对于无法识别的格式,将返回当前时间。

    @param str ISO 日期格式。可选的格式如下:
        Y-m-d
        Y-m -- 这将假定为 1 月
        Y -- 这将假定为 1 月 1 日
]]
function parseDate(str)
    local y, m, d = str:match("(%d%d%d%d)-?(%d?%d?)-?(%d?%d?)$")
    -- 如果没有匹配到日期,就使用当前时间
    if y == nil then
        return os.time()
    end
    -- 默认值
    if m == '' then
        m = 1
    end
    if d == '' then
        d = 1
    end
    -- 创建时间
    return os.time{year=y, month=m, day=d, hour=0}
end
--[[
--测试代码:
print(  os.date( "%Y-%m-%d", parseDate("2019-12-28") )  )
print(  os.date( "%Y-%m-%d", parseDate("2019-12") )  )
print(  os.date( "%Y-%m-%d", parseDate("2019") )  )
]]
2019-11-25 22:37:45