¿Existe un estándar para fines de intervalos de tiempo inclusivos / exclusivos?

Me pregunto si hay un medio estándar o “normal” para interpretar los puntos finales de los datos con intervalos de tiempo con respecto a la inclusión / exclusividad del valor que define el punto final. Sin embargo, tenga en cuenta que estoy preguntando cuál es la convención estándar (o la más común) (si la hay), no para una disertación sobre su preferencia personal. Si realmente desea proporcionar una disertación, adjúntela a una referencia al estándar publicado de alguien o a un texto estándar sobre el tema. Los estándares abiertos (que no tengo que pagar para leer) son muy preferidos a menos que sean fundamentalmente defectuosos :).

Por supuesto, hay 4 posibilidades para un intervalo de tiempo de A a B:

  1. (A, B) – Ambos extremos son exclusivos.
  2. [A, B] – Ambos extremos son inclusivos.
  3. [A, B) – El inicio es inclusivo y el final es exclusivo
  4. (A, B] – El inicio es exclusivo y el final es inclusivo

Cada uno de estos tiene características diferentes (como yo lo veo, siéntase libre de señalar más)

La convención [A, B] tendría la propiedad aparentemente inconveniente de que B está contenido dentro del intervalo [A, B] y también [B, C]. Esto es particularmente inconveniente si B está destinado a representar el límite de la medianoche y está tratando de determinar en qué día cae, por ejemplo. Además, esto significa que la duración del intervalo es ligeramente irritante para calcular ya que [A, B] donde A = B debe tener una longitud de 1 y, por lo tanto, la duración de [A, B] es (B – A) + 1

De manera similar, la convención (A, B) tendría la dificultad de que B no caiga dentro de (A, B) ni (B, C) … continuando la analogía con los límites del día, la medianoche no sería parte de ninguno de los dos días. Esto también es lógicamente inconveniente porque [A, B] donde A = B es un intervalo sin sentido con una duración menor que cero, pero la inversión de A y B no lo hace un intervalo válido .

Así que creo que quiero ya sea [A, B) o (A, B] y no puedo averiguar cómo decidir entre ellos.

Entonces, si alguien tiene un enlace a un documento de normas, haga referencia a un texto estándar o similar que aclare la convención que sería genial. Alternativamente, si puede vincular una variedad de documentos de estándares y / o referencias que no están de acuerdo más o menos, entonces solo puedo elegir uno que parezca tener la autoridad suficiente para CMA y terminar con esto :).

Finalmente, trabajaré en Java, por lo que soy particularmente susceptible a las respuestas que funcionan bien en Java.

En el caso general, [A, B) tiene mucho a su favor y no veo ninguna razón por la que lo mismo no sea cierto para los intervalos de tiempo.

Djikstra escribió un buen artículo al respecto. Por qué la numeración debería comenzar en cero, que, a pesar del nombre, trata sobre todo de exactamente esto.

Breve resumen de las ventajas:

  • end - start es igual al número de elementos en la lista
  • El límite superior del intervalo anterior es el límite inferior de la siguiente
  • permite indexar un intervalo comenzando desde 0 con números sin signo [1]

Personalmente, el segundo punto es extremadamente útil para muchos problemas; considere una función recursiva bastante estándar (en pseudo python):

 def foo(start, end): if end - start == 1: # base case else: middle = start + (end - start) / 2 foo(start, middle) foo(middle, end) 

Escribir lo mismo con un límite superior inclusivo introduce muchos errores propensos a apagarse por uno.

[1] Esa es la ventaja en comparación con (A, B] : un intervalo que comienza desde 0 es MUCHO más común que un intervalo que termina en MAX_VAL . Tenga en cuenta que esto también se relaciona con un problema adicional: el uso de dos límites inclusivos significa que podemos denotar una secuencia cuyos la longitud no se puede express con el mismo tamaño.

Proporcionaré lo que escribí para nuestro equipo como respuesta utilizando el enlace de Voo hasta que Voo agregue una respuesta, luego le daré crédito. Esto es lo que decidí para nuestro caso:

Los intervalos de tiempo en nuestras aplicaciones se representarán como un par de tiempos instantáneos con la convención de que la hora de inicio es inclusiva y la hora de finalización es exclusiva. Esta convención es matemáticamente conveniente porque la diferencia de los límites es igual a la longitud del intervalo, y también es numéricamente coherente con la forma en que las matrices y las listas están suscritas en progtwigs java (consulte http://www.cs.utexas.edu /~EWD/ewd08xx/EWD831.PDF ). El resultado práctico de esto es que el intervalo 2012-03-17T00: 00: 00.000Z – 2012-03-18T00: 00: 00.000Z denota la totalidad del Día de San Patricio, y cada fecha que comience con 2012-03-17 será identificado como incluido en el Día de San Patricio, pero no se incluirá el 2012-03-18T00: 00: 00.000Z, y el Día de San Patricio incluirá exactamente 24 * 60 * 60 * 1000 milisegundos.

No puedo decir con certeza, pero dudo que exista una norma o convención. Si incluye o no el inicio o final instantáneo dependerá de su caso de uso, así que considere si son importantes para usted. Si la decisión es arbitraria, elija una, tenga en cuenta que la elección es arbitraria y siga adelante.

En cuanto a lo que se admite en Java, la biblioteca de Joda Time implementa Interval que incluyen la hora de inicio pero no la hora de finalización.

A pesar de que este hilo se centró más en Java, pensé que sería bastante interesante ver otras convenciones adoptadas, especialmente dado que la biblioteca de Python de pandas es omnipresente para el análisis de datos en estos días, y el hecho de que esta página StackOverflow sea una de las principales búsquedas resultados al buscar convenciones sobre la inclusión / exclusividad de rangos de tiempo.

Citando esta página :

Las fechas de inicio y final son estrictamente inclusivas. Por lo tanto, no generará ninguna fecha fuera de esas fechas si se especifica.

Además, no solo genera rangos de fechas. La convención también se adopta cuando se intenta indexar datos de series de tiempo. Aquí hay una prueba simple en marcos de datos con DatetimeIndex

 >>> import pandas as pd >>> pd.__version__ '0.20.2' >>> df = pd.DataFrame(list(range(20))) >>> df.index = pd.date_range(start="2017-07-01", periods=20) >>> df["2017-07-01":"2017-07-05"] 0 2017-07-01 0 2017-07-02 1 2017-07-03 2 2017-07-04 3 2017-07-05 4 

java.time y medio abierto

Las clases java.time que suplantan a las problemáticas clases de fecha y hora heredadas, así como el proyecto Joda-Time, definen un lapso de tiempo utilizando el enfoque semiabierto [) donde el principio es inclusivo mientras que el final es exclusivo .

Para la fecha y hora con una fracción de segundo, esto elimina el problema de intentar capturar el último momento. El último segundo infinitamente divisible debe resolverse, pero varios sistemas utilizan varias granularidades como milisegundos, microsegundos, nanosegundos o algo más. Con Half-Open, un día, por ejemplo, comienza en el primer momento del día y se extiende hasta, pero no incluye, el primer momento del día siguiente. Problema resuelto, no hay necesidad de luchar con el último momento del día y su segundo fraccional.

He llegado a ver los beneficios de utilizar este enfoque de manera consistente en todo mi código de manejo de fecha y hora. Una semana, por ejemplo, a partir de un lunes se ejecuta hasta, pero no incluye, el siguiente lunes. Un mes comienza el 1 y se extiende hasta el primero del mes siguiente, pero no lo incluye, ignorando así el desafío de determinar el número del último día del mes, incluido el 28/29 de febrero.

Otro beneficio del uso constante de Half-Open [) es la aceleración de la carga cognitiva cada vez que tengo que detectar y descifrar y verificar una parte del enfoque del lapso de tiempo del código. En mi propia progtwigción, simplemente busco una mención de Half-Open en un comentario en la parte superior y al instante sé cómo leer ese código.

Un resultado del uso constante de Half-Open es la reducción de la posibilidad de errores en mi código, ya que mi forma de pensar y de escribir son uniformes, sin posibilidad de confundirse con respecto a la exclusiva inclusiva.

Por cierto, tenga en cuenta que Half-Open [) significa evitar la conjunción de BETWEEN SQL, ya que siempre está completamente cerrado [].

En cuanto al pensamiento empresarial de los clientes a los que atiendo, cuando corresponda, trato de convencerlos de que utilicen también Half-Open constantemente. He visto muchas situaciones en las que varias personas de negocios hacían suposiciones incorrectas sobre los períodos de tiempo cubiertos en los informes. El uso constante de Half-Open evita estas ambiguas desafortunadas. Pero si el cliente insiste, tomo nota de esto en mi código y ajusto las entradas / salidas para usar Half-Open dentro de mi propia lógica. Por ejemplo, mi lógica usa una semana de lunes a lunes, pero en un informe, reste un día para mostrar el domingo.

Para incluso más clases que representan períodos de tiempo con el enfoque semiabierto [), consulte el proyecto ThreeTen-Extras para su clase de Interval (un par de objetos Instant ) y la clase LocalDateRange (un par de objetos LocalDate ).


Acerca de java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases sustituyen a las antiguas y problemáticas clases de fecha y hora como java.util.Date , Calendar y SimpleDateFormat .

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busca Stack Overflow para muchos ejemplos y explicaciones. La especificación es JSR 310 .

¿Dónde obtener las clases java.time?

  • Java SE 8 , Java SE 9 y posteriores
    • Incorporado.
    • Parte de la API de Java estándar con una implementación en paquete.
    • Java 9 agrega algunas características menores y correcciones.
  • Java SE 6 y Java SE 7
    • Gran parte de la funcionalidad de java.time está respaldada a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • El proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente) para Android específicamente.
    • Vea Cómo usar ThreeTenABP… .

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí como Interval , YearWeek , YearQuarter , y más .

Acabo de pasar por este mismo proceso de pensamiento y creo que es muy importante que esté estandarizado de alguna manera, o al menos aclarado por medio de este tipo de publicaciones de preguntas y respuestas.

En nuestro caso, los intervalos de fechas en cuestión se utilizan como entradas y salidas a / desde un microservicio; una que, al menos a corto plazo, será llamada por una aplicación monolítica existente (es un proyecto de descomposición monolítica). Por lo tanto, creo que el comentario anterior relacionado con la decisión impulsada por los requisitos del negocio es, en nuestro caso, menos relevante (porque los “usuarios” directos del software que estamos construyendo son personas realmente técnicas). ¡Si estuviéramos manejando la entrada de un datepicker, eso podría ser una historia diferente!

Mi recomendación fue que todas las fechas de inicio son inclusivas y todas las fechas de finalización son exclusivas, por lo que [A, B) en su notación. Esto fue por las siguientes razones:

  1. Anteriormente habíamos acordado que cualquier fecha entrante que contuviera partes de tiempo se rechazaría (incluso si el valor JSON era “2018-01-01T00: 00: 00”) y que generaríamos todas las fechas sin horarios. Por lo tanto, si la fecha de finalización es exclusiva, tan pronto como la cadena se deserialice en el objeto DateTime .NET, quedará un día fuera.

  2. Me gusta la idea de que los intervalos de fechas (que en nuestro caso siempre deberían producir días enteros) siempre se pueden calcular simplemente haciendo dateRange = (endDateExcl – startDateIncl) .TotalDays. No hay necesidad de añadir 1 en todas partes!

  3. Gran parte de la validación comercial realizada por el servicio está comprobando que múltiples rangos de datos están alineados entre sí sin vacíos. Esto es fácil de controlar a simple vista cuando se usa [A, B) porque cada B debe coincidir con la A anterior. Si vamos con [A, B], entonces nosotros (desarrolladores, evaluadores, ingenieros de soporte) nos preguntamos a menudo “¿Cuántos días? es en marzo otra vez? ” (p. ej. [2018-03-01,2018-03-30], [2018-04-01,2018-04-30]) o “¿Tiene 2016 un día bisiesto?” (por ejemplo, [2016-02-01.2016-02-28], [2016-03-01.2016-03-30]).

Solo para agregar, recomiendo encarecidamente a cualquiera, independientemente de la decisión, el sufijo explícito de todos los nombres de atributos, variables, métodos o de otra manera con “Incl” o “Excl” para que quede claro para todos sin tener que buscar documentación.

También hemos recomendado que todas las fechas vengan en formato ISO y que todo lo que tenga una “Z” al final también se rechace (porque entendemos que estamos trabajando en días completos y no queremos una fecha para ser deserializado en un objeto DateTime con una hora rogue (o 23!) debido al horario de verano).

Nota al pie, probablemente hubiera publicado esto como un comentario a la respuesta de Voo, pero me he unido (¡con retraso!) ¡SO y necesito ganarme mis felicitaciones antes de poder hacerlo! 😉

Feliz cita x