2023-10-31 10:18:00 +00:00
|
|
|
|
package datetime
|
|
|
|
|
|
|
|
|
|
import (
|
2023-11-04 10:39:54 +00:00
|
|
|
|
"fmt"
|
|
|
|
|
|
2023-10-31 10:18:00 +00:00
|
|
|
|
. "gitea.zaclys.com/bvaudour/gob/option"
|
|
|
|
|
)
|
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// TimeComparator représente tout type d’indication de temps.
|
|
|
|
|
type TimeComparator[E any] interface {
|
|
|
|
|
Eq(E) bool
|
|
|
|
|
Ne(E) bool
|
|
|
|
|
Gt(E) bool
|
|
|
|
|
Ge(E) bool
|
|
|
|
|
Lt(E) bool
|
|
|
|
|
Le(E) bool
|
|
|
|
|
IsNow() bool
|
|
|
|
|
IsPast() bool
|
|
|
|
|
IsFuture() bool
|
|
|
|
|
Now() E
|
2023-11-04 10:39:54 +00:00
|
|
|
|
String() string
|
|
|
|
|
IsNil() bool
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Min retourne le temps le plus petit.
|
|
|
|
|
func Min[C TimeComparator[C]](e C, args ...C) C {
|
|
|
|
|
out := e
|
|
|
|
|
for _, a := range args {
|
|
|
|
|
if a.Lt(out) {
|
|
|
|
|
out = a
|
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
2023-11-03 19:33:26 +00:00
|
|
|
|
return out
|
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Max retourne le temps le plus grand.
|
|
|
|
|
func Max[C TimeComparator[C]](e C, args ...C) C {
|
|
|
|
|
out := e
|
|
|
|
|
for _, a := range args {
|
|
|
|
|
if a.Gt(out) {
|
|
|
|
|
out = a
|
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
2023-11-03 19:33:26 +00:00
|
|
|
|
return out
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func minB[C TimeComparator[C]](e1, e2 C) C {
|
|
|
|
|
if e1.IsNil() {
|
|
|
|
|
return e1
|
|
|
|
|
} else if e2.IsNil() {
|
|
|
|
|
return e2
|
|
|
|
|
}
|
|
|
|
|
return Min(e1, e2)
|
|
|
|
|
}
|
|
|
|
|
func maxB[C TimeComparator[C]](e1, e2 C) C {
|
|
|
|
|
if e1.IsNil() {
|
|
|
|
|
return e2
|
|
|
|
|
} else if e2.IsNil() {
|
|
|
|
|
return e1
|
|
|
|
|
}
|
|
|
|
|
return Max(e1, e2)
|
|
|
|
|
}
|
|
|
|
|
func minE[C TimeComparator[C]](e1, e2 C) C {
|
|
|
|
|
if e1.IsNil() {
|
|
|
|
|
return e2
|
|
|
|
|
} else if e2.IsNil() {
|
|
|
|
|
return e1
|
|
|
|
|
}
|
|
|
|
|
return Min(e1, e2)
|
|
|
|
|
}
|
|
|
|
|
func maxE[C TimeComparator[C]](e1, e2 C) C {
|
|
|
|
|
if e1.IsNil() {
|
|
|
|
|
return e1
|
|
|
|
|
} else if e2.IsNil() {
|
|
|
|
|
return e2
|
|
|
|
|
}
|
|
|
|
|
return Max(e1, e2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Range représente une période entre deux bornes temporelles begin et end.
|
|
|
|
|
// Si begin est nul, cela représente ]-∞ ; end].
|
|
|
|
|
// Se end est nul, cela représent [begin; +∞[.
|
2023-11-03 19:33:26 +00:00
|
|
|
|
type Range[C TimeComparator[C]] struct {
|
|
|
|
|
begin, end C
|
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// NewRange retourne une période entre begin et end.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
// Si begin < end, le programme panique.
|
2023-11-03 19:33:26 +00:00
|
|
|
|
func NewRange[C TimeComparator[C]](begin, end C) Range[C] {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
if !begin.IsNil() && !end.IsNil() && begin.Gt(end) {
|
|
|
|
|
panic("begin should be located in past of end.")
|
2023-11-03 19:33:26 +00:00
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
return Range[C]{begin, end}
|
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func gt[C TimeComparator[C]](e C, strict ...bool) func(C) bool {
|
|
|
|
|
if len(strict) == 0 || !strict[0] {
|
|
|
|
|
return e.Ge
|
|
|
|
|
}
|
|
|
|
|
return e.Ge
|
|
|
|
|
}
|
|
|
|
|
func lt[C TimeComparator[C]](e C, strict ...bool) func(C) bool {
|
|
|
|
|
if len(strict) == 0 || !strict[0] {
|
|
|
|
|
return e.Le
|
|
|
|
|
}
|
|
|
|
|
return e.Le
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r Range[C]) bE(e C, strict ...bool) bool { return r.end.IsNil() || lt(e, strict...)(r.end) }
|
|
|
|
|
func (r Range[C]) aE(e C, strict ...bool) bool { return !r.end.IsNil() && gt(e, strict...)(r.end) }
|
|
|
|
|
func (r Range[C]) bB(e C, strict ...bool) bool { return !r.begin.IsNil() || lt(e, strict...)(r.begin) }
|
|
|
|
|
func (r Range[C]) aB(e C, strict ...bool) bool { return r.begin.IsNil() || gt(e, strict...)(r.begin) }
|
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Begin retourne le début de la période.
|
|
|
|
|
func (r Range[C]) Begin() C { return r.begin }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// End retourne la fin de la période.
|
|
|
|
|
func (r Range[C]) End() C { return r.end }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Before retourne vrai si e est située avant la période.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) Before(e C) bool { return r.bB(e, true) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// After retourne vrai si e est située après la période.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) After(e C) bool { return r.aE(e, true) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Contains retourne vrai si e ∈ [begin;end].
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) Contains(e C) bool { return r.aB(e) && r.bE(e) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// ContainsStrictLeft retourne vrai si e ∈ ]begin;end].
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) ContainsStrictLeft(e C) bool { return r.aB(e, true) && r.bE(e) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// ContainsStrictRight retourne vrai si e ∈ [begin;end[.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) ContainsStrictRight(e C) bool { return r.aB(e) && r.bE(e, true) }
|
2023-11-03 19:33:26 +00:00
|
|
|
|
|
|
|
|
|
// ContainsStrict retourne vrai si e ∈ ]begin;end[.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r Range[C]) ContainsStrict(e C) bool { return r.aB(e, true) && r.bE(e, true) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
|
|
|
|
// IsNow retourne vrai si la période est en cours.
|
2023-11-03 19:33:26 +00:00
|
|
|
|
func (r Range[C]) IsNow() bool { return r.Contains(r.begin.Now()) }
|
|
|
|
|
|
|
|
|
|
// IsPast retourne vrai si la période est située dans le passé.
|
|
|
|
|
func (r Range[C]) IsPast() bool { return r.Before(r.begin.Now()) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// IsFuture retourne vrai la période est située dans le futur.
|
|
|
|
|
func (r Range[C]) IsFuture() bool { return r.After(r.begin.Now()) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// BeforeRange retourne vrai si r1 est terminée avant que r2 commence.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r1 Range[C]) BeforeRange(r2 Range[C]) bool { return !r1.end.IsNil() && r2.bB(r1.end) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// AfterRange retourne vrai si r2 est terminée avec que r1 commence.
|
2023-11-04 10:39:54 +00:00
|
|
|
|
func (r1 Range[C]) AfterRange(r2 Range[C]) bool { return !r1.begin.IsNil() && r2.aE(r1.begin) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// ContainsRange retourne vrai si r2 est intégralement comprise dans r1.
|
|
|
|
|
func (r1 Range[C]) ContainsRange(r2 Range[C]) bool {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
return ((r2.begin.IsNil() && r1.begin.IsNil()) || r1.aB(r2.begin)) && ((r2.end.IsNil() && r1.end.IsNil()) || r1.bE(r2.end))
|
2023-11-03 19:33:26 +00:00
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// InRange retourne vrai si r2 comprend intégralement r1.
|
|
|
|
|
func (r1 Range[C]) InRange(r2 Range[C]) bool { return r2.ContainsRange(r1) }
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Excludes retourne vrai si r1 et r2 ne se chevauchent pas.
|
|
|
|
|
func (r1 Range[C]) Excludes(r2 Range[C]) bool {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
return (!r1.end.IsNil() && r2.bB(r1.end)) || (!r1.begin.IsNil() && r2.aE(r1.begin))
|
2023-11-03 19:33:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Overlaps retourne vrai si r1 et r2 se chevauchent.
|
|
|
|
|
func (r1 Range[C]) Overlaps(r2 Range[C]) bool {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
return (r1.begin.IsNil() || r2.bE(r1.begin, true)) && (r1.end.IsNil() || r2.aB(r1.end, true))
|
2023-11-03 19:33:26 +00:00
|
|
|
|
}
|
2023-10-31 10:18:00 +00:00
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Intersection retourne la période commune entre deux périodes.
|
|
|
|
|
func (r1 Range[C]) Intersection(r2 Range[C]) (result Option[Range[C]]) {
|
|
|
|
|
if r1.Overlaps(r2) {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
result = Some(NewRange(maxB(r1.begin, r2.begin), minE(r1.end, r2.end)))
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Joins retourne la plus grande période contiguë formée par deux périodes.
|
|
|
|
|
func (r1 Range[C]) Joins(r2 Range[C]) (result Option[Range[C]]) {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
if (r1.begin.IsNil() || r2.bE(r1.begin)) && (r1.end.IsNil() || r2.aB(r1.end)) {
|
|
|
|
|
result = Some(NewRange(minB(r1.begin, r2.begin), maxE(r1.end, r2.end)))
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 19:33:26 +00:00
|
|
|
|
// Diff retourne la liste des périodes qui ne se chevauchent pas.
|
|
|
|
|
func (r1 Range[C]) Diff(r2 Range[C]) (result []Range[C]) {
|
2023-11-04 10:39:54 +00:00
|
|
|
|
b1, b2 := minB(r1.begin, r2.begin), maxB(r1.begin, r2.begin)
|
|
|
|
|
e1, e2 := minE(r1.end, r2.end), maxE(r1.end, r2.end)
|
|
|
|
|
|
|
|
|
|
if b1.Ne(b2) {
|
|
|
|
|
e := b2
|
|
|
|
|
if !e1.IsNil() {
|
|
|
|
|
e = Min(b2, e1)
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
2023-11-04 10:39:54 +00:00
|
|
|
|
result = append(result, NewRange(b1, e))
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-04 10:39:54 +00:00
|
|
|
|
if e1.Ne(e2) {
|
|
|
|
|
b := e1
|
|
|
|
|
if !b2.IsNil() {
|
|
|
|
|
b = Max(b2, e1)
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
2023-11-04 10:39:54 +00:00
|
|
|
|
result = append(result, NewRange(b, e2))
|
2023-10-31 10:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-11-04 10:39:54 +00:00
|
|
|
|
|
|
|
|
|
// String retourne la représentation textuelle de la période.
|
|
|
|
|
func (r Range[C]) String() string {
|
|
|
|
|
b, e := r.begin.String(), r.end.String()
|
|
|
|
|
if r.begin.IsNil() {
|
|
|
|
|
b = "-∞"
|
|
|
|
|
}
|
|
|
|
|
if r.end.IsNil() {
|
|
|
|
|
e = "+∞"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("[%s ; %s]", b, e)
|
|
|
|
|
}
|