Lua ISO 8601 datetime parsing pattern

There is also the luadate package, which supports iso8601. (You probably want the patched version)


The full ISO 8601 format can't be done with a single pattern match. There is too much variation.

Some examples from the wikipedia page:

  • There is a "compressed" format that doesn't separate numbers: YYYYMMDD vs YYYY-MM-DD
  • The day can be omited: YYYY-MM-DD and YYYY-MM are both valid dates
  • The ordinal date is also valid: YYYY-DDD, where DDD is the day of the year (1-365/6)
  • When representing the time, the minutes and seconds can be ommited: hh:mm:ss, hh:mm and hh are all valid times
  • Moreover, time also has a compressed version: hhmmss, hhmm
  • And on top of that, time accepts fractions, using both the dot or the comma to denote fractions of the lower time element in the time section. 14:30,5, 1430,5, 14:30.5, or 1430.5 all represent 14 hours, 30 seconds and a half.
  • Finally, the timezone section is optional. When present, it can be either the letter Z, ±hh:mm, ±hh or ±hhmm.

So, there are lots of possible exceptions to take into account, if you are going to parse according to the full spec. In that case, your initial code might look like this:

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

And then you would have to create parseDate, parseTime and parseOffset. The later should return the time offsets from UTC, while the first two would have to take into account things like compressed formats, time fractions, comma or dot separators, and the like.

parseDate will likely use the "^" character at the beginning of its pattern matches, since the date has to be at the beginning of the string. parseTime's patterns will likely start with "T". And parseOffset's will end with "$", since the time offsets, when they exist, are at the end.

A "full ISO" parseOffset function might look similar to this:

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

By the way, I'm assuming that your computer is working in UTC time. If that's not the case, you will have to include an additional offset on your hours/minutes to account for that.

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

To get your local offset you might want to look at http://lua-users.org/wiki/TimeZone .

I hope this helps. Regards!