Dado dois períodos, qual é a maneira mais simples ou mais eficiente de determinar se os dois períodos se sobrepõem?
Como exemplo, suponha que tenhamos intervalos indicados pelas variáveis DateTime StartDate1
para EndDate1
e StartDate2
para EndDate2
.
datetime
math
language-agnostic
Ian Nelson
fonte
fonte
Respostas:
(StartA <= EndB) e (EndA> = StartB)
Prova:
Let ConditionA significa que o DateRange A completamente após o DateRange B
_ |---- DateRange A ------| |---Date Range B -----| _
(verdadeiro se
StartA > EndB
)Let ConditionB significa que o DateRange A é completamente anterior ao DateRange B
|---- DateRange A -----| _ _ |---Date Range B ----|
(True se
EndA < StartB
)Então a Sobreposição existe se Nem A nem B são verdadeiros -
(Se um intervalo não é completamente após o outro,
nem completamente antes do outro, eles devem se sobrepor.)
Agora, uma das leis de De Morgan diz que:
Not (A Or B)
<=>Not A And Not B
Que se traduz em:
(StartA <= EndB) and (EndA >= StartB)
NOTA: Isso inclui condições em que as bordas se sobrepõem exatamente. Se você deseja excluir isso,
altere os
>=
operadores para>
e<=
para<
NOTA 2. Graças à @Baodad, consulte este blogue , a sobreposição real é menos:
{
endA-startA
,endA - startB
,endB-startA
,endB - startB
}(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
NOTA 3. Graças a @tomosius, uma versão mais curta diz:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Na verdade, é um atalho sintático para uma implementação mais longa, que inclui verificações extras para verificar se as datas de início estão em ou antes das datas finais. Derivando isso de cima:
Se as datas de início e fim pode estar fora de ordem, ou seja, se é possível que
startA > endA
oustartB > endB
, então você também tem que verificar se eles estão em ordem, o que significa que você tem que adicionar duas regras de validade adicionais:(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)
ou:(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)
ou,(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))
ou:(Max(StartA, StartB) <= Min(EndA, EndB)
Mas para implementar
Min()
eMax()
, você deve codificar (usando C ternary para terseness):(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
fonte
Start
e oEnd
significado. Se você tiver duas variáveis denominadas Top e Bottom, ou East e West, ou HighValue e LoValue, pode ser assumido ou implícito que algo ou alguém, em algum lugar, deve garantir que um dos pares de valores não seja armazenado nas variáveis opostas. -Só um dos dois pares porque, bem, também funcionará se os dois pares de valores forem alterados.start
eend
(com a semântica que "null start" = "Desde o início dos tempos" e "null end" = "Até o fim dos tempos") assim:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Acredito que basta dizer que as duas faixas se sobrepõem se:
fonte
(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)
notação mais fácil de entender, o Range1 está sempre à esquerda nos testes.<=
para<
se o início é inclusivo e o final é exclusivo.Este artigo Biblioteca de Períodos de Tempo para .NET descreve a relação de dois períodos pela enumeração PeriodRelation :
fonte
Para raciocinar sobre relações temporais (ou quaisquer outras relações de intervalo, chegue a isso), considere a Álgebra de Intervalo de Allen . Descreve as 13 possíveis relações que dois intervalos podem ter um em relação ao outro. Você pode encontrar outras referências - "Allen Interval" parece ser um termo de pesquisa operacional. Você também pode encontrar informações sobre essas operações em Developing Time-Oriented Applications em SQL do Snodgrass (PDF disponível on-line em URL) e em Date, Darwen e Lorentzos Temporal Data and the Relational Model (2002) ou Time and Relational Theory: Temporal Databases in o Modelo Relacional e SQL (2014; efetivamente a segunda edição do TD&RM).
A resposta curta (ish) é: dados dois intervalos de data
A
eB
com componentes.start
e.end
e a restrição.start <= .end
, dois intervalos se sobrepõem se:Você pode ajustar o uso de
>=
vs>
e<=
vs<
para atender aos seus requisitos de grau de sobreposição.ErikE comenta:
Eu acho que você não pode contar as duas entradas 'antes: antes' e 'depois: depois'. Eu poderia ver 7 entradas se você equiparar algumas relações aos seus inversos (veja o diagrama no URL da Wikipedia referenciado; ele tem 7 entradas, 6 das quais têm um inverso diferente, com iguais não tendo um inverso distinto). E se três é sensato depende de suas necessidades.
fonte
Se a sobreposição em si também deve ser calculada, você pode usar a seguinte fórmula:
fonte
Todas as soluções que verificam uma infinidade de condições com base em onde os intervalos estão em relação um ao outro podem ser bastante simplificadas, garantindo apenas que um intervalo específico comece mais cedo! Você garante que o primeiro intervalo inicie mais cedo (ou ao mesmo tempo) trocando os intervalos, se necessário, antecipadamente.
Em seguida, é possível detectar sobreposição se o outro início do intervalo for menor ou igual ao final do primeiro intervalo (se os intervalos forem inclusivos, contendo os horários de início e de término) ou menor que (se os intervalos incluirem o início e o exclusivo) .
Supondo que seja inclusivo nas duas extremidades, existem apenas quatro possibilidades, das quais uma não é sobreposta:
O ponto final do intervalo 2 não entra nele. Então, no pseudo-código:
Isso pode ser simplificado ainda mais em:
Se os intervalos forem inclusivos no início e exclusivos no final, basta substituir
>
por>=
na segundaif
instrução (para o primeiro segmento de código: no segundo segmento de código, você usaria em<
vez de<=
):Você limita bastante o número de verificações que precisa fazer porque remove metade do espaço do problema mais cedo, garantindo que o intervalo 1 nunca seja iniciado após o intervalo 2.
fonte
Aqui está mais uma solução usando JavaScript. Especialidades da minha solução:
Os testes são baseados em números inteiros, mas como os objetos de data no JavaScript são comparáveis, você também pode inserir dois objetos de data. Ou você pode inserir o registro de data e hora em milissegundos.
Código:
Testes:
Resultado quando executado com karma & jasmine & PhantomJS:
fonte
eu faria
Onde
IsBetween
é algo comofonte
Aqui está o código que faz a mágica:
Onde..
Prova? Confira esta essência do código do console de teste .
fonte
Aqui está minha solução em Java , que também funciona em intervalos ilimitados
fonte
!startA.after(endB)
significa startA <= endB e!endA.before(startB)
significa startB <= endA. Estes são os critérios para um intervalo fechado e não para um intervalo aberto.endB == null
estartA == null
verifique se há um intervalo aberto.endB == null
,startA == null
,endA == null
EstartB == null
são todos os critérios para verificar se há um intervalo ilimitado e não um intervalo aberto. Exemplo para as diferenças entre os intervalos ilimitado e aberto: (10, 20) e (20, nulo) são dois intervalos abertos que não se sobrepõem. O último tem um fim ilimitado. Sua função retornará verdadeiro, mas os intervalos não se sobrepõem, porque os intervalos não incluem 20. (números usados em vez de marcas de tempo para simplificar)A solução publicada aqui não funcionou para todos os intervalos sobrepostos ...
minha solução de trabalho foi:
fonte
Esta foi a minha solução javascript com moment.js:
fonte
Uma maneira fácil de lembrar a solução seria
min(ends)>max(starts)
fonte
No Microsoft SQL SERVER - Função SQL
fonte
A maneira mais simples é usar uma biblioteca dedicada bem projetada para o trabalho de data e hora.
java.time e ThreeTen-Extra
O melhor do negócio é a
java.time
estrutura incorporada no Java 8 e posterior. Acrescente a isso o projeto ThreeTen-Extra que complementa java.time com classes adicionais, especificamente aInterval
classe que precisamos aqui.Quanto à
language-agnostic
tag nesta questão, o código fonte dos dois projetos está disponível para uso em outros idiomas (lembre-se de suas licenças).Interval
A
org.threeten.extra.Interval
classe é útil, mas requer momentos (java.time.Instant
objetos) de data e hora em vez de valores somente de data. Portanto, prosseguimos usando o primeiro momento do dia no UTC para representar a data.Crie um
Interval
para representar esse período de tempo.Também podemos definir um
Interval
com um momento inicial mais umDuration
.Comparando para testar sobreposições é fácil.
Você pode comparar um
Interval
contra outroInterval
ouInstant
:abuts
contains
encloses
equals
isAfter
isBefore
overlaps
Todos eles usam a
Half-Open
abordagem para definir um período de tempo em que o começo é inclusivo e o final é exclusivo .fonte
Esta é uma extensão da excelente resposta de @ charles-bretana.
A resposta, no entanto, não faz distinção entre intervalos abertos, fechados e semi-abertos (ou semi-fechados).
Caso 1 : A, B são intervalos fechados
Sobreposição se:
(StartA <= EndB) and (EndA >= StartB)
Caso 2 : A, B são intervalos abertos
Sobreposição se:
(StartA < EndB) and (EndA > StartB)
Caso 3 : A, B aberto à direita
Condição de sobreposição:
(StartA < EndB) and (EndA > StartB)
Caso 4 : A, B deixado em aberto
Condição de sobreposição:
(StartA < EndB) and (EndA > StartB)
Caso 5 : Um direito aberto, B fechado
Condição de sobreposição:
(StartA <= EndB) and (EndA > StartB)
etc ...
Finalmente, a condição geral para dois intervalos se sobreporem é
(StartA <🞐 EndB) e (EndA> 🞐 StartB)
onde 🞐 transforma uma desigualdade estrita em não-estrita sempre que a comparação é feita entre dois pontos de extremidade incluídos.
fonte
Resposta curta usando momentjs :
a resposta é baseada nas respostas acima, mas é encurtada.
fonte
Caso você esteja usando um período que ainda não terminou (ainda em andamento), por exemplo, não defina endDate = '0000-00-00', não é possível usar ENTRE, porque 0000-00-00 não é uma data válida!
Eu usei esta solução:
Se startdate2 for maior, então enddate não haverá sobreposição!
fonte
A resposta é muito simples para mim, por isso criei uma instrução SQL dinâmica mais genérica que verifica se uma pessoa tem datas sobrepostas.
fonte
A solução matemática dada pelo @Bretana é boa, mas negligencia dois detalhes específicos:
Sobre o estado fechado ou aberto dos limites de intervalo, a solução do @Bretana é válida para intervalos fechados
pode ser reescrito por intervalos semi-abertos para:
Essa correção é necessária porque um limite de intervalo aberto não pertence ao intervalo de valores de um intervalo por definição.
E sobre intervalos vazios , bem, aqui a relação mostrada acima NÃO se aplica. Intervalos vazios que não contêm nenhum valor válido por definição devem ser tratados como caso especial. Eu demonstro isso na minha biblioteca de tempo Java Time4J através deste exemplo:
O colchete à esquerda "[" indica um início fechado, enquanto o último colchete ")" indica um fim em aberto.
Como mostrado acima, os intervalos vazios violam a condição de sobreposição acima (especialmente startA <endB), portanto, o Time4J (e outras bibliotecas também) deve lidar com isso como um caso especial de borda para garantir que a sobreposição de qualquer intervalo arbitrário com um intervalo vazio não existe. Obviamente, os intervalos de datas (que são fechados por padrão no Time4J, mas também podem ser semi-abertos, como intervalos de datas vazios) são tratados da mesma maneira.
fonte
Aqui está um método genérico que pode ser útil localmente.
fonte
fonte
Usando Java util.Date, aqui o que eu fiz.
fonte
A maneira mais fácil de fazer isso, na minha opinião, seria comparar se EndDate1 é anterior a StartDate2 e EndDate2 é anterior a StartDate1.
Isso, é claro, se você está considerando intervalos em que StartDate está sempre antes de EndDate.
fonte
Eu tive uma situação em que tínhamos datas em vez de datas e as datas podiam se sobrepor apenas no início / fim. Exemplo abaixo:
(Verde é o intervalo atual, blocos azuis são intervalos válidos, vermelhos são intervalos sobrepostos).
Adaptei a resposta de Ian Nelson à seguinte solução:
Isso corresponde a todos os casos de sobreposição, mas ignora os casos de sobreposição permitidos.
fonte
Divida o problema em casos e lide com cada caso .
A situação 'dois intervalos de datas se cruzam' é coberta por dois casos - o primeiro período começa no segundo ou o segundo período começa no primeiro.
fonte
Você pode tentar isso:
fonte
Esta foi a minha solução, retorna true quando os valores não se sobrepõem:
X INÍCIO 1 E FIM 1
A INÍCIO 2 B FIM 2
fonte
Para ruby, também encontrei o seguinte:
Encontrei aqui com uma boa explicação -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails
fonte
A consulta abaixo fornece os IDs para os quais o período fornecido (datas de início e término se sobrepõe a qualquer uma das datas (datas de início e término) em meu nome_tabela
fonte