3 puntos por GN⁺ 2023-09-01 | 1 comentarios | Compartir por WhatsApp
  • 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:00Z y 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:00 terminan 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 T por 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
  • 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:00 y 14:08:00.372+00:00
  • RFC 3339 no distingue mayúsculas de minúsculas, por lo que T y Z pueden escribirse como t y z
    • 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 T en 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:00 como 14: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:00Z y 2026-06-26T14:08:00+00:00
  • En la representación Date-Time de ISO 8601, T siempre es obligatoria
    • En ediciones anteriores también se permitía omitir T en Date-Time
    • Incluso en ediciones anteriores no se permitía insertar caracteres alternativos como espacio o guion bajo
  • En la tabla, RFC 3339 permite las siguientes variantes de Date-Time
    • t y z en 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
  • 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

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

 
GN⁺ 2023-09-01
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...

    • Hay un borrador de documento para ese tipo de formato: IXDTF (Internet Extended Date/Time Format)[0]. Permite agregar una zona horaria con el nombre tz entre corchetes después de una cadena RFC 3339 y, para representar la hora local, también hay que incluir una estimación del desfase UTC
      Por ejemplo, 2030-07-01 18:00:00 Europe/London se convierte en 2030-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 UTC
      Este 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ón offset qué 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...
    • En realidad, parece que hace falta información más específica que una zona horaria. Por ejemplo, si quieres reunirte no en Londres sino en Glasgow, Escocia, el 1 de julio de 2030 a las 6 p. m., hoy Glasgow está en la zona horaria Europe/London
      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
    • La trampa de este tipo de expresión es que aparecen marcas de tiempo ambiguas o imposibles. 2023-11-05 01:30:00 America/New_York corresponde a uno de dos instantes distintos
      En 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
    • En el mundo de los calendarios, iCal ya lo soporta. Una fecha-hora sin zona horaria significa solo la hora local[0]
      [0]: https://www.rfc-editor.org/rfc/rfc5545#section-3.3.5
    • La información necesaria son tres cosas: fecha, ubicación y hora local. Quizá lo que realmente quieres no sea una zona horaria. Hay que pensar qué hacer si un lugar que no es Londres pasa a otra zona horaria
      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ís es 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.

    • iCalendar es un estándar RFC para esto[1]. Sin embargo, cuando la hora cambia por el horario de verano, algunos instantes tienen dos representaciones y otros no pueden representarse con este formato.
      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...
    • Hay un borrador de estándar llamado IXDTF que extiende RFC 3339 poniendo el nombre de zona horaria IANA entre corchetes: 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ía P7W.

    • En su lugar, también se puede consultar la definición de duration en el ABNF del apéndice A de RFC 3339.
    • No entiendo por qué querrían dejar como string datos que pueden estructurarse.
      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-31 o anteriores a -9999-01-01, y las bibliotecas comunes por lo general no las manejan en absoluto. Incluso cuando lo hacen, el comportamiento de 00-01-01 queda 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.

    • ISO 8601 permite ese formato si hay acuerdo mutuo. “Acuerdo mutuo” suena grandilocuente, pero basta con una restricción simple como “ISO 8601, pero se puede cambiar la T por 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:59 sea el formato de fecha-hora más usado. El idioma más usado es el chino, y son comunes separadores propios como 2023年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-01 se 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.
    • El formato separado por espacios es mucho más legible. Después de cumplir el estándar durante casi 20 años, últimamente empecé a ignorar ambos en favor del mejor formato separado por espacios.
      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.

    • No usamos las computadoras solo para cosas relacionadas con el presente. Por ejemplo, si se ejecuta un cálculo climático muy largo, quizá podrías simplemente ignorar un error causado por la fecha, pero ¿no sería mejor que no ocurriera?
    • Ya sean 5 o 7 dígitos, puede usarse cualquier cantidad de dígitos que ambas partes puedan acordar antes de iniciar la comunicación. En la parte 2 del estándar incluso hay ejemplos de años de 10 dígitos.
    • Los años de 6 dígitos son una solución a un problema que ya existe. Piensa en un geólogo simulando el movimiento de los continentes.
  • 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.

    • El párrafo relacionado está en ISO 8601-1:2019 §3.2.1.
      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-0500 cumple y puede usarse en un nombre de archivo, pero 2023-08-31T1510-0500 y variantes similares no. Además, la función Get-Date de PowerShell no entiende el primer timestamp sin guiones ni dos puntos.

    • Quitar los dos puntos es seguro y no vuelve ambiguas las fechas ni las fechas-hora de RFC 3339. Siempre se pueden restaurar los dos puntos sin pérdida.
    • No sabía que Windows tenía este problema. MacOS también tiene otro problema relacionado con los dos puntos en nombres de archivo. Si dos de los tres sistemas operativos más usados tienen este problema, entonces claramente faltó pensar mejor el diseño.
  • 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 usando T.

    • Prefiero T. No es algo que uno vaya a dividir accidentalmente tomando T como 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?

    • Viendo cuánto tarda uno en desprenderse por completo de tecnologías antiguas, no me sorprendería que en el año 100,000 todavía estén emulando x86 en computadoras cuánticas.
    • Cuando una biblioteca dice ISO 8601, el 90% de las veces en realidad no implementa las partes más oscuras de ISO 8601.
    • El paquete oficial time de Golang y Chrono de 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.
    • Si se trata de herramientas científicas, puede que quieran representar fechas del futuro lejano.
    • Y10k (:
  • No hay un formato en el que la zona horaria se especifique con cuatro dígitos sin dos puntos, pero date +%z devuelve ±NNNN.

    • Por suerte existe %:z. Relacionado con eso, también se le puede enseñar a date a imprimir así por defecto: https://gist.github.com/d081dad407432d53172e30d0d35c39db
      $ date
      2023-08-31T11:15:00-07:00
    • ±NNNN es 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:00
      20230901T094001+0800