2011-10-27 31 views
8

Estoy tratando de analizar una fecha de tiempo ISO8601 completa de datos JSON en Lua. Tengo problemas con el patrón de coincidencia.Lua ISO 8601 patrón de análisis de fecha y hora

Hasta ahora, esto es lo que tengo:

-- 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}) 

estoy atascado en cómo hacer frente a la zona horaria en el patrón porque no hay ninguna lógica o que se encargará de la - o + o ninguno. Aunque sé que lua no es compatible con la zona horaria en la función os.time, al menos sabría cómo se debe ajustar.

He considerado quitar todo después de la "." (milisegundos y zona horaria), pero entonces realmente no tendría una fecha y hora válida. Milisegundos no es tan importante y no me importaría perderlo, pero la zona horaria cambia las cosas.

Nota: Alguien puede tener un código mucho mejor para hacer esto y no estoy casado con ella, sólo tiene que conseguir algo útil de la cadena de fecha y hora :)

+0

La respuesta de BTW kikito es buena; solo una nota: podría ser un buen movimiento aceptar '' (espacio) y 'T' para separar la fecha/hora, porque muchas personas y herramientas escriben fechas de estilo ISO de esa manera (tal vez más que usa 'T'!). – snogglethorpe

+0

Gracias por ese puntero. En mi caso, no necesito preocuparme demasiado porque el patrón es conocido: 2011-10-25T00: 29: 55.503-04: 00 –

Respuesta

10

El formato completo ISO 8601 pueden' se puede hacer con una coincidencia de patrón único. Hay demasiada variación.

Algunos ejemplos: from the wikipedia page

  • hay un formato "comprimido" que hace números no separadas: YYYYMMDD vs YYYY-MM-DD
  • El día puede ser omitido: YYYY-MM-DD y YYYY-MM son ambas fechas válidas
  • La fecha ordinal también es válida: YYYY-DDD, donde DDD es el día del año (1-365/6)
  • Al representar la t ime, los minutos y los segundos se pueden omitir recayendo: hh:mm:ss, hh:mm y hh son todo momento válidos
  • Por otra parte, el tiempo también tiene una versión comprimida: hhmmss, hhmm
  • Y encima de eso, el tiempo acepta fracciones, utilizando tanto el punto o la coma para indicar fracciones del elemento de tiempo más bajo en la sección de tiempo. 14:30,5, 1430,5, 14:30.5, o 1430.5, todos representan 14 horas, 30 segundos y medio.
  • Finalmente, la sección de zona horaria es opcional. Cuando está presente, puede ser la letra Z, ±hh:mm, ±hh o ±hhmm.

Por lo tanto, hay muchas excepciones posibles a tener en cuenta, si va a analizar de acuerdo con la especificación completa. En ese caso, su código inicial podría tener este aspecto:

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 

Y entonces tendría que crear parseDate, parseTime y parseOffset. El último debería devolver las compensaciones de tiempo desde UTC, mientras que las dos primeras tendrían que tener en cuenta cosas como formatos comprimidos, fracciones de tiempo, separadores de coma o de punto, y cosas por el estilo.

parseDate probablemente usará el carácter "^" al comienzo de sus coincidencias de patrón, ya que la fecha debe estar al principio de la cadena.Los patrones de parseTime probablemente comiencen con "T". Y parseOffset terminará con "$", ya que las compensaciones de tiempo, cuando existen, están al final.

A "llena ISO" parseOffset función podría ser similar a esto:

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 

Por cierto, estoy suponiendo que el sistema está funcionando en el tiempo UTC. Si ese no es el caso, tendrá que incluir un desplazamiento adicional en sus horas/minutos para dar cuenta de eso.

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 

Para obtener compensar sus locales es posible que desee mirar a http://lua-users.org/wiki/TimeZone.

Espero que esto ayude. ¡Saludos!

+0

Tengo alguna ventaja porque sé cuál será el formato en el servidor Sin embargo, no quería modificarlo para mejorar la calidad de la suite Lua, porque entonces otros clientes podrían tener problemas. –

+0

La compensación de la zona horaria de la hora es incorrecta. Se debe restar de la hora para obtener la hora correcta en GMT. – CanSpice

+0

@CanSpice ¡Gracias, corregido! – kikito

Cuestiones relacionadas