440 lines
14 KiB
Go
440 lines
14 KiB
Go
package datetime
|
||
|
||
import (
|
||
"time"
|
||
)
|
||
|
||
type clock uint
|
||
|
||
// Precision représente la précision d’une horloge.
|
||
// Par exemple :
|
||
// - 15:06 est une horloge précise à la minute,
|
||
// - 15:06:02 est précise à la seconde,
|
||
// - 15:06:02.257 est précise à la milliseconde.
|
||
//
|
||
// La précision est utilisée dans Clock pour les comparaisons :
|
||
// les comparaisons entre deux horloges se font à la précision
|
||
// le l’horloge la moins précise.
|
||
type Precision uint
|
||
|
||
func newC0(h, i uint, e ...uint) clock {
|
||
h, i, s, v, p := formatC(h, i, e...)
|
||
return newC(ms(h, i, s, v), p)
|
||
}
|
||
func newC(n uint, p Precision) clock {
|
||
switch p {
|
||
case PrecisionMillisecond:
|
||
case PrecisionSecond:
|
||
n -= n % MillisecondPerSecond
|
||
case PrecisionMinute:
|
||
n -= n % MillisecondPerMinute
|
||
default:
|
||
return clockNil
|
||
}
|
||
if n > maxClock {
|
||
return clockNil
|
||
}
|
||
return clock(n<<bitsPrecision) | clock(p&maskPrecision)
|
||
}
|
||
func (c clock) p() Precision { return Precision(c & maskPrecision) }
|
||
func (c clock) valid() bool { return c.p() > NoPrecision && c.dv() <= maxClock }
|
||
|
||
func (c clock) dv() uint { return uint(c >> bitsPrecision) }
|
||
func (c clock) ds() uint { return c.dv() / MillisecondPerSecond }
|
||
func (c clock) di() uint { return c.dv() / MillisecondPerMinute }
|
||
|
||
func (c clock) v() uint { return c.dv() % MillisecondPerSecond }
|
||
func (c clock) s() uint { return c.ds() % SecondPerMinute }
|
||
func (c clock) i() uint { return c.di() % MinutePerHour }
|
||
func (c clock) h() uint { return c.dv() / MillisecondPerHour }
|
||
func (c clock) t(l *time.Location) (t time.Time) {
|
||
if !c.valid() {
|
||
return
|
||
}
|
||
return fromT(c.h(), c.i(), c.s(), c.v(), l)
|
||
}
|
||
func (c clock) dst(l *time.Location) bool { return c.t(l).IsDST() }
|
||
func (c clock) ts(l *time.Location) int64 { return c.t(l).Unix() }
|
||
func (c clock) tv(l *time.Location) int64 { return c.t(l).UnixMilli() }
|
||
|
||
func (c clock) _setV(e, v uint, p Precision) (v2 uint, p2 Precision) {
|
||
if e >= MillisecondPerSecond || p == NoPrecision {
|
||
return
|
||
}
|
||
return v + e - c.v(), PrecisionMillisecond
|
||
}
|
||
func (c clock) _setS(e, v uint, p Precision) (v2 uint, p2 Precision) {
|
||
if e >= SecondPerMinute || p == NoPrecision {
|
||
return
|
||
}
|
||
return v + e*MillisecondPerSecond - c.s()*MillisecondPerSecond, min(p, PrecisionSecond)
|
||
}
|
||
func (c clock) _setI(e, v uint, p Precision) (v2 uint, p2 Precision) {
|
||
if e >= MinutePerHour || p == NoPrecision {
|
||
return
|
||
}
|
||
return v + e*MillisecondPerMinute - c.i()*MillisecondPerMinute, min(p, PrecisionMinute)
|
||
}
|
||
func (c clock) _setH(e, v uint, p Precision) (v2 uint, p2 Precision) {
|
||
if e >= HourPerDay || p == NoPrecision {
|
||
return
|
||
}
|
||
return v + e*MillisecondPerHour - c.h()*MillisecondPerHour, min(p, PrecisionMinute)
|
||
}
|
||
|
||
func (c clock) setV(v uint) clock {
|
||
v, p := c._setV(v, c.dv(), c.p())
|
||
return newC(v, p)
|
||
}
|
||
func (c clock) setS(s uint, e ...uint) clock {
|
||
v, p := c._setS(s, c.dv(), c.p())
|
||
if len(e) > 0 {
|
||
v, p = c._setV(e[0], v, p)
|
||
}
|
||
return newC(v, p)
|
||
}
|
||
func (c clock) setI(i uint, e ...uint) clock {
|
||
v, p := c._setI(i, c.dv(), c.p())
|
||
if len(e) > 0 {
|
||
i, p = c._setS(e[0], v, p)
|
||
if len(e) > 1 {
|
||
i, p = c._setV(e[1], v, p)
|
||
}
|
||
}
|
||
return newC(v, p)
|
||
}
|
||
func (c clock) setH(h uint, e ...uint) clock {
|
||
v, p := c._setH(h, c.dv(), c.p())
|
||
if len(e) > 0 {
|
||
v, p = c._setI(e[0], v, p)
|
||
if len(e) > 1 {
|
||
v, p = c._setS(e[1], v, p)
|
||
if len(e) > 2 {
|
||
v, p = c._setV(e[2], v, p)
|
||
}
|
||
}
|
||
}
|
||
return newC(v, p)
|
||
}
|
||
func (c clock) setP(p Precision) clock { return newC(c.dv(), p) }
|
||
|
||
func (c clock) add(h, i int, e ...int) clock {
|
||
if !c.valid() {
|
||
return c
|
||
}
|
||
r, v, p := h*MillisecondPerHour+i*MillisecondPerMinute, c.dv(), c.p()
|
||
if len(e) > 0 {
|
||
r, p = r+e[0]*MillisecondPerSecond, min(p, PrecisionSecond)
|
||
if len(e) > 1 {
|
||
r, p = r+e[1], PrecisionMillisecond
|
||
}
|
||
}
|
||
switch {
|
||
case r >= 0:
|
||
v += uint(r)
|
||
case int(v)+r >= 0:
|
||
v -= uint(abs(r))
|
||
default:
|
||
return clockNil
|
||
}
|
||
|
||
return newC(v, p)
|
||
}
|
||
func (c clock) addH(h int) clock { return c.add(h, 0) }
|
||
func (c clock) addI(i int) clock { return c.add(0, i) }
|
||
func (c clock) addS(s int) clock { return c.add(0, 0, s) }
|
||
func (c clock) addV(n int) clock { return c.add(0, 0, 0, n) }
|
||
|
||
func (c clock) addDD(d Duration) clock {
|
||
e, u := d.Duration()
|
||
switch u {
|
||
case Millisecond:
|
||
return c.add(0, 0, 0, e)
|
||
case Second:
|
||
return c.add(0, 0, e)
|
||
case Minute:
|
||
return c.add(0, e)
|
||
case Hour:
|
||
return c.add(e, 0)
|
||
default:
|
||
return c
|
||
}
|
||
}
|
||
|
||
func (c clock) bD() clock { return newC(minClock, c.p()) }
|
||
func (c clock) eD() clock { return newC(maxClock, c.p()) }
|
||
func (c clock) bH() clock { return c.setI(0, beginPrecisionS[c.p()]...) }
|
||
func (c clock) eH() clock { return c.setI(59, endPrecisionS[c.p()]...) }
|
||
func (c clock) bI() clock { return c.setS(0, beginPrecisionM[c.p()]...) }
|
||
func (c clock) eI() clock { return c.setS(59, endPrecisionM[c.p()]...) }
|
||
func (c clock) bS() clock { return c.setV(0) }
|
||
func (c clock) eS() clock { return c.setV(999) }
|
||
|
||
func (c1 clock) cmp(c2 clock) int {
|
||
if !c1.valid() {
|
||
if !c2.valid() {
|
||
return 0
|
||
}
|
||
return -1
|
||
}
|
||
if !c2.valid() {
|
||
return 1
|
||
}
|
||
p := max(c1.p(), c2.p())
|
||
c1, c2 = c1.setP(p), c2.setP(p)
|
||
return cmp(c1.dv(), c2.dv())
|
||
}
|
||
|
||
func (c1 clock) subV(c2 clock) int { return int(c1.dv()) - int(c2.dv()) }
|
||
func (c1 clock) subS(c2 clock) int { return c1.subV(c2) / MillisecondPerSecond }
|
||
func (c1 clock) subI(c2 clock) int { return c1.subV(c2) / MillisecondPerMinute }
|
||
func (c1 Clock) subH(c2 clock) int { return c1.subV(c2) / MillisecondPerHour }
|
||
|
||
func (c clock) f(f string, l *time.Location) string {
|
||
if !c.valid() {
|
||
return "-"
|
||
}
|
||
return formatT(c.t(l), f)
|
||
}
|
||
func (c clock) str(l *time.Location) string { return c.f(formatP[c.p()], l) }
|
||
|
||
// Clock représente une indication de temps dans la journée.
|
||
type Clock struct {
|
||
clock
|
||
location *time.Location
|
||
}
|
||
|
||
// ClockNil retourne une horloge nulle.
|
||
func ClockNil() (c Clock) { return }
|
||
|
||
func newClock(c clock, l *time.Location) Clock {
|
||
if c.valid() {
|
||
return Clock{
|
||
clock: c,
|
||
location: l,
|
||
}
|
||
}
|
||
|
||
return ClockNil()
|
||
}
|
||
func setClock(v uint, p Precision, l *time.Location) Clock {
|
||
return newClock(newC(v, p), l)
|
||
}
|
||
|
||
// NewClock retourne une horloge à l’heure h et la minute i dans le fuseau horaire par défaut.
|
||
// La précision est calculée automatiquement suivant que la seconde et la milliseconde sont également
|
||
// présentes dans args.
|
||
func NewClock(h uint, i uint, args ...uint) Clock { return newClock(newC0(h, i, args...), DefaultTZ) }
|
||
|
||
// NewClockTZ agit comme NewClock mais dans le fuseau horaire tz.
|
||
// tz peut être mis sous la forme "Europe/Paris", "CET", "Local", etc.
|
||
func NewClockTZ(tz string, h uint, i uint, args ...uint) Clock {
|
||
c := NewClock(h, i, args...)
|
||
if c.valid() {
|
||
c.location = timezone(tz)
|
||
}
|
||
|
||
return c
|
||
}
|
||
|
||
// NewClockFromTime retourne l’heure à partir d’une date Go t.
|
||
// Si la précision p n’est pas définie, c’est une précision à la ms.
|
||
func NewClockFromTime(t time.Time, p ...Precision) Clock {
|
||
var (
|
||
pp = PrecisionMillisecond
|
||
h, i, s = t.Clock()
|
||
v = t.Nanosecond() / int(time.Millisecond)
|
||
l = t.Location()
|
||
)
|
||
|
||
if len(p) > 0 {
|
||
pp = p[0]
|
||
}
|
||
|
||
return setClock(ms(uint(h), uint(i), uint(s), uint(v)), pp, l)
|
||
}
|
||
|
||
// ClockNow retourne l’heure actuelle avec une précision p. Si tz est renseignée,
|
||
// l’heure est placée dans le fuseau horaire indiqué.
|
||
func ClockNow(p Precision, tz ...string) Clock {
|
||
return NewClockFromTime(now(tz...), p)
|
||
}
|
||
|
||
// Now retourne l’heure actuelle avec la même précision et le même fuseau horaire que c.
|
||
func (c Clock) Now() Clock { return ClockNow(c.p(), c.location.String()) }
|
||
|
||
// ClockGuess retourne l’heure à partir de e et à la précision p en essayant de deviner le format.
|
||
func ClockGuess(p Precision, e string, tz ...string) Clock {
|
||
if t, ok := guess(e, tz...).Get(); ok {
|
||
return NewClockFromTime(t, p)
|
||
}
|
||
|
||
return ClockNil()
|
||
}
|
||
|
||
// ClockParse retourne l’heure à partir de e et à la précision p en spécifiant le format f.
|
||
func ClockParse(p Precision, e, f string, tz ...string) Clock {
|
||
if t, ok := parse(e, f).Get(); ok {
|
||
return NewClockFromTime(t, p)
|
||
}
|
||
|
||
return ClockNil()
|
||
}
|
||
|
||
// Precision retourne la précision de l’horloge.
|
||
func (c Clock) Precision() Precision { return c.p() }
|
||
|
||
// IsNil retourne vrai si l’horloge est nulle.
|
||
func (c Clock) IsNil() bool { return !c.valid() }
|
||
|
||
// DayMilli retourne le nombre de millisecondes écoulées dans le jour.
|
||
func (c Clock) DayMilli() uint { return c.dv() }
|
||
|
||
// DaySecond retourne le nombre de secondes écoulées dans le jour.
|
||
func (c Clock) DaySecond() uint { return c.ds() }
|
||
|
||
// DayMinute retourne le nombre de minutes écoulées dans le jour.
|
||
func (c Clock) DayMinute() uint { return c.di() }
|
||
|
||
// Milli retourne la milliseconde dans la seconde.
|
||
func (c Clock) Milli() uint { return c.v() }
|
||
|
||
// Second retourne la seconde de la minute.
|
||
func (c Clock) Second() uint { return c.s() }
|
||
|
||
// Minute retourne la minute de l’heure.
|
||
func (c Clock) Minute() uint { return c.i() }
|
||
|
||
// Hour retourne l’heure du jour.
|
||
func (c Clock) Hour() uint { return c.h() }
|
||
|
||
// Clock retourne l’heure, la minute, la seconde et la milliseconde.
|
||
func (c Clock) Clock() (h, i, s, v uint) { return c.h(), c.i(), c.s(), c.v() }
|
||
|
||
// SetMilli modifie la milliseconde de la seconde.
|
||
func (c Clock) SetMilli(v uint) Clock { return newClock(c.setV(v), c.location) }
|
||
|
||
// SetSecond modifie la seconde de la minute, et la milliseconde, si v est renseignée.
|
||
func (c Clock) SetSecond(s uint, v ...uint) Clock { return newClock(c.setS(s, v...), c.location) }
|
||
|
||
// SetMinute modifie la minute de l’heure, ainsi que la seconde et la millisecondes si elles sont renseignées.
|
||
func (c Clock) SetMinute(i uint, args ...uint) Clock { return newClock(c.setI(i, args...), c.location) }
|
||
|
||
// SetHour modifie l’heure et, facultativement, la minute, la seconde et la milliseconde.
|
||
func (c Clock) SetHour(h uint, args ...uint) Clock { return newClock(c.setH(h, args...), c.location) }
|
||
|
||
// SetPrecision retourne l’horloge avec la précision p.
|
||
func (c Clock) SetPrecision(p Precision) Clock { return newClock(c.setP(p), c.location) }
|
||
|
||
// Add ajoute un nombre d’heures et de minutes, et, facultativement,
|
||
// de secondes et de millisecondes.
|
||
func (c Clock) Add(h, i int, args ...int) Clock { return newClock(c.add(h, i, args...), c.location) }
|
||
|
||
// AddHour ajoute une durée en heures.
|
||
func (c Clock) AddHour(h int) Clock { return newClock(c.addH(h), c.location) }
|
||
|
||
// AddMinute ajoute une durée en minutes.
|
||
func (c Clock) AddMinute(i int) Clock { return newClock(c.addI(i), c.location) }
|
||
|
||
// AddSecond ajoute une durée en secondes.
|
||
func (c Clock) AddSecond(s int) Clock { return newClock(c.addS(s), c.location) }
|
||
|
||
// AddMilli ajoute une durée en millisecondes.
|
||
func (c Clock) AddMilli(v int) Clock { return newClock(c.addV(v), c.location) }
|
||
|
||
// AddDuration ajoute une durée.
|
||
func (c Clock) AddDuration(dd Duration) Clock { return newClock(c.addDD(dd), c.location) }
|
||
|
||
// BeginOfDay retourne le début de la journée, en gardant la précision.
|
||
func (c Clock) BeginOfDay() Clock { return newClock(c.bD(), c.location) }
|
||
|
||
// EndOfDay retourne la fin de la journée, en gardant la précision.
|
||
func (c Clock) EndOfDay() Clock { return newClock(c.eD(), c.location) }
|
||
|
||
// BeginOfHour retourne le début de l’heure en cours.
|
||
func (c Clock) BeginOfHour() Clock { return newClock(c.bH(), c.location) }
|
||
|
||
// EndOfHour retourne la fin de l’heure en cours.
|
||
func (c Clock) EndOfHour() Clock { return newClock(c.eH(), c.location) }
|
||
|
||
// BeginOfMinute retourne le début de la minute en cours.
|
||
func (c Clock) BeginOfMinute() Clock { return newClock(c.bI(), c.location) }
|
||
|
||
// EndOfMinute retourne la fin de la minute en cours.
|
||
func (c Clock) EndOfMinute() Clock { return newClock(c.eI(), c.location) }
|
||
|
||
// BeginOfSecond retourne le début de la seconde en cours.
|
||
func (c Clock) BeginOfSecond() Clock { return newClock(c.bS(), c.location) }
|
||
|
||
// EndOfSecond retourne la fin de la seconde en cours.
|
||
func (c Clock) EndOfSecond() Clock { return newClock(c.eS(), c.location) }
|
||
|
||
// ToTime convertit l’heure en date de type time.Time.
|
||
func (c Clock) ToTime() time.Time { return c.t(c.location) }
|
||
|
||
// In retourne l’heure dans le fuseau horaire indiqué.
|
||
func (c Clock) In(l *time.Location) Clock {
|
||
if c.IsNil() {
|
||
return c
|
||
}
|
||
|
||
return NewClockFromTime(c.ToTime().In(l))
|
||
}
|
||
|
||
// InTZ agit comme In mais en fournissant le fuseau horaire sous forme
|
||
// de chaîne de caractères.
|
||
func (c Clock) inTZ(tz string) Clock { return c.In(timezone(tz)) }
|
||
|
||
// TZ retourne le fuseau horaire.
|
||
func (c Clock) TZ() *time.Location { return c.location }
|
||
|
||
// IsDST retourne vrai si le fuseau horaire est à l’heure d’été.
|
||
func (c Clock) IsDST() bool { return c.dst(c.location) }
|
||
|
||
// Timestamp retourne le timestamp en secondes.
|
||
func (c Clock) Timestamp() int64 { return c.ts(c.location) }
|
||
|
||
// TimestampMilli retourne le timestamp en secondes.
|
||
func (c Clock) TimestampMilli() int64 { return c.tv(c.location) }
|
||
|
||
// Compare retourne :
|
||
// - 1 si c1 est dans le futur de c2
|
||
// - -1 si c1 est dans le passé de c2
|
||
// - 0 si c1 = c2.
|
||
// La comparaison s’effectue après normalisation (ie. même fuseau horaire et même précision).
|
||
func (c1 Clock) Compare(c2 Clock) int { return c1.cmp(c2.In(c1.location).clock) }
|
||
|
||
func (c1 Clock) Eq(c2 Clock) bool { return c1.Compare(c2) == 0 }
|
||
func (c1 Clock) Ne(c2 Clock) bool { return c1.Compare(c2) != 0 }
|
||
func (c1 Clock) Gt(c2 Clock) bool { return c1.Compare(c2) > 0 }
|
||
func (c1 Clock) Ge(c2 Clock) bool { return c1.Compare(c2) >= 0 }
|
||
func (c1 Clock) Lt(c2 Clock) bool { return c1.Compare(c2) < 0 }
|
||
func (c1 Clock) Le(c2 Clock) bool { return c1.Compare(c2) <= 0 }
|
||
|
||
// IsNow retourne vrai si l’horloge est à l’heure actuelle.
|
||
func (c Clock) IsNow() bool { return c.Eq(c.Now()) }
|
||
|
||
// IsPast retourne vrai si l’horloge est située dans le passé.
|
||
func (c Clock) IsPast() bool { return c.Lt(c.Now()) }
|
||
|
||
// IsFuture retourne vrai si l’horloge est située dans le futur.
|
||
func (c Clock) IsFuture() bool { return c.Gt(c.Now()) }
|
||
|
||
// DiffInMillis retourne c1 - c2 en millisecondes.
|
||
func (c1 Clock) DiffInMillis(c2 Clock) int { return c1.subV(c2.clock) }
|
||
|
||
// DiffInSeconds retourne c1 - c2 en secondes.
|
||
func (c1 Clock) DiffInSeconds(c2 Clock) int { return c1.subS(c2.clock) }
|
||
|
||
// DiffInMinutes retourne c1 - c2 en minutes.
|
||
func (c1 Clock) DiffInMinutes(c2 Clock) int { return c1.subI(c2.clock) }
|
||
|
||
// DiffInHours retourne c1 - c2 en heures.
|
||
func (c1 Clock) DiffInHours(c2 Clock) int { return c1.subH(c2.clock) }
|
||
|
||
// Format retourne une représentation de l’heure au format spécifié.
|
||
func (c Clock) Format(format string) string { return c.f(format, c.location) }
|
||
|
||
// String retourne une représentation au format H:iT, H:i:sT ou H:i:s.vT suivant la précision.
|
||
func (c Clock) String() string { return c.str(c.location) }
|