Comparación entre RFC 3339 e ISO 8601
(ijmacd.github.io)- Al tratar formatos de fecha y hora, RFC 3339 se acerca más a un subconjunto reducido y fácil de usar en la web e internet, mientras que ISO 8601-1:2019 incluye un conjunto de formatos mucho más amplio
- El alcance de la comparación se limita a ISO 8601-1:2019, y las expresiones adicionales de ISO 8601-2:2019, como estaciones, conjuntos, calificadores de incertidumbre y aritmética de fechas, todavía no están reflejadas en la tabla
- Ambos estándares cubren formatos básicos de fecha y hora ampliamente usados, como
2026-06-26,14:08:00Z,2026-06-26T14:08:00Zy offsets+00:00 - ISO 8601 abarca también siglos, décadas, día ordinal, fecha por semana, horas abreviadas, fracciones con coma, períodos (
P1Y) y rangos (2026-06-26/P1Y), mientras que RFC 3339 los excluye en su mayoría en la tabla - En la notación Date-Time, diferencias como el separador
T, el uso de mayúsculas y minúsculas, y offsets como-00:00terminan definiendo la compatibilidad real entre parsers
Alcance de la comparación y supuestos
- La tabla de formatos no es una lista completa
- El estándar considerado es ISO 8601-1:2019
- Hay diferencias importantes con ediciones anteriores y borradores
- ISO 8601-2:2019 incluye expresiones adicionales, pero todavía no están reflejadas en esta página
- Grupos subanuales, por ejemplo estaciones
- Unidades de agrupación
- Conjuntos
- Calificadores de incertidumbre
- Aritmética de fechas
- RFC 3339 propone que un estándar derivado puede reemplazar
Tpor otro carácter, pero solo da como ejemplo el carácter de espacio - Cada estándar define formatos según su propósito, y los demás formatos no se recomiendan
ISO 8601 es más amplio en la notación de fecha
- Tanto RFC 3339 como ISO 8601 soportan fechas año-mes-día como
2026-06-26 - ISO 8601 cubre más representaciones de fecha que RFC 3339
- Siglo:
20 - Década:
202 - Año:
2026 - Año-mes:
2026-06 - Día ordinal:
2026-177 - Fecha por semana:
2026-W26,2026-W26-5 - Formato básico:
20260626,2026177,2026W26,2026W265
- Siglo:
- En la tabla, RFC 3339 no permite esos formatos de fecha exclusivos de ISO
Diferencias en la notación de hora
- Ambos estándares permiten tiempos a nivel de segundos y con offset de zona horaria como
14:08:00Z,14:08:00+00:00y14:08:00.372+00:00 - RFC 3339 no distingue mayúsculas de minúsculas, por lo que
TyZpueden escribirse comotyz- Ediciones anteriores de ISO 8601 tampoco distinguían mayúsculas de minúsculas
- ISO 8601 permite agregar una fracción al valor de tiempo más pequeño
- La tabla muestra principalmente ejemplos con una sola cifra decimal, pero el estándar permite precisión arbitraria
- Acepta tanto coma como punto como separador decimal, y pueden sustituirse entre sí en todos los formatos
- ISO 8601-1:2019 permite omitir
Ten una expresión de hora sola cuando no haya ambigüedad - ISO 8601 soporta expresiones de hora abreviadas, básicas y con coma decimal como
14,14:08,14:08:00,140800,T14:08:00,14:08:00,372 - RFC 3339 permite offsets
-00:00como14:08:00-00:00, pero en la tabla ISO 8601 no los permite
T y separadores en Date-Time
- Tanto RFC 3339 como ISO 8601 permiten Date-Time como
2026-06-26T14:08:00Zy2026-06-26T14:08:00+00:00 - En la representación Date-Time de ISO 8601,
Tsiempre es obligatoria- En ediciones anteriores también se permitía omitir
Ten Date-Time - Incluso en ediciones anteriores no se permitía insertar caracteres alternativos como espacio o guion bajo
- En ediciones anteriores también se permitía omitir
- En la tabla, RFC 3339 permite las siguientes variantes de Date-Time
tyzen minúscula:2026-06-26t14:08:00z- Separador de espacio:
2026-06-26 14:08:00Z - Separador con guion bajo:
2026-06-26_14:08:00Z - Offset
-00:00:2026-06-26T14:08:00-00:00
- ISO 8601 cubre Date-Time abreviados y basados en día ordinal o fecha por semana, como
2026-06-26T14,2026-06-26T14:08,2026-06-26T14:08:00,2026-177T14:08,2026-W26-5T14:08
Períodos y rangos: foco de ISO 8601
- En la tabla, los formatos de períodos (Periods) solo aparecen marcados para ISO 8601
- Ej.:
P1Y,P1M,P1W,P1D - Con tiempo:
PT1H,PT1M,PT1S - Combinados:
P1Y1M1DT1H1M1S - Con fracción:
P1.5Y,P1,5W,PT1.5S
- Ej.:
- Los formatos de rangos (Ranges) también aparecen marcados solo para ISO 8601
- Fecha y período:
2026-06-26/P1Y - Fecha y fecha:
2026-06-26/2026-06-26 - Período y fecha:
P1Y/2026-06-26 - Date-Time y período:
2026-06-26T14:08/P1DT1H - Rango repetitivo:
R/2026-06-26/P1Y,R10/2026-06-26/P1Y
- Fecha y período:
Claves de formato y herramienta de prueba
- La tabla de formatos usa claves de formato como
%Y,%M,%D,%h,%m,%s%Y: Year%M: Month%D: Day%V: Week Year%W: Week%w: Week Day%O: Ordinal Day%h: Hour%m: Minute%s: Second%u: Microsecond%n: Nanosecond%Z: hora de zona horaria con+o-%z: minuto de zona horaria
- El validador de formatos solo verifica si el formato de entrada corresponde a alguno de los formatos de la tabla
- No verifica todos los formatos posibles
- ISO 8601 as a Service es un servicio en beta para pruebas
- Actualmente solo soporta Date, Time y DateTime
- No soporta Period ni Range
- El código fuente está publicado en GitHub
1 comentarios
Opiniones en Hacker News
Es raro que no haya una forma de especificar una fecha/hora futura en una zona horaria determinada. Por ejemplo, quizá quieras programar una reunión para el 1 de julio de 2030 a las 6 p. m., hora local de Londres, y debería seguir siendo “las 6 p. m. en Londres” sin importar cómo cambien mientras tanto las reglas de la zona horaria del Reino Unido
Actualmente, el Reino Unido usa aproximadamente Z+00:00 de noviembre a marzo y horario de verano Z+01:00 de abril a octubre[0], pero antes de 2030 podría adoptar la Hora de Europa Central[1], volver a intentar el British Double Summer Time[2] o eliminar el horario de verano. Por eso, las mismas “6 p. m.” podrían variar mucho respecto de un epoch específico
Quiero poner en un evento de calendario “6 p. m. según la hora de Londres de ese momento”, pero no hay una forma estándar e interoperable de expresar 2030-07-01 18:00:00 Europe/London
[0] https://en.wikipedia.org/wiki/British_Summer_Time
[1] https://en.wikipedia.org/wiki/Central_European_Time
[2] https://en.wikipedia.org/wiki/British_Summer_Time#Periods_of...
tzentre corchetes después de una cadena RFC 3339 y, para representar la hora local, también hay que incluir una estimación del desfase UTCPor ejemplo,
2030-07-01 18:00:00 Europe/Londonse convierte en2030-07-01T18:00:00+01:00[Europe/London]. Si antes de entonces cambian las reglas del Reino Unido, la marca de tiempo queda “inconsistente” y la aplicación decide cómo manejarla. Sin embargo, si se pone!antes del nombre de la zona horaria entre corchetes, debe detectar el problema en vez de seguir a ciegas el desfase UTCEste formato extendido de marca de tiempo también se usa en la biblioteca propuesta Temporal de JavaScript[1], y la función de parseo
ZonedDateTime.from()[2] permite controlar con la opciónoffsetqué lado priorizar en una marca de tiempo inconsistente. También admite omitir el desfase UTC y usar solo la zona horaria, pero advierte que la hora que se repite durante una transición de horario de verano es ambigua[0] https://www.ietf.org/archive/id/draft-ietf-sedate-datetime-e...
[1] https://tc39.es/proposal-temporal/docs/strings.html#iana-tim...
[2] https://tc39.es/proposal-temporal/docs/ambiguity.html#ambigu...
Pero no es imposible imaginar que, mientras tanto, Escocia vuelva a celebrar un referéndum de independencia y se una a la Hora de Europa Central, o cree la Scottish Standard Time
2023-11-05 01:30:00 America/New_Yorkcorresponde a uno de dos instantes distintosEn calendarios, “la misma hora según el reloj de pared” suele ser el significado deseado, así que tiene sentido, pero hay dificultades de UI para manejar horas extrañas, y quizá convenga incluir en la sintaxis una forma de resolver ambigüedades. Por suerte, estas transiciones normalmente ocurren de madrugada, pero he visto casos así en el trabajo real
Si invitas a alguien de una zona horaria sin horario de verano, la hora se moverá en su calendario, y a veces los colegas internacionales tienen que aceptarlo
[0]: https://www.rfc-editor.org/rfc/rfc5545#section-3.3.5
Los formatos comunes de fecha-hora se crearon para representar un instante concreto, pero en este caso ese instante concreto todavía no existe. Es común que las especificaciones de fecha/hora tengan una estructura no trivial: una reunión el último viernes de cada mes, una reunión mensual que empieza el 31 de enero, dos días antes del cierre de trimestre, etc.
Crear un estándar que cubra todos los casos que la gente pueda imaginar parece algo que se complicaría muy rápido. Si una simple fecha, una hora y un instante no alcanzan, parece que no queda otra que crear una estructura aparte que incluya todos los elementos necesarios
La especificación ISO no está disponible de forma gratuita, así que normalmente conviene seguir el RFC; además, muchas implementaciones open source se basan en borradores, por lo que tampoco puede decirse que sea totalmente amigable con el open source. También es una carga importante para los desarrolladores open source.
Si estás creando algo que maneje fechas futuras, casi siempre vas a querer guardar hora de reloj de pared + ubicación. Lamentablemente, no hay un estándar para esto. En Europa y Estados Unidos las zonas horarias son bastante estables, así que puede que no se note mucho, pero en muchas regiones las zonas horarias cambian con frecuencia, por lo que guardar el offset no es estable.
5 de junio de 2026 13:30, hora de reloj de pared, Paríses lo que la mayoría de la gente quiere decir, y según lo que decida la UE con el horario de verano podría ser UTC+2 o UTC+1. Si es una API que maneja instantes del pasado, basta con usar timestamps POSIX en segundos, milisegundos, microsegundos o nanosegundos.iCal tampoco contempla el problema de que una ubicación sea reasignada a otra zona horaria. Además, un archivo iCal correcto incluye todos los datos de las zonas horarias a las que hace referencia, así que resulta engorroso cuando solo quieres emitir una fecha-hora individual.
[1] https://icalendar.org/iCalendar-RFC-5545/3-3-5-date-time.htm...
2026-06-05T13:30+0200[Europe/Paris]https://www.ietf.org/archive/id/draft-ietf-sedate-datetime-e...
Una parte que los estándares suelen ignorar es la representación de duraciones.
Se puede ver la sección 5.5.4.2, “Representation of time-interval by duration only”, página 21, de http://xml.coverpages.org/ISO-FDIS-8601.pdf. Sería bueno que los parsers JSON de lenguajes estáticos pudieran definir un campo como duración y serializarlo en un formato válido.
Aquí hay un ejemplo de propuesta para Crystal: https://github.com/crystal-lang/crystal/issues/11942
Por ejemplo, 15 días, 5 horas y 20 segundos sería
P15DT5H0M20S, y 7 semanas seríaP7W.durationen el ABNF del apéndice A de RFC 3339.Por ejemplo, si se escribe como
"duration": { "days": 15, "hours": 5, "seconds": 20 }, el parser JSON no necesita entender el significado de los datos; de eso se encarga el validador de entrada. De todos modos, cualquiera sea la forma en que JSON represente estos datos, se necesita un paso de conversión que puede fallar.Es “gracioso” que RFC 3339 e ISO 8601 incluyan muchos formatos de fecha-hora redundantes con objetivos superpuestos, pero que ninguno incluya el formato más usado en todos los sistemas y demasiado obvio:
2023-09-01 15:30:59.Además, ambos estándares son muy poco claros sobre cómo representar fechas antes de la era común y fechas posteriores a
9999-12-31o anteriores a-9999-01-01, y las bibliotecas comunes por lo general no las manejan en absoluto. Incluso cuando lo hacen, el comportamiento de00-01-01queda prácticamente indefinido.El calendario gregoriano es raro porque después de 1 a. C. viene 1 d. C., y salvo el software astronómico especializado, casi todo el software no maneja bien fechas fuera del rango de tiempo Unix del futuro cercano. Bastaría con que el estándar definiera claramente incluso cosas para las que alcanza con guardarlas como string, como los años de nacimiento y muerte del emperador Augusto.
Tpor un espacio”. RFC 3339 hace algo parecido de una forma mucho más verbosa.Tampoco es fácil asegurar que
2023-09-01 15:30:59sea el formato de fecha-hora más usado. El idioma más usado es el chino, y son comunes separadores propios como2023年9月1日.ISO 8601 también permite años anteriores a 1582 o posteriores a 9999 si hay acuerdo mutuo. Si no caben en 4 dígitos, hay que anteponer un único carácter de signo. Esas fechas suelen no estar soportadas porque hay pocas cosas significativas que puedan hacerse con ellas, pero he visto bastantes bibliotecas que las parsean, especialmente implementaciones independientes de C.
00-01-01se define como 1 de enero del año 1 a. C.. ISO 8601 deja claro que la numeración de años sigue el calendario gregoriano proléptico (proleptic Gregorian calendar), por lo que se extrapola hasta el infinito negativo.Entiendo por qué hacía falta un carácter que no fuera espacio, pero al menos se podría haber usado un guion bajo o un punto. Y tampoco entiendo por qué, siendo un string, se sacrificó la universalidad del formato limitando el rango de años a cuatro dígitos.
Los años de 6 dígitos se sienten como una solución a un problema que en realidad no va a ocurrir, solo para parecer futuristas. Es imposible que la tecnología actual o las normas sociales duren 8000 años.
La explicación de que ISO 8601 usa U+2010 HYPHEN y U+2212 MINUS, y que en conjuntos de caracteres que no tengan esos caracteres debe usarse U+2D HYPHEN-MINUS, es incorrecta.
En realidad, ISO 8601 especifica que si el conjunto de caracteres de destino está basado en ISO/IEC 646, en ambos casos debe usarse el carácter hyphen-minus. Eso claramente incluye a Unicode. Hay cierta ambigüedad, pero en Unicode la interpretación es clara, y parece una forma indirecta de garantizar la compatibilidad con otros conjuntos de caracteres basados en 646 al especificar el mapeo normalizado de 646.
Dice que “todos los caracteres usados en representaciones de fecha y hora pertenecen al repertorio ISO/IEC 646, excepto ‘hyphen’, ‘minus’ y ‘plus-minus’. En entornos que usan repertorios de caracteres basados en ISO/IEC 646, tanto ‘hyphen’ como ‘minus’ deben mapearse a ‘hyphen-minus’”.
Como Unicode está basado en ISO 8859, e ISO 8859 está basado en ISO 646, parece correcto entender que la intención es usar U+2D hyphen-minus en el conjunto de caracteres Unicode.
En Windows, como los dos puntos son un carácter especial, suele molestar que no haya una forma compatible con RFC 3339 de poner fecha y hora en nombres de archivo.
Sería bueno que se pudiera cumplir ISO 8601 usando guiones en la fecha pero omitiendo los dos puntos. Por ejemplo,
20230831T1510-0500cumple y puede usarse en un nombre de archivo, pero2023-08-31T1510-0500y variantes similares no. Además, la funciónGet-Datede PowerShell no entiende el primer timestamp sin guiones ni dos puntos.Es una visualización muy buena de este tema.
Para separar la parte de fecha y hora prefiero un espacio o un guion bajo en lugar de
T, pero para evitar problemas con cosas que solo procesan ISO 8601 y por consistencia, en general sigo usandoT.T. No es algo que uno vaya a dividir accidentalmente tomandoTcomo separador.Me dan curiosidad dos cosas. Primero, ¿cuál es la justificación de los años de 6 dígitos? ¿No es cierto que ningún sistema diseñado hoy va a existir en el año 100,000?
Segundo, ISO 8601 está muy difundido, pero ¿RFC 3339 también se usa y se adopta mucho en sistemas reales?
timede Golang yChronode Rust tienen herramientas integradas para manejar RFC 3339, pero no para RFC 8601.Según recuerdo, RFC 8601 tenía problemas de ambigüedad que RFC 3339 no tiene, y entiendo que Python también tuvo problemas para hacer conversiones de ida y vuelta de fechas por eso.
No hay un formato en el que la zona horaria se especifique con cuatro dígitos sin dos puntos, pero
date +%zdevuelve±NNNN.%:z. Relacionado con eso, también se le puede enseñar adatea imprimir así por defecto: https://gist.github.com/d081dad407432d53172e30d0d35c39db$ date2023-08-31T11:15:00-07:00±NNNNes válido sin dos puntos si se usa como parte del “formato básico”. Es decir, no debe haber guiones ni dos puntos en ningún lugar del formato completo.Por lo tanto, los dos siguientes son equivalentes y ambos válidos:
2023-09-01T09:40:01+08:0020230901T094001+0800