我正在尝试从Lua中的JSON数据解析完整的ISO8601日期时间。 我遇到了匹配模式的问题。
到目前为止,这就是我所拥有的:
-- Example datetime string 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函数中的时区,但至少我知道它需要如何调整。
我考虑在“。”之后删除所有内容。 (毫秒和时区),但那时我真的没有有效的日期时间。毫秒并不是那么重要,我不介意失去它,但时区改变了事情。
注意:有人可能会有更好的代码来执行此操作而且我没有嫁给它,我只需要从日期时间字符串中获得一些有用的东西:)
答案 0 :(得分:10)
单个模式匹配无法完成完整的ISO 8601格式。变化太大了。
YYYYMMDD
vs YYYY-MM-DD
YYYY-MM-DD
和YYYY-MM
都是有效日期YYYY-DDD
,其中DDD
是一年中的某一天(1-365 / 6)hh:mm:ss
,hh:mm
和hh
都是有效时间hhmmss
,hhmm
14:30,5
,1430,5
,14:30.5
或1430.5
均代表14小时30秒半。±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
然后您必须创建parseDate
,parseTime
和parseOffset
。后者应返回UTC的时间偏移量,而前两个则必须考虑压缩格式,时间分数,逗号或点分隔符等内容。
parseDate
可能会在模式匹配开头使用"^"
字符,因为日期必须位于字符串的开头。 parseTime
的模式可能会以"T"
开头。并且parseOffset
将以"$"
结束,因为它们存在时的偏移量即将结束。
“完整ISO”parseOffset
功能可能与此类似:
function parseOffset(str)
if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time
-- matches ±hh:mm, ±hhmm or ±hh; else returns 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。
我希望这会有所帮助。此致!
答案 1 :(得分:1)
还有luadate包,它支持iso8601。 (你可能想要patched version)
答案 2 :(得分:0)
这是一个简单的parseDate
函数,用于ISO日期。请注意,我使用“现在”作为后备。这可能对您不起作用。 YMMV?。
--[[
Parse date given in any of supported forms.
Note! For unrecognised format will return now.
@param str ISO date. Formats:
Y-m-d
Y-m -- this will assume January
Y -- this will assume 1st January
]]
function parseDate(str)
local y, m, d = str:match("(%d%d%d%d)-?(%d?%d?)-?(%d?%d?)$")
-- fallback to now
if y == nil then
return os.time()
end
-- defaults
if m == '' then
m = 1
end
if d == '' then
d = 1
end
-- create time
return os.time{year=y, month=m, day=d, hour=0}
end
--[[
--Tests:
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") ) )
]]