Dados dos rangos de fechas, ¿cuál es la forma más simple o más eficiente de determinar si los dos rangos de fechas se superponen?
Como ejemplo, supongamos que tenemos rangos denotados por las variables DateTime StartDate1
to EndDate1
y StartDate2
to EndDate2
.
datetime
math
language-agnostic
Ian Nelson
fuente
fuente
Respuestas:
(InicioA <= FinB) y (FinA> = InicioB)
Prueba:
Dejar que la condición A signifique que el rango de fecha A completamente después del rango de fecha B
_ |---- DateRange A ------| |---Date Range B -----| _
(verdadero si
StartA > EndB
)Deje que la condición B signifique que el rango de fecha A es completamente anterior al rango de fecha B
|---- DateRange A -----| _ _ |---Date Range B ----|
(verdadero si
EndA < StartB
)Entonces, la superposición existe si ni A ni B son verdaderas
(si un rango no está completamente detrás del otro,
ni completamente antes del otro, entonces deben superponerse).
Ahora una de las leyes de De Morgan dice que:
Not (A Or B)
<=>Not A And Not B
Lo que se traduce en:
(StartA <= EndB) and (EndA >= StartB)
NOTA: Esto incluye condiciones en las que los bordes se superponen exactamente. Si desea excluir eso,
cambie los
>=
operadores a>
, y<=
a<
NOTA 2. Gracias a @Baodad, ver este blog , la superposición real es menos de:
{
endA-startA
,endA - startB
,endB-startA
,endB - startB
}(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
NOTA 3. Gracias a @tomosius, se lee una versión más corta:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
este es en realidad un acceso directo sintáctico para lo que es una implementación más larga, que incluye comprobaciones adicionales para verificar que las fechas de inicio sean anteriores o posteriores a las fechas finales. Derivando esto de arriba:
Si las fechas de inicio y finalización pueden estar fuera de servicio, es decir, si es posible eso
startA > endA
ostartB > endB
, entonces también debe verificar que estén en orden, lo que significa que debe agregar dos reglas de validez adicionales:(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)
o:(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)
o,(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))
o:(Max(StartA, StartB) <= Min(EndA, EndB)
Pero para implementar
Min()
yMax()
, tiene que codificar, (usando C ternary para ser conciso):(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
fuente
Start
yEnd
media. Si tiene dos variables denominadas Superior e Inferior, o Este y Oeste, o HighValue y LoValue, se puede suponer o implicar que algo o alguien, en algún lugar, debe asegurarse de que uno de los pares de valores no esté almacenado en las variables opuestas. -Solo uno de los dos pares porque, bueno, también funcionará si se cambian ambos pares de valores.start
yend
(con la semántica que "null start" = "Desde el principio del tiempo" y "null end" = "Hasta el final del tiempo") así:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Creo que es suficiente decir que los dos rangos se superponen si:
fuente
(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)
notación me parece más fácil de entender, Range1 siempre está a la izquierda en las pruebas.<=
a<
si el inicio es inclusivo y el final es exclusivo.Este artículo Biblioteca de períodos de tiempo para .NET describe la relación de dos períodos de tiempo mediante la enumeración PeriodRelation :
fuente
Para razonar sobre las relaciones temporales (o cualquier otra relación de intervalo, veamos eso), considere el Álgebra de intervalos de Allen . Describe las 13 posibles relaciones que pueden tener dos intervalos entre sí. Puede encontrar otras referencias: "Intervalo Allen" parece ser un término de búsqueda operativa. También puede encontrar información sobre estas operaciones en Desarrollo de aplicaciones orientadas al tiempo de Snodgrass en SQL (PDF disponible en línea en URL), y en Fecha, Darwen y Lorentzos Datos temporales y el Modelo relacional (2002) o Tiempo y teoría relacional: Bases de datos temporales en el Modelo Relacional y SQL (2014; efectivamente la segunda edición de TD&RM).
La corta (más o menos) la respuesta es: dado dos intervalos de fechas
A
yB
con los componentes.start
y.end
y la restricción.start <= .end
, a continuación, dos intervalos se solapan si:Puede ajustar el uso de
>=
vs>
y<=
vs<
para cumplir con sus requisitos de grado de superposición.ErikE comenta:
Creo que no puede contar las dos entradas 'before: before' y 'after: after'. Podría ver 7 entradas si equipara algunas relaciones con sus inversas (vea el diagrama en la URL de Wikipedia referenciada; tiene 7 entradas, 6 de las cuales tienen un inverso diferente, con iguales que no tienen un inverso distinto). Y si tres es sensato depende de sus requisitos.
fuente
Si también se debe calcular la superposición, puede usar la siguiente fórmula:
fuente
¡Todas las soluciones que comprueban una multitud de condiciones en función de dónde están los rangos en relación entre sí se pueden simplificar enormemente simplemente asegurando que un rango específico comience antes! Se asegura de que el primer rango comience antes (o al mismo tiempo) intercambiando los rangos si es necesario por adelantado.
Luego, puede detectar la superposición si el otro inicio del rango es menor o igual que el primer final del rango (si los rangos son inclusivos, que contienen las horas de inicio y finalización) o menor que (si los rangos incluyen el inicio y excluyen el final) .
Suponiendo que sea inclusivo en ambos extremos, solo hay cuatro posibilidades de las cuales una no se superpone:
El punto final del rango 2 no entra en él. Entonces, en pseudocódigo:
Esto podría simplificarse aún más en:
Si los rangos están incluidos en el inicio y exclusivo al final, sólo hay que sustituir
>
con>=
en la segundaif
declaración (el primer segmento de código: en el segundo segmento de código, tendrá que utilizar<
en lugar de<=
):Limita en gran medida el número de comprobaciones que tiene que hacer porque elimina la mitad del espacio del problema antes de tiempo al garantizar que el rango 1 nunca comience después del rango 2.
fuente
Aquí hay otra solución más usando JavaScript. Especialidades de mi solución:
Las pruebas se basan en números enteros, pero como los objetos de fecha en JavaScript son comparables, también puede agregar dos objetos de fecha. O podría agregar la marca de tiempo de milisegundos.
Código:
Pruebas:
Resultado cuando se ejecuta con karma y jazmín y PhantomJS:
fuente
yo lo haría
Donde
IsBetween
es algo comofuente
Aquí está el código que hace la magia:
Dónde..
¿Prueba? Echa un vistazo a este código de consola de prueba .
fuente
Aquí está mi solución en Java , que también funciona en intervalos ilimitados
fuente
!startA.after(endB)
significa startA <= endB y!endA.before(startB)
significa startB <= endA. Estos son los criterios para un intervalo cerrado y no un intervalo abierto.endB == null
ystartA == null
compruebe un intervalo abiertoendB == null
,startA == null
,endA == null
YstartB == null
son todos los criterios para verificar si hay un intervalo acotado y no un intervalo abierto. Ejemplo de las diferencias entre los intervalos ilimitados y abiertos: (10, 20) y (20, nulo) son dos intervalos abiertos que no se superponen. El último tiene un final ilimitado. Su función devolverá verdadero, pero los intervalos no se superponen, porque los intervalos no incluyen 20. (números usados en lugar de marcas de tiempo para simplificar)La solución publicada aquí no funcionó para todos los rangos superpuestos ...
mi solución de trabajo fue:
fuente
Esta fue mi solución de JavaScript con moment.js:
fuente
Una forma fácil de recordar la solución sería
min(ends)>max(starts)
fuente
En Microsoft SQL SERVER - Función SQL
fuente
La forma más simple es usar una biblioteca dedicada bien diseñada para el trabajo de fecha y hora.
java.time y ThreeTen-Extra
Lo mejor en el negocio es el
java.time
marco integrado en Java 8 y versiones posteriores. Agregue a eso el proyecto ThreeTen-Extra que complementa java.time con clases adicionales, específicamente laInterval
clase que necesitamos aquí.En cuanto a la
language-agnostic
etiqueta de esta pregunta, el código fuente de ambos proyectos está disponible para su uso en otros idiomas (tenga en cuenta sus licencias).Interval
La
org.threeten.extra.Interval
clase es útil, pero requiere momentos de fecha y hora (java.time.Instant
objetos) en lugar de valores de solo fecha. Entonces, procedemos utilizando el primer momento del día en UTC para representar la fecha.Crea una
Interval
para representar ese lapso de tiempo.También podemos definir un
Interval
con un momento inicial más unDuration
.Comparar las pruebas de superposiciones es fácil.
Puede comparar una
Interval
con otraInterval
oInstant
:abuts
contains
encloses
equals
isAfter
isBefore
overlaps
Todos estos utilizan el
Half-Open
enfoque para definir un período de tiempo donde el comienzo es inclusivo y el final es exclusivo .fuente
Esta es una extensión de la excelente respuesta de @ charles-bretana.
Sin embargo, la respuesta no distingue entre intervalos abiertos, cerrados y medio abiertos (o medio cerrados).
Caso 1 : A, B son intervalos cerrados
Superposición iff:
(StartA <= EndB) and (EndA >= StartB)
Caso 2 : A, B son intervalos abiertos
Superposición iff:
(StartA < EndB) and (EndA > StartB)
Caso 3 : A, B derecho abierto
Condición de superposición:
(StartA < EndB) and (EndA > StartB)
Caso 4 : A, B dejado abierto
Condición de superposición:
(StartA < EndB) and (EndA > StartB)
Caso 5 : A derecha abierta, B cerrada
Condición de superposición:
(StartA <= EndB) and (EndA > StartB)
etc ...
Finalmente, la condición general para que dos intervalos se superpongan es
(InicioA <🞐 FinB) y (FinA> 🞐 InicioB)
donde 🞐 convierte una desigualdad estricta en una no estricta siempre que se haga la comparación entre dos puntos finales incluidos.
fuente
Respuesta corta usando momentjs :
la respuesta se basa en las respuestas anteriores, pero se acorta.
fuente
En caso de que esté usando un intervalo de fechas que aún no ha finalizado (todavía en curso), por ejemplo, no establecido endDate = '0000-00-00', no puede usar ENTRE porque ¡0000-00-00 no es una fecha válida!
Usé esta solución:
Si startdate2 es más alto que enddate, ¡no hay superposición!
fuente
La respuesta es demasiado simple para mí, así que he creado una instrucción SQL dinámica más genérica que verifica si una persona tiene fechas superpuestas.
fuente
La solución matemática dada por @Bretana es buena pero descuida dos detalles específicos:
Sobre el estado cerrado o abierto de límites de intervalo, la solución de @Bretana válida para intervalos cerrados
puede reescribirse para intervalos medio abiertos para:
Esta corrección es necesaria porque un límite de intervalo abierto no pertenece al rango de valores de un intervalo por definición.
Y sobre intervalos vacíos , bueno, aquí la relación que se muestra arriba NO es válida. Los intervalos vacíos que no contienen ningún valor válido por definición deben manejarse como casos especiales. Lo demuestro con mi biblioteca de tiempo Java Time4J a través de este ejemplo:
El corchete inicial "[" indica un inicio cerrado mientras que el último corchete ")" indica un final abierto.
Como se muestra arriba, los intervalos vacíos violan la condición de superposición anterior (especialmente startA <endB), por lo que Time4J (y otras bibliotecas también) deben manejarlo como un caso especial para garantizar que la superposición de cualquier intervalo arbitrario con un intervalo vacío no existe. Por supuesto, los intervalos de fechas (que están cerrados por defecto en Time4J pero también pueden estar medio abiertos, como los intervalos de fechas vacías) se manejan de manera similar.
fuente
Aquí hay un método genérico que puede ser útil localmente.
fuente
fuente
Usando Java util.Date, aquí lo que hice.
fuente
La forma más fácil de hacerlo en mi opinión sería comparar si EndDate1 es anterior a StartDate2 y EndDate2 es anterior a StartDate1.
Eso, por supuesto, si está considerando intervalos donde StartDate siempre es anterior a EndDate.
fuente
Tuve una situación en la que teníamos fechas en lugar de fechas y las fechas solo podían superponerse al inicio / final. Ejemplo a continuación:
(El verde es el intervalo actual, los bloques azules son intervalos válidos, los rojos son intervalos superpuestos).
Adapte la respuesta de Ian Nelson a la siguiente solución:
Esto coincide con todos los casos de superposición, pero ignora los casos de superposición permitidos.
fuente
Divida el problema en casos y luego maneje cada caso .
La situación 'dos intervalos de fechas se cruzan' está cubierta por dos casos: el primer intervalo de fechas comienza dentro del segundo, o el segundo intervalo de fechas comienza dentro del primero.
fuente
Puedes probar esto:
fuente
Esta fue mi solución, devuelve verdadero cuando los valores no se superponen:
X INICIO 1 Y FINAL 1
A INICIO 2 B FIN 2
fuente
Para ruby también encontré esto:
Lo encontré aquí con una buena explicación -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails
fuente
La siguiente consulta me da los identificadores para los cuales el rango de fechas suministrado (fechas de inicio y finalización se superpone con cualquiera de las fechas (fechas de inicio y finalización) en mi nombre_tabla
fuente