Pour deux plages de dates, quel est le moyen le plus simple ou le plus efficace de déterminer si les deux plages se chevauchent?
Par exemple, supposons que nous ayons des plages désignées par les variables DateTime StartDate1
à EndDate1
etStartDate2
à EndDate2
.
(StartA <= EndB) et (EndA> = StartB)
Preuve:
Laisser ConditionA dire que DateRange A complètement après DateRange B_ |---- DateRange A ------| |---Date Range B -----| _
(Vrai si StartA > EndB
)
Laissons à ConditionB le fait que DateRange A est complètement avant DateRange B|---- DateRange A -----| _ _ |---Date Range B ----|
(Vrai si EndA < StartB
)
Alors le chevauchement existe si ni A ni B n'est vrai -
(Si une plage n'est ni complètement après l'autre,
ni complètement avant l'autre, alors ils doivent se chevaucher.)
Maintenant l'une des lois de De Morgan dit que:
Not (A Or B)
<=> Not A And Not B
Ce qui se traduit par: (StartA <= EndB) and (EndA >= StartB)
REMARQUE: cela inclut les conditions dans lesquelles les bords se chevauchent exactement. Si vous souhaitez exclure cela,
change les opérateurs >=
en >
, et <=
en <
NOTE 2. Grâce à @Baodad, voir ce blog , le chevauchement réel est le moindre de:
{endA-startA
, endA - startB
, endB-startA
, endB - startB
}
(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
NOTE 3. Grâce à @tomosius, une version plus courte se lit comme suit:DateRangesOverlap = max(start1, start2) < min(end1, end2)
Il s'agit en fait d'un raccourci syntaxique pour une implémentation plus longue, qui inclut des vérifications supplémentaires pour vérifier que les dates de début sont égales ou antérieures aux dates de fin. En dérivant cela d'en haut:
Si les dates de début et de fin peuvent ne pas fonctionner correctement, c'est-à-dire s'il est possible que startA > endA
ou startB > endB
, vous devez également vérifier qu'elles sont en ordre, ce qui signifie que vous devez en ajouter deux. règles de validité supplémentaires:(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)
Mais pour implémenter Min()
et Max()
, vous devez coder (en utilisant C ternary pour la terseness):(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
Je pense qu'il suffit de dire que les deux plages se chevauchent si:
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
Cet article Bibliothèque de périodes pour .NET décrit la relation de deux périodes par l'énumération PeriodRelation :
// ------------------------------------------------------------------------
public enum PeriodRelation
{
After,
StartTouching,
StartInside,
InsideStartTouching,
EnclosingStartTouching,
Enclosing,
EnclosingEndTouching,
ExactMatch,
Inside,
InsideEndTouching,
EndInside,
EndTouching,
Before,
} // enum PeriodRelation
Pour raisonner à propos des relations temporelles (ou de toute autre relation d'intervalle, y arriver), considérons Algèbre d'intervalle d'Allen . Il décrit les 13 relations possibles que deux intervalles peuvent avoir l'un par rapport à l'autre. Vous pouvez trouver d'autres références - "Allen Interval" semble être un terme de recherche opérationnel. Vous pouvez également trouver des informations sur ces opérations dans Snodgrass Développement d'applications temporelles en SQL (PDF disponible en ligne à l'adresse URL), et dans Date, Darwen et Lorentzos. Données temporelles et modèle relationnel (2002) ou Théorie temporelle et relationnelle: Bases de données temporelles dans le modèle relationnel et SQL (2014; effectivement la deuxième édition de TD & RM).
La réponse courte (ish) est: étant donné deux intervalles de date A
et B
avec des composants .start
et .end
et la contrainte .start <= .end
, deux intervalles se chevauchent si :
A.end >= B.start AND A.start <= B.end
Vous pouvez ajuster l'utilisation de >=
vs >
et <=
vs <
afin de répondre à vos exigences en matière de degré de chevauchement.
ErikE commente:
Vous ne pouvez obtenir que 13 si vous comptez les choses drôles… Je peux avoir "15 relations possibles que deux intervalles peuvent avoir" quand je deviens fou. En comptant judicieusement, je n’en ai que six, et si vous vous moquez de savoir si A ou B vient en premier, j’en ai seulement trois (pas d’intersection, pas de recoupement partiel, un dans l’autre). 15 va comme ceci: [avant: avant, début, intérieur, fin, après], [début: début, intérieur, fin, après], [dans: intérieur, fin, après], [fin: fin, après], [ après: après].
Je pense que vous ne pouvez pas compter les deux entrées 'avant: avant' et 'après: après'. Je pourrais voir 7 entrées si vous assimilez certaines relations à leurs inverses (voir le diagramme dans l'URL référencée de Wikipédia; il comporte 7 entrées, dont 6 ont un inverse différent, avec des égaux n'ayant pas d'inverse distinct). Et si trois est raisonnable dépend de vos besoins.
----------------------|-------A-------|----------------------
|----B1----|
|----B2----|
|----B3----|
|----------B4----------|
|----------------B5----------------|
|----B6----|
----------------------|-------A-------|----------------------
|------B7-------|
|----------B8-----------|
|----B9----|
|----B10-----|
|--------B11--------|
|----B12----|
|----B13----|
----------------------|-------A-------|----------------------
Si le chevauchement lui-même doit également être calculé, vous pouvez utiliser la formule suivante:
overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) {
...
}
Toutes les solutions qui vérifient une multitude de conditions en fonction de la relation entre les plages peuvent être grandement simplifiées par vous assurant simplement qu'une plage spécifique commence plus tôt! Vous vous assurez que la première plage commence plus tôt ( ou en même temps) en échangeant les gammes si nécessaire à l’avance.
Ensuite, vous pouvez détecter un chevauchement si le début de l'autre plage est inférieur ou égal à la fin de la première plage (si les plages sont inclusives, contenant les heures de début et de fin) ou inférieur à (si les plages incluent le début et exclusive de la fin). .
En supposant que cela soit inclus aux deux extrémités, il n'y a que quatre possibilités, dont l'une est un non-chevauchement:
|----------------------| range 1
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 no overlap
Le point final de la plage 2 n'y entre pas. Donc, en pseudo-code:
def doesOverlap (r1, r2):
if r1.s > r2.s:
swap r1, r2
if r2.s > r1.e:
return false
return true
Cela pourrait être simplifié encore plus dans:
def doesOverlap (r1, r2):
if r1.s > r2.s:
swap r1, r2
return r2.s <= r1.e
Si les plages sont inclusives au début et exclusives à la fin, il vous suffit de remplacer >
par >=
dans la deuxième instruction if
(pour le premier segment de code: dans le deuxième code segment, vous utiliseriez <
plutôt que <=
):
|----------------------| range 1
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 no overlap
|---> range 2 no overlap
Vous limitez grandement le nombre de vérifications à effectuer car vous supprimez la moitié de l’espace posant problème en vous assurant que la plage 1 ne commence jamais après la plage 2.
Voici encore une autre solution utilisant JavaScript. Spécialités de ma solution:
Les tests sont basés sur des entiers, mais comme les objets de date en JavaScript sont comparables, vous pouvez également ajouter deux objets de date. Ou vous pouvez ajouter le timestamp milliseconde.
/**
* Compares to comparable objects to find out whether they overlap.
* It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
* A null value is interpreted as infinity
*/
function intervalsOverlap(from1, to1, from2, to2) {
return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}
describe('', function() {
function generateTest(firstRange, secondRange, expected) {
it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function() {
expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected);
});
}
describe('no overlap (touching ends)', function() {
generateTest([10,20], [20,30], false);
generateTest([20,30], [10,20], false);
generateTest([10,20], [20,null], false);
generateTest([20,null], [10,20], false);
generateTest([null,20], [20,30], false);
generateTest([20,30], [null,20], false);
});
describe('do overlap (one end overlaps)', function() {
generateTest([10,20], [19,30], true);
generateTest([19,30], [10,20], true);
generateTest([10,20], [null,30], true);
generateTest([10,20], [19,null], true);
generateTest([null,30], [10,20], true);
generateTest([19,null], [10,20], true);
});
describe('do overlap (one range included in other range)', function() {
generateTest([10,40], [20,30], true);
generateTest([20,30], [10,40], true);
generateTest([10,40], [null,null], true);
generateTest([null,null], [10,40], true);
});
describe('do overlap (both ranges equal)', function() {
generateTest([10,20], [10,20], true);
generateTest([null,20], [null,20], true);
generateTest([10,null], [10,null], true);
generateTest([null,null], [null,null], true);
});
});
Résultat lorsqu'il est exécuté avec karma & jasmine & PhantomJS:
PhantomJS 1.9.8 (Linux): exécution de 20 sur 20 SUCCESS (0.003 sec/0.004 sec)
Je ferais
StartDate1.IsBetween(StartDate2, EndDate2) || EndDate1.IsBetween(StartDate2, EndDate2)
Où IsBetween
est quelque chose comme
public static bool IsBetween(this DateTime value, DateTime left, DateTime right) {
return (value > left && value < right) || (value < left && value > right);
}
Je sais que cela a été étiqueté comme étant indépendant de la langue, mais pour tous ceux qui implémentent en Java: ne réinventez pas la roue et utilisez Joda Time.
Voici le code qui fait la magie:
var isOverlapping = ((A == null || D == null || A <= D)
&& (C == null || B == null || C <= B)
&& (A == null || B == null || A <= B)
&& (C == null || D == null || C <= D));
Où..
Preuve? Découvrez ce test code de la console Gist .
Voici ma solution dans Java, qui fonctionne aussi sur des intervalles non bornés
private Boolean overlap (Timestamp startA, Timestamp endA,
Timestamp startB, Timestamp endB)
{
return (endB == null || startA == null || !startA.after(endB))
&& (endA == null || startB == null || !endA.before(startB));
}
La solution publiée ici ne fonctionnait pas pour toutes les gammes qui se chevauchent ...
---------------------- | ------- A ------- | ----------- ----------- | ---- B1 ---- | | ---- B2 ---- | | - --- B3 ---- | | ---------- B4 ---------- | | ------- --------- B5 ---------------- | | ---- B6 ---- | - -------------------- | ------- A ------- | ------------- --------- | ------ B7 ------- | | ---------- B8 --- -------- | | ---- B9 ---- | | ---- B10 ----- | | - ------ B11 -------- | | ---- B12 ---- | | ---- B13 ---- | ---------------------- | ------- A ------- | -------- --------------
ma solution de travail était:
. - prend en compte la date de début et la date de début, extérieure OU (STARTDATE ENTRE 'date_début' ET 'date_fin') - une seule nécessaire pour la plage extérieure contenant les dates. )
C'était ma solution javascript avec moment.js:
// Current row dates
var dateStart = moment("2014-08-01", "YYYY-MM-DD");
var dateEnd = moment("2014-08-30", "YYYY-MM-DD");
// Check with dates above
var rangeUsedStart = moment("2014-08-02", "YYYY-MM-DD");
var rangeUsedEnd = moment("2014-08-015", "YYYY-MM-DD");
// Range covers other ?
if((dateStart <= rangeUsedStart) && (rangeUsedEnd <= dateEnd)) {
return false;
}
// Range intersects with other start ?
if((dateStart <= rangeUsedStart) && (rangeUsedStart <= dateEnd)) {
return false;
}
// Range intersects with other end ?
if((dateStart <= rangeUsedEnd) && (rangeUsedEnd <= dateEnd)) {
return false;
}
// All good
return true;
le plus simple
Le moyen le plus simple consiste à utiliser une bibliothèque dédiée bien conçue pour le travail date-heure.
_someInterval.overlaps( anotherInterval )
_
Le meilleur du marché est le framework Java.time
intégré à Java 8 et versions ultérieures. Ajoutez à cela le projet ThreeTen-Extra qui complète Java.time avec des classes supplémentaires, en particulier la classe Interval
dont nous avons besoin ici.
En ce qui concerne la balise _language-agnostic
_ de cette Question, le code source des deux projets est disponible pour une utilisation dans d'autres langues (en tenant compte de leurs licences).
Interval
La classe org.threeten.extra.Interval
est pratique, mais nécessite des instants date-heure (objets _Java.time.Instant
_) plutôt que des valeurs de date uniquement. Nous procédons donc en utilisant le premier moment de la journée en UTC pour représenter la date.
_Instant start = Instant.parse( "2016-01-01T00:00:00Z" );
Instant stop = Instant.parse( "2016-02-01T00:00:00Z" );
_
Créez un Interval
pour représenter cette période.
_Interval interval_A = Interval.of( start , stop );
_
Nous pouvons également définir un Interval
avec un moment de départ plus un Duration
.
_Instant start_B = Instant.parse( "2016-01-03T00:00:00Z" );
Interval interval_B = Interval.of( start_B , Duration.of( 3 , ChronoUnit.DAYS ) );
_
Comparer pour tester les chevauchements est facile.
_Boolean overlaps = interval_A.overlaps( interval_B );
_
Vous pouvez comparer un Interval
à un autre Interval
ou Instant
:
Tous utilisent l’approche _Half-Open
_ pour définir une période de temps où le début est inclus et la fin est exclusif .
La réponse est trop simple pour moi. J'ai donc créé une instruction SQL dynamique plus générique qui vérifie si une personne a des dates qui se chevauchent.
SELECT DISTINCT T1.EmpID
FROM Table1 T1
INNER JOIN Table2 T2 ON T1.EmpID = T2.EmpID
AND T1.JobID <> T2.JobID
AND (
(T1.DateFrom >= T2.DateFrom AND T1.dateFrom <= T2.DateTo)
OR (T1.DateTo >= T2.DateFrom AND T1.DateTo <= T2.DateTo)
OR (T1.DateFrom < T2.DateFrom AND T1.DateTo IS NULL)
)
AND NOT (T1.DateFrom = T2.DateFrom)
Si vous utilisez une plage de dates qui n'est pas encore terminée (par exemple, par exemple). not set endDate = '0000-00-00' vous ne pouvez pas utiliser BETWEEN car 0000-00-00 n'est pas une date valide!
J'ai utilisé cette solution:
(Startdate BETWEEN '".$startdate2."' AND '".$enddate2."') //overlap: starts between start2/end2
OR (Startdate < '".$startdate2."'
AND (enddate = '0000-00-00' OR enddate >= '".$startdate2."')
) //overlap: starts before start2 and enddate not set 0000-00-00 (still on going) or if enddate is set but higher then startdate2
Si startdate2 est supérieur à enddate, il n'y a pas de chevauchement!
Ceci est une extension du excellente réponse de @ charles-bretana.
La réponse ne fait cependant pas de distinction entre les intervalles ouverts, fermés et semi-ouverts (ou semi-fermés).
Cas 1 : A, B sont des intervalles fermés
A = [StartA, EndA]
B = [StartB, EndB]
[---- DateRange A ------] (True if StartA > EndB)
[--- Date Range B -----]
[---- DateRange A -----] (True if EndA < StartB)
[--- Date Range B ----]
Chevauchement ssi: (StartA <= EndB) and (EndA >= StartB)
Cas 2 : A, B sont des intervalles ouverts
A = (StartA, EndA)
B = (StartB, EndB)
(---- DateRange A ------) (True if StartA >= EndB)
(--- Date Range B -----)
(---- DateRange A -----) (True if EndA <= StartB)
(--- Date Range B ----)
Chevauchement ssi: (StartA < EndB) and (EndA > StartB)
Cas 3 : A, B à droite
A = [StartA, EndA)
B = [StartB, EndB)
[---- DateRange A ------) (True if StartA >= EndB)
[--- Date Range B -----)
[---- DateRange A -----) (True if EndA <= StartB)
[--- Date Range B ----)
État de chevauchement: (StartA < EndB) and (EndA > StartB)
Cas 4 : A, B laissés ouverts
A = (StartA, EndA]
B = (StartB, EndB]
(---- DateRange A ------] (True if StartA >= EndB)
(--- Date Range B -----]
(---- DateRange A -----] (True if EndA <= StartB)
(--- Date Range B ----]
État de chevauchement: (StartA < EndB) and (EndA > StartB)
Cas 5 : A ouvert, B fermé
A = [StartA, EndA)
B = [StartB, EndB]
[---- DateRange A ------) (True if StartA > EndB)
[--- Date Range B -----]
[---- DateRange A -----) (True if EndA <= StartB)
[--- Date Range B ----]
État de chevauchement: (StartA <= EndB) and (EndA > StartB)
etc ...
Enfin, la condition générale de chevauchement de deux intervalles est
(StartA <???? EndB) et (EndA> ???? StartB)
où ???? transforme une inégalité stricte en une non-stricte chaque fois que la comparaison est faite entre deux paramètres inclus.
Dans Microsoft SQL SERVER - Fonction SQL
CREATE FUNCTION IsOverlapDates
(
@startDate1 as datetime,
@endDate1 as datetime,
@startDate2 as datetime,
@endDate2 as datetime
)
RETURNS int
AS
BEGIN
DECLARE @Overlap as int
SET @Overlap = (SELECT CASE WHEN (
(@startDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and end date outer
OR
(@endDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and start date outer
OR
(@startDate2 BETWEEN @startDate1 AND @endDate1) -- only one needed for outer range where dates are inside.
) THEN 1 ELSE 0 END
)
RETURN @Overlap
END
GO
--Execution of the above code
DECLARE @startDate1 as datetime
DECLARE @endDate1 as datetime
DECLARE @startDate2 as datetime
DECLARE @endDate2 as datetime
DECLARE @Overlap as int
SET @startDate1 = '2014-06-01 01:00:00'
SET @endDate1 = '2014-06-01 02:00:00'
SET @startDate2 = '2014-06-01 01:00:00'
SET @endDate2 = '2014-06-01 01:30:00'
SET @Overlap = [dbo].[IsOverlapDates] (@startDate1, @endDate1, @startDate2, @endDate2)
SELECT Overlap = @Overlap
Une solution facile à retenir seraitmin(ends)>max(starts)
La meilleure façon de le faire, à mon avis, serait de comparer si EndDate1 est avant StartDate2 et EndDate2 est avant StartDate1.
Cela bien sûr si vous envisagez des intervalles où StartDate est toujours avant EndDate.
En utilisant Java util.Date, voici ce que j'ai fait.
public static boolean checkTimeOverlaps(Date startDate1, Date endDate1, Date startDate2, Date endDate2)
{
if (startDate1 == null || endDate1 == null || startDate2 == null || endDate2 == null)
return false;
if ((startDate1.getTime() <= endDate2.getTime()) && (startDate2.getTime() <= endDate1.getTime()))
return true;
return false;
}
La solution mathématique donnée par @Bretana est bonne mais néglige deux détails spécifiques:
À propos de l'état fermé ou ouvert des limites d'intervalle, la solution de @Bretana est valide pour les intervalles fermés
(StartA <= EndB) et (EndA> = StartB)
peut être réécrit pour des intervalles semi-ouverts en:
(StartA <EndB) et (EndA> StartB)
Cette correction est nécessaire car une limite d'intervalle ouvert n'appartient pas à la plage de valeurs d'un intervalle par définition.
Et à propos de intervalles vides , eh bien, la relation ci-dessus ne tient pas. Les intervalles vides qui, par définition, ne contiennent aucune valeur valide doivent être traités comme des cas particuliers. Je le démontre par ma bibliothèque de temps Java Time4J via cet exemple:
MomentInterval a = MomentInterval.between(Instant.now(), Instant.now().plusSeconds(2));
MomentInterval b = a.collapse(); // make b an empty interval out of a
System.out.println(a); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:13,909000000Z)
System.out.println(b); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:11,909000000Z)
Le crochet principal "[" indique un début fermé tandis que le dernier crochet ")" indique une extrémité ouverte.
System.out.println(
"startA < endB: " + a.getStartAsInstant().isBefore(b.getEndAsInstant())); // false
System.out.println(
"endA > startB: " + a.getEndAsInstant().isAfter(b.getStartAsInstant())); // true
System.out.println("a overlaps b: " + a.intersects(b)); // a overlaps b: false
Comme indiqué ci-dessus, les intervalles vides violent la condition de chevauchement ci-dessus (en particulier startA <endB), donc Time4J (ainsi que d'autres bibliothèques) doit le gérer comme un cas Edge spécial afin de garantir le chevauchement de tout intervalle arbitraire avec un intervalle vide. n'existe pas. Bien entendu, les intervalles de date (qui sont fermés par défaut dans Time4J mais peuvent être semi-ouverts, comme les intervalles de date vides) sont traités de la même manière.
Voici une méthode générique qui peut être utile localement.
// Takes a list and returns all records that have overlapping time ranges.
public static IEnumerable<T> GetOverlappedTimes<T>(IEnumerable<T> list, Func<T, bool> filter, Func<T,DateTime> start, Func<T, DateTime> end)
{
// Selects all records that match filter() on left side and returns all records on right side that overlap.
var overlap = from t1 in list
where filter(t1)
from t2 in list
where !object.Equals(t1, t2) // Don't match the same record on right side.
let in1 = start(t1)
let out1 = end(t1)
let in2 = start(t2)
let out2 = end(t2)
where in1 <= out2 && out1 >= in2
let totover = GetMins(in1, out1, in2, out2)
select t2;
return overlap;
}
public static void TestOverlap()
{
var tl1 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 1:00pm".ToDate(), Out = "1/1/08 4:00pm".ToDate() };
var tl2 = new TempTimeEntry() { ID = 2, Name = "John", In = "1/1/08 5:00pm".ToDate(), Out = "1/1/08 6:00pm".ToDate() };
var tl3 = new TempTimeEntry() { ID = 3, Name = "Lisa", In = "1/1/08 7:00pm".ToDate(), Out = "1/1/08 9:00pm".ToDate() };
var tl4 = new TempTimeEntry() { ID = 4, Name = "Joe", In = "1/1/08 3:00pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() };
var tl5 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 8:01pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() };
var list = new List<TempTimeEntry>() { tl1, tl2, tl3, tl4, tl5 };
var overlap = GetOverlappedTimes(list, (TempTimeEntry t1)=>t1.ID==1, (TempTimeEntry tIn) => tIn.In, (TempTimeEntry tOut) => tOut.Out);
Console.WriteLine("\nRecords overlap:");
foreach (var tl in overlap)
Console.WriteLine("Name:{0} T1In:{1} T1Out:{2}", tl.Name, tl.In, tl.Out);
Console.WriteLine("Done");
/* Output:
Records overlap:
Name:Joe T1In:1/1/2008 3:00:00 PM T1Out:1/1/2008 8:00:00 PM
Name:Lisa T1In:1/1/2008 7:00:00 PM T1Out:1/1/2008 9:00:00 PM
Done
*/
}
Voici encore une autre réponse abrégée utilisant momentjs:
function isOverlapping(startDate1, endDate1, startDate2, endDate2){
return moment(startDate1).isSameOrBefore(endDate2) &&
moment(startDate2).isSameOrBefore(endDate1);
}
la réponse est basée sur les réponses ci-dessus, mais est raccourcie.
J'ai eu une situation où nous avions des dates au lieu de dates-heures, et les dates peuvent se chevaucher seulement au début/à la fin. Exemple ci-dessous:
(Le vert est l'intervalle actuel, les blocs bleus sont des intervalles valides, les rouges les intervalles qui se chevauchent).
J'ai adapté la réponse de Ian Nelson à la solution suivante:
(startB <= startA && endB > startA)
|| (startB >= startA && startB < endA)
Cela correspond à tous les cas de chevauchement mais ignore les cas de chevauchement autorisés.
public static class NumberExtensionMethods
{
public static Boolean IsBetween(this Int64 value, Int64 Min, Int64 Max)
{
if (value >= Min && value <= Max) return true;
else return false;
}
public static Boolean IsBetween(this DateTime value, DateTime Min, DateTime Max)
{
Int64 numricValue = value.Ticks;
Int64 numericStartDate = Min.Ticks;
Int64 numericEndDate = Max.Ticks;
if (numricValue.IsBetween(numericStartDate, numericEndDate) )
{
return true;
}
return false;
}
}
public static Boolean IsOverlap(DateTime startDate1, DateTime endDate1, DateTime startDate2, DateTime endDate2)
{
Int64 numericStartDate1 = startDate1.Ticks;
Int64 numericEndDate1 = endDate1.Ticks;
Int64 numericStartDate2 = startDate2.Ticks;
Int64 numericEndDate2 = endDate2.Ticks;
if (numericStartDate2.IsBetween(numericStartDate1, numericEndDate1) ||
numericEndDate2.IsBetween(numericStartDate1, numericEndDate1) ||
numericStartDate1.IsBetween(numericStartDate2, numericEndDate2) ||
numericEndDate1.IsBetween(numericStartDate2, numericEndDate2))
{
return true;
}
return false;
}
if (IsOverlap(startdate1, enddate1, startdate2, enddate2))
{
Console.WriteLine("IsOverlap");
}
Pour Ruby j'ai aussi trouvé ceci:
class Interval < ActiveRecord::Base
validates_presence_of :start_date, :end_date
# Check if a given interval overlaps this interval
def overlaps?(other)
(start_date - other.end_date) * (other.start_date - end_date) >= 0
end
# Return a scope for all interval overlapping the given interval, including the given interval itself
named_scope :overlapping, lambda { |interval| {
:conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
}}
end
Je l'ai trouvé ici avec une explication intéressante -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-Ruby-or-Rails
if (StartDate1 > StartDate2) swap(StartDate, EndDate);
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1);
Divisez le problème en cas puis gérez chaque cas.
La situation "intersection de deux plages de dates" est couverte par deux cas: la première plage de dates commence dans la seconde ou la deuxième plage de dates commence dans la première.
Si vous fournissez une plage de dates en tant qu'entrée et souhaitez savoir si elle chevauche la plage de dates existante dans la base de données, les conditions suivantes peuvent répondre à votre demande.
Supposons que vous fournissiez un
@StartDate
et@EndDate
à partir de votre entrée de formulaire.
les conditions sont:
Si @StartDate
est en avance sur existingStartDate
et derrière existingEndDat
e, alors nous pouvons dire que @StartDate
se trouve au milieu d'une plage de dates existante, nous pouvons donc en conclure qu'il va se chevaucher.
@StartDate >=existing.StartDate And @StartDate <= existing.EndDate)
Si @StartDate
est derrière existingStartDate
mais @EndDate
est en avance sur existingStartDate
on peut dire qu'il va se chevaucher
(@StartDate <= existing.StartDate And @EndDate >= existing.StartDate)
Si @StartDate
est en retard existingStartDate
et @EndDate
en avance sur existingEndDate
, nous pouvons en conclure que la plage de dates fournie dévore une plage de dates existante et se chevauche.
(@StartDate <= existing.StartDate And @EndDate >= existing.EndDate))
Si l'une des conditions est vraie, la plage de dates fournie chevauche celle qui existe dans la base de données.
La requête ci-dessous me donne les identifiants pour lesquels la plage de dates fournie (dates de début et de fin se chevauche avec l'une des dates (dates de début et de fin) de mon nom de table
select id from table_name where (START_DT_TM >= 'END_DATE_TIME' OR
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
Solution facile:
compare the two dates:
A = the one with smaller start date, B = the one with bigger start date
if(A.end < B.start)
return false
return true
Vous pouvez essayer ceci:
//custom date for example
$d1 = new DateTime("2012-07-08");
$d2 = new DateTime("2012-07-11");
$d3 = new DateTime("2012-07-08");
$d4 = new DateTime("2012-07-15");
//create a date period object
$interval = new DateInterval('P1D');
$daterange = iterator_to_array(new DatePeriod($d1, $interval, $d2));
$daterange1 = iterator_to_array(new DatePeriod($d3, $interval, $d4));
array_map(function($v) use ($daterange1) { if(in_array($v, $daterange1)) print "Bingo!";}, $daterange);
C'était ma solution, elle retourne true lorsque les valeurs ne se chevauchent pas:
X DÉBUT 1 Y FIN 1
A START 2 B END 2
TEST1: (X <= A || X >= B)
&&
TEST2: (Y >= B || Y <= A)
&&
TEST3: (X >= B || Y <= A)
X-------------Y
A-----B
TEST1: TRUE
TEST2: TRUE
TEST3: FALSE
RESULT: FALSE
---------------------------------------
X---Y
A---B
TEST1: TRUE
TEST2: TRUE
TEST3: TRUE
RESULT: TRUE
---------------------------------------
X---Y
A---B
TEST1: TRUE
TEST2: TRUE
TEST3: TRUE
RESULT: TRUE
---------------------------------------
X----Y
A---------------B
TEST1: FALSE
TEST2: FALSE
TEST3: FALSE
RESULT: FALSE