gob/datetime/range.go

226 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package datetime
import (
"fmt"
. "gitea.zaclys.com/bvaudour/gob/option"
)
// TimeComparator représente tout type dindication 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
String() string
IsNil() bool
}
// 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
}
}
return out
}
// 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
}
}
return out
}
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; +∞[.
type Range[C TimeComparator[C]] struct {
begin, end C
}
// NewRange retourne une période entre begin et end.
// Si begin < end, le programme panique.
func NewRange[C TimeComparator[C]](begin, end C) Range[C] {
if !begin.IsNil() && !end.IsNil() && begin.Gt(end) {
panic("begin should be located in past of end.")
}
return Range[C]{begin, end}
}
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) }
// Begin retourne le début de la période.
func (r Range[C]) Begin() C { return r.begin }
// End retourne la fin de la période.
func (r Range[C]) End() C { return r.end }
// Before retourne vrai si e est située avant la période.
func (r Range[C]) Before(e C) bool { return r.bB(e, true) }
// After retourne vrai si e est située après la période.
func (r Range[C]) After(e C) bool { return r.aE(e, true) }
// Contains retourne vrai si e ∈ [begin;end].
func (r Range[C]) Contains(e C) bool { return r.aB(e) && r.bE(e) }
// ContainsStrictLeft retourne vrai si e ∈ ]begin;end].
func (r Range[C]) ContainsStrictLeft(e C) bool { return r.aB(e, true) && r.bE(e) }
// ContainsStrictRight retourne vrai si e ∈ [begin;end[.
func (r Range[C]) ContainsStrictRight(e C) bool { return r.aB(e) && r.bE(e, true) }
// ContainsStrict retourne vrai si e ∈ ]begin;end[.
func (r Range[C]) ContainsStrict(e C) bool { return r.aB(e, true) && r.bE(e, true) }
// IsNow retourne vrai si la période est en cours.
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()) }
// IsFuture retourne vrai la période est située dans le futur.
func (r Range[C]) IsFuture() bool { return r.After(r.begin.Now()) }
// BeforeRange retourne vrai si r1 est terminée avant que r2 commence.
func (r1 Range[C]) BeforeRange(r2 Range[C]) bool { return !r1.end.IsNil() && r2.bB(r1.end) }
// AfterRange retourne vrai si r2 est terminée avec que r1 commence.
func (r1 Range[C]) AfterRange(r2 Range[C]) bool { return !r1.begin.IsNil() && r2.aE(r1.begin) }
// ContainsRange retourne vrai si r2 est intégralement comprise dans r1.
func (r1 Range[C]) ContainsRange(r2 Range[C]) bool {
return ((r2.begin.IsNil() && r1.begin.IsNil()) || r1.aB(r2.begin)) && ((r2.end.IsNil() && r1.end.IsNil()) || r1.bE(r2.end))
}
// InRange retourne vrai si r2 comprend intégralement r1.
func (r1 Range[C]) InRange(r2 Range[C]) bool { return r2.ContainsRange(r1) }
// Excludes retourne vrai si r1 et r2 ne se chevauchent pas.
func (r1 Range[C]) Excludes(r2 Range[C]) bool {
return (!r1.end.IsNil() && r2.bB(r1.end)) || (!r1.begin.IsNil() && r2.aE(r1.begin))
}
// Overlaps retourne vrai si r1 et r2 se chevauchent.
func (r1 Range[C]) Overlaps(r2 Range[C]) bool {
return (r1.begin.IsNil() || r2.bE(r1.begin, true)) && (r1.end.IsNil() || r2.aB(r1.end, true))
}
// 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) {
result = Some(NewRange(maxB(r1.begin, r2.begin), minE(r1.end, r2.end)))
}
return
}
// 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]]) {
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)))
}
return
}
// Diff retourne la liste des périodes qui ne se chevauchent pas.
func (r1 Range[C]) Diff(r2 Range[C]) (result []Range[C]) {
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)
}
result = append(result, NewRange(b1, e))
}
if e1.Ne(e2) {
b := e1
if !b2.IsNil() {
b = Max(b2, e1)
}
result = append(result, NewRange(b, e2))
}
return
}
// 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)
}