Module datetime réécrit
This commit is contained in:
parent
0700fced7b
commit
41e37d8398
|
@ -0,0 +1,414 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type clock uint
|
||||
|
||||
// Precision représente la précision d’une horloge.
|
||||
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 dans le fuseau horaire par défaut.
|
||||
// La précision est calculée automatiquement suivant que la seconde et la milliseconde sont indiquées.
|
||||
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.
|
||||
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.
|
||||
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. 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)
|
||||
}
|
||||
|
||||
func (c Clock) Now() Clock { return ClockNow(c.p(), c.location.String()) }
|
||||
|
||||
// ClockGuess retourne l’heure à partir de e 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 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.
|
||||
func (c Clock) SetMilli(v uint) Clock { return newClock(c.setV(v), c.location) }
|
||||
|
||||
// SetSecond modifie la seconde.
|
||||
func (c Clock) SetSecond(s uint, v ...uint) Clock { return newClock(c.setS(s, v...), c.location) }
|
||||
|
||||
// SetMinute modifie la minute.
|
||||
func (c Clock) SetMinute(i uint, args ...uint) Clock { return newClock(c.setI(i, args...), c.location) }
|
||||
|
||||
// SetHour modifie l’heure.
|
||||
func (c Clock) SetHour(h uint, args ...uint) Clock { return newClock(c.setH(h, args...), c.location) }
|
||||
|
||||
// SetPrecision modifie la précision.
|
||||
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 ms.
|
||||
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))
|
||||
}
|
||||
|
||||
// ToTimezone retourne l’heure dans le fuseau horaire indiqué.
|
||||
func (c Clock) ToTimezone(tz string) Clock { return c.In(timezone(tz)) }
|
||||
|
||||
// Location retourne le fuseau horaire.
|
||||
func (c Clock) Location() *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 }
|
||||
|
||||
func (c Clock) IsNow() bool { return c.Eq(c.Now()) }
|
||||
func (c Clock) IsPast() bool { return c.Lt(c.Now()) }
|
||||
func (c Clock) IsFuture() bool { return c.Gt(c.Now()) }
|
||||
|
||||
func (c1 Clock) DiffInMillis(c2 Clock) int { return c1.subV(c2.clock) }
|
||||
func (c1 Clock) DiffInSeconds(c2 Clock) int { return c1.subS(c2.clock) }
|
||||
func (c1 Clock) DiffInMinutes(c2 Clock) int { return c1.subI(c2.clock) }
|
||||
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) }
|
|
@ -1,78 +0,0 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/go/analysis/passes/bools"
|
||||
)
|
||||
|
||||
// IsZero retourne vrai si la date représente le 01/01/0001 à 00:00 UTC.
|
||||
func IsZero(t time.Time) bool {
|
||||
return t.IsZero()
|
||||
}
|
||||
|
||||
// Compare retourne :
|
||||
// - 1 si t1 est dans le futur de t2,
|
||||
// - 0 si t1 et t2 sont égaux,
|
||||
// - -1 sinon.
|
||||
func Compare(t1, t2 time.Time) int {
|
||||
u1, u2 := t1.Unix(), t2.Unix()
|
||||
|
||||
switch {
|
||||
case u1 == u2:
|
||||
return 0
|
||||
case u1 > u2:
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Eq retourne vrai si t1 = t2.
|
||||
func Eq(t1, t2 time.Time) bool { return Compare(t1, t2) == 0 }
|
||||
|
||||
// Ne retourne vrai si t1 ≠ t2.
|
||||
func Ne(t1, t2 time.Time) bool { return Compare(t1, t2) != 0 }
|
||||
|
||||
// Gt retourne vrai si t1 > t2.
|
||||
func Gt(t1, t2 time.Time) bool { return Compare(t1, t2) > 0 }
|
||||
|
||||
// Ge retourne vrai si t1 ≥ t2.
|
||||
func Ge(t1, t2 time.Time) bool { return Compare(t1, t2) >= 0 }
|
||||
|
||||
// Lt retourne vrai si t1 < t2.
|
||||
func Lt(t1, t2 time.Time) bool { return Compare(t1, t2) < 0 }
|
||||
|
||||
// Le retourne vrai si t1 ≤ t2.
|
||||
func Le(t1, t2 time.Time) bool { return Compare(t1, t2) <= 0 }
|
||||
|
||||
// Min retourne la date la plus située dans le passé.
|
||||
func Min(t time.Time, times ...time.Time) time.Time {
|
||||
for _, tt := range times {
|
||||
if Lt(tt, t) {
|
||||
t = tt
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Max retourne la date la plus située dans le futur.
|
||||
func Max(t time.Time, times ...time.Time) time.Time {
|
||||
for _, tt := range times {
|
||||
if Gt(tt, t) {
|
||||
t = tt
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// IsNow retourne vrai si la date est actuelle.
|
||||
func IsNow(t time.Time) bool { return Eq(t, time.Now()) }
|
||||
|
||||
// IsPast retourne vrai si la date est située dans le passé.
|
||||
func IsPast(t time.Time) bool { return Lt(t, time.Now()) }
|
||||
|
||||
// IsFuture retourne vrai si la date est située dans le future.
|
||||
func IsFuture(t time.Time) bool { return Gt(t, time.Now()) }
|
|
@ -4,9 +4,229 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Conversions d’unités.
|
||||
NanosecondPerMicrosecond = 1000
|
||||
MicrosecondPerMillisecond = 1000
|
||||
MillisecondPerSecond = 1000
|
||||
SecondPerMinute = 60
|
||||
MinutePerHour = 60
|
||||
HourPerDay = 24
|
||||
DayPerWeek = 7
|
||||
MonthPerYear = 12
|
||||
BeatPerDay = 1000
|
||||
|
||||
HourPerWeek = HourPerDay * DayPerWeek
|
||||
MinutePerDay = MinutePerHour * HourPerDay
|
||||
MinutePerWeek = MinutePerHour * HourPerWeek
|
||||
SecondPerHour = SecondPerMinute * MinutePerHour
|
||||
SecondPerDay = SecondPerMinute * MinutePerDay
|
||||
SecondPerWeek = SecondPerMinute * MinutePerWeek
|
||||
MillisecondPerMinute = MillisecondPerSecond * SecondPerMinute
|
||||
MillisecondPerHour = MillisecondPerSecond * SecondPerHour
|
||||
MillisecondPerDay = MillisecondPerSecond * SecondPerDay
|
||||
MillisecondPerWeek = MillisecondPerSecond * SecondPerWeek
|
||||
MicrosecondPerSecond = MicrosecondPerMillisecond * MillisecondPerSecond
|
||||
NanosecondPerMillisecond = NanosecondPerMicrosecond * MicrosecondPerMillisecond
|
||||
NanosecondPerSecond = NanosecondPerMicrosecond * MicrosecondPerSecond
|
||||
|
||||
// Unités supportées
|
||||
NoUnit Unit = iota
|
||||
Millisecond
|
||||
Second
|
||||
Minute
|
||||
Hour
|
||||
Day
|
||||
Week
|
||||
Month
|
||||
Year
|
||||
|
||||
// Durée nulle
|
||||
DurationNil = Duration(0)
|
||||
|
||||
bitsUnit = Duration(4)
|
||||
bitsValue = 64 - bitsUnit
|
||||
|
||||
maskUnit = 1<<bitsUnit - 1
|
||||
maskValue = ^maskUnit
|
||||
|
||||
// Précisions supportées
|
||||
NoPrecision Precision = iota
|
||||
PrecisionMillisecond
|
||||
PrecisionSecond
|
||||
PrecisionMinute
|
||||
|
||||
bitsPrecision = uint32(2)
|
||||
maskPrecision = 1<<bitsPrecision - 1
|
||||
maskMillisecond = ^maskPrecision
|
||||
|
||||
minClock = uint(0)
|
||||
maxClock = uint(MillisecondPerDay - 1)
|
||||
|
||||
clockNil = clock(0)
|
||||
|
||||
bitsDay = 5
|
||||
bitsMonth = 4
|
||||
|
||||
shiftDay = date(0)
|
||||
shiftMonth = date(bitsDay)
|
||||
shiftYear = date(bitsDay + bitsMonth)
|
||||
|
||||
maskDay = 1<<bitsDay - 1
|
||||
maskMonth = (1<<bitsMonth - 1) << shiftMonth
|
||||
maskYear = ^(maskDay | maskMonth)
|
||||
|
||||
dateNil = date(0)
|
||||
|
||||
// Mois
|
||||
January = uint(1) + iota
|
||||
February
|
||||
March
|
||||
April
|
||||
May
|
||||
June
|
||||
July
|
||||
August
|
||||
September
|
||||
October
|
||||
November
|
||||
December
|
||||
|
||||
// Jours de la semaine
|
||||
Sunday = uint(iota)
|
||||
Monday
|
||||
Tuesday
|
||||
Wednesday
|
||||
Thursday
|
||||
Friday
|
||||
Saturday
|
||||
)
|
||||
|
||||
var (
|
||||
// Fuseau horaire par défaut
|
||||
DefaultTZ = time.Local
|
||||
// représentation de l’unité sous forme de caractère
|
||||
unitToByte = map[Unit]byte{
|
||||
NoUnit: 0,
|
||||
Millisecond: 'N',
|
||||
Second: 'S',
|
||||
Minute: 'M',
|
||||
Hour: 'H',
|
||||
Day: 'd',
|
||||
Week: 'w',
|
||||
Month: 'm',
|
||||
Year: 'y',
|
||||
}
|
||||
|
||||
maskDate = map[string]date{
|
||||
"d": maskDay,
|
||||
"m": maskMonth,
|
||||
"y": maskYear,
|
||||
}
|
||||
shiftDate = map[string]date{
|
||||
"d": shiftDay,
|
||||
"m": shiftMonth,
|
||||
"y": shiftYear,
|
||||
}
|
||||
|
||||
beginPrecisionM = map[Precision][]uint{
|
||||
PrecisionMillisecond: []uint{0},
|
||||
}
|
||||
endPrecisionM = map[Precision][]uint{
|
||||
PrecisionMillisecond: []uint{999},
|
||||
}
|
||||
beginPrecisionS = map[Precision][]uint{
|
||||
PrecisionSecond: []uint{0},
|
||||
PrecisionMillisecond: []uint{0, 0},
|
||||
}
|
||||
endPrecisionS = map[Precision][]uint{
|
||||
PrecisionSecond: []uint{999},
|
||||
PrecisionMillisecond: []uint{59, 999},
|
||||
}
|
||||
|
||||
// Layouts de base pour le parsage des dates
|
||||
layouts = []string{
|
||||
"Mon, Jan 2, 2006 3:04 PM",
|
||||
"2006-01-02 15:04:05", "2006-01-02 15:04:05.999", "20060102150405", "20060102150405.999",
|
||||
"2006-01-02", "2006-01-02.999", "20060102", "20060102.999",
|
||||
// ISO8601
|
||||
"2006-01-02T15:04:05-07:00", "2006-01-02T15:04:05.999-07:00",
|
||||
// RFC822
|
||||
"02 Jan 06 15:04 MST", "02 Jan 06 15:04 -0700",
|
||||
// RFC850
|
||||
"Monday, 02-Jan-06 15:04:05 MST",
|
||||
// RFC1123
|
||||
"Mon, 02 Jan 2006 15:04:05 MST", "Mon, 02 Jan 2006 15:04:05 -0700",
|
||||
// RFC3339
|
||||
"2006-01-02T15:04:05Z07:00", "2006-01-02T15:04:05.999999999Z07:00",
|
||||
// RFC1036
|
||||
"Mon, 02 Jan 06 15:04:05 -0700",
|
||||
// RFC7231
|
||||
"Mon, 02 Jan 2006 15:04:05 MST",
|
||||
"3:04PM",
|
||||
// Cookies
|
||||
"Monday, 02-Jan-2006 15:04:05 MST",
|
||||
// ANSIC
|
||||
"Mon Jan _2 15:04:05 2006",
|
||||
// UNIX
|
||||
"Mon Jan _2 15:04:05 MST 2006",
|
||||
// Ruby
|
||||
"Mon Jan 02 15:04:05 -0700 2006",
|
||||
|
||||
"2006",
|
||||
"2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "2006-1-2 15:4:5.999",
|
||||
"2006.1", "2006.1.2", "2006.1.2 15", "2006.1.2 15:4", "2006.1.2 15:4:5", "2006.1.2 15:4:5.999",
|
||||
"2006/1", "2006/1/2", "2006/1/2 15", "2006/1/2 15:4", "2006/1/2 15:4:5", "2006/1/2 15:4:5.999",
|
||||
"2006-01", "2006-01-02", "2006-01-02 15", "2006-01-02 15:04", "2006-01-02 15:04:05", "2006-01-02 15:04:05.999",
|
||||
"2006.01", "2006.01.02", "2006.01.02 15", "2006.01.02 15:04", "2006.01.02 15:04:05", "2006.01.02 15:04:05.999",
|
||||
"2006/01", "2006/01/02", "2006/01/02 15", "2006/01/02 15:04", "2006/01/02 15:04:05", "2006/01/02 15:04:05.999",
|
||||
"2006-01-02 15:04:05PM MST", "2006-01-02 15:04:05.999PM MST", "2006-1-2 15:4:5PM MST", "2006-1-2 15:4:5.999PM MST",
|
||||
"2006-01-02 15:04:05 PM MST", "2006-01-02 15:04:05.999 PM MST", "2006-1-2 15:4:5 PM MST", "2006-1-2 15:4:5.999 PM MST",
|
||||
"2/1/2006", "2/1/2006 15", "2/1/2006 15:4", "2/1/2006 15:4:5", "2/1/2006 15:4:5.999",
|
||||
"02/01/2006", "02/01/2006 15", "02/01/2006 15:04", "02/01/2006 15:04:05", "02/01/2006 15:04:05.999",
|
||||
"2006-1-2 15:4:5 -0700 MST", "2006-1-2 15:4:5.999 -0700 MST",
|
||||
"2006-1-2T15:4:5Z07", "2006-1-2T15:4:5.999Z07",
|
||||
"2006-1-2T15:4:5Z07:00", "2006-1-2T15:4:5.999Z07:00",
|
||||
"2006-1-2T15:4:5-07:00", "2006-1-2T15:4:5.999-07:00",
|
||||
"20060102150405-07:00", "20060102150405.999-07:00",
|
||||
"20060102150405Z07", "20060102150405.999Z07",
|
||||
"20060102150405Z07:00", "20060102150405.999Z07:00",
|
||||
}
|
||||
|
||||
// Conversion parsage → layout
|
||||
parsers = map[rune]string{
|
||||
// Jours
|
||||
'j': "2", // Jour du mois sans les 0 initiaux (1-31)
|
||||
'd': "02", // Jour du mois, sur deux chiffres (avec un 0 initial) (01-31)
|
||||
'D': "Mon", // Jour de la semaine, en 3 lettres (Mon-Sun)
|
||||
'l': "Monday", // Jour de la semaine, textuel, version longue (Monday-Sunday)
|
||||
|
||||
// Mois
|
||||
'n': "1", // Mois sans les 0 initiaux (1-12)
|
||||
'm': "01", // Mois au format numérique, avec 0 initiaux (01-12)
|
||||
'M': "Jan", // Mois, en trois lettres (Jan-Dec)
|
||||
'F': "January", // Mois, textuel, version longue (January-December)
|
||||
|
||||
// Année
|
||||
'y': "06", // Année sur 2 chiffres (Exemples : 99 ou 03)
|
||||
'Y': "2006", // Année sur au moins 4 chiffres, avec - pour les années av. J.-C. (Exemples : -0055, 0787, 1999, 2003, 10191)
|
||||
|
||||
// Heure
|
||||
'a': "pm", // Ante meridiem et Post meridiem en minuscules (am ou pm)
|
||||
'A': "PM", // Ante meridiem et Post meridiem en majuscules (AM ou PM)
|
||||
'g': "3", // Heure, au format 12h, sans les 0 initiaux (1-12)
|
||||
'h': "03", // Heure, au format 12h, avec les 0 initiaux (01-12)
|
||||
'H': "15", // Heure, au format 24h, avec les 0 initiaux (00-23)
|
||||
'i': "04", // Minutes avec les 0 initiaux (00-59)
|
||||
's': "05", // Secondes avec les 0 initiaux (00-59)
|
||||
|
||||
// Fuseau horaire
|
||||
'T': "MST", // Abréviation du fuseau horaire, si connu ; sinon décalage depuis GMT (Exemples : EST, MDT, +05)
|
||||
'O': "-0700", // Différence d’heures avec l’heure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
|
||||
'P': "-07:00", // Différence d’heures avec l’heure de Greenwich (GMT), avec deux-points entre les heures et les minutes (Exemple : +02:00)
|
||||
|
||||
// Date et heure complète
|
||||
'c': "2006-01-02T15:04:05-07:00", // Date au format ISO 8601 (2004-02-12T15:19:21+00:00)
|
||||
'r': "Thu, 21 Dec 2000 16:01:07 +0200", // Date au format RFC 5322 (Thu, 21 Dec 2000 16:01:070200)
|
||||
}
|
||||
|
||||
// Jours de la semaine (format court)
|
||||
shortDays = []string{
|
||||
|
@ -115,143 +335,22 @@ var (
|
|||
'U': format_U, // Secondes depuis l’époque Unix (1er Janvier 1970, 0h00 00s GMT)
|
||||
}
|
||||
|
||||
parsers = map[rune]string{
|
||||
// Jours
|
||||
'j': "2", // Jour du mois sans les 0 initiaux (1-31)
|
||||
'd': "02", // Jour du mois, sur deux chiffres (avec un 0 initial) (01-31)
|
||||
'D': "Mon", // Jour de la semaine, en 3 lettres (Mon-Sun)
|
||||
'l': "Monday", // Jour de la semaine, textuel, version longue (Monday-Sunday)
|
||||
|
||||
// Mois
|
||||
'n': "1", // Mois sans les 0 initiaux (1-12)
|
||||
'm': "01", // Mois au format numérique, avec 0 initiaux (01-12)
|
||||
'M': "Jan", // Mois, en trois lettres (Jan-Dec)
|
||||
'F': "January", // Mois, textuel, version longue (January-December)
|
||||
|
||||
// Année
|
||||
'y': "06", // Année sur 2 chiffres (Exemples : 99 ou 03)
|
||||
'Y': "2006", // Année sur au moins 4 chiffres, avec - pour les années av. J.-C. (Exemples : -0055, 0787, 1999, 2003, 10191)
|
||||
|
||||
// Heure
|
||||
'a': "pm", // Ante meridiem et Post meridiem en minuscules (am ou pm)
|
||||
'A': "PM", // Ante meridiem et Post meridiem en majuscules (AM ou PM)
|
||||
'g': "3", // Heure, au format 12h, sans les 0 initiaux (1-12)
|
||||
'h': "03", // Heure, au format 12h, avec les 0 initiaux (01-12)
|
||||
'H': "15", // Heure, au format 24h, avec les 0 initiaux (00-23)
|
||||
'i': "04", // Minutes avec les 0 initiaux (00-59)
|
||||
's': "05", // Secondes avec les 0 initiaux (00-59)
|
||||
|
||||
// Fuseau horaire
|
||||
'T': "MST", // Abréviation du fuseau horaire, si connu ; sinon décalage depuis GMT (Exemples : EST, MDT, +05)
|
||||
'O': "-0700", // Différence d’heures avec l’heure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
|
||||
'P': "-07:00", // Différence d’heures avec l’heure de Greenwich (GMT), avec deux-points entre les heures et les minutes (Exemple : +02:00)
|
||||
|
||||
// Date et heure complète
|
||||
'c': "2006-01-02T15:04:05-07:00", // Date au format ISO 8601 (2004-02-12T15:19:21+00:00)
|
||||
'r': "Thu, 21 Dec 2000 16:01:07 +0200", // Date au format RFC 5322 (Thu, 21 Dec 2000 16:01:070200)
|
||||
amPmLow = map[bool]string{
|
||||
true: "am",
|
||||
false: "pm",
|
||||
}
|
||||
|
||||
layouts = []string{
|
||||
DayDateTimeLayout,
|
||||
DateTimeLayout, DateTimeNanoLayout, ShortDateTimeLayout, ShortDateTimeNanoLayout,
|
||||
DateLayout, DateNanoLayout, ShortDateLayout, ShortDateNanoLayout,
|
||||
ISO8601Layout, ISO8601NanoLayout,
|
||||
RFC822Layout, RFC822ZLayout, RFC850Layout, RFC1123Layout, RFC1123ZLayout, RFC3339Layout, RFC3339NanoLayout, RFC1036Layout, RFC7231Layout,
|
||||
KitchenLayout,
|
||||
CookieLayout,
|
||||
ANSICLayout,
|
||||
UnixDateLayout,
|
||||
RubyDateLayout,
|
||||
"2006",
|
||||
"2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "2006-1-2 15:4:5.999999999",
|
||||
"2006.1", "2006.1.2", "2006.1.2 15", "2006.1.2 15:4", "2006.1.2 15:4:5", "2006.1.2 15:4:5.999999999",
|
||||
"2006/1", "2006/1/2", "2006/1/2 15", "2006/1/2 15:4", "2006/1/2 15:4:5", "2006/1/2 15:4:5.999999999",
|
||||
"2006-01-02 15:04:05PM MST", "2006-01-02 15:04:05.999999999PM MST", "2006-1-2 15:4:5PM MST", "2006-1-2 15:4:5.999999999PM MST",
|
||||
"2006-01-02 15:04:05 PM MST", "2006-01-02 15:04:05.999999999 PM MST", "2006-1-2 15:4:5 PM MST", "2006-1-2 15:4:5.999999999 PM MST",
|
||||
"1/2/2006", "1/2/2006 15", "1/2/2006 15:4", "1/2/2006 15:4:5", "1/2/2006 15:4:5.999999999",
|
||||
"2006-1-2 15:4:5 -0700 MST", "2006-1-2 15:4:5.999999999 -0700 MST",
|
||||
"2006-1-2T15:4:5Z07", "2006-1-2T15:4:5.999999999Z07",
|
||||
"2006-1-2T15:4:5Z07:00", "2006-1-2T15:4:5.999999999Z07:00",
|
||||
"2006-1-2T15:4:5-07:00", "2006-1-2T15:4:5.999999999-07:00",
|
||||
"20060102150405-07:00", "20060102150405.999999999-07:00",
|
||||
"20060102150405Z07", "20060102150405.999999999Z07",
|
||||
"20060102150405Z07:00", "20060102150405.999999999Z07:00",
|
||||
amPmUp = map[bool]string{
|
||||
true: "AM",
|
||||
false: "PM",
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// Unités de durée
|
||||
Nanosecond = time.Nanosecond
|
||||
Microsecond = time.Microsecond
|
||||
Millisecond = time.Millisecond
|
||||
Second = time.Second
|
||||
Minute = time.Minute
|
||||
Hour = time.Hour
|
||||
Day = time.Hour * 24
|
||||
Week = Day * 7
|
||||
|
||||
MonthsPerYear = 12
|
||||
DaysPerWeek = 7
|
||||
HoursPerDay = 24
|
||||
HoursPerWeek = HoursPerDay * DaysPerWeek
|
||||
MinutesPerHour = 60
|
||||
MinutesPerDay = MinutesPerHour * HoursPerDay
|
||||
MinutesPerWeek = MinutesPerHour * HoursPerWeek
|
||||
SecondsPerMinute = 60
|
||||
SecondsPerHour = SecondsPerMinute * MinutesPerHour
|
||||
SecondsPerDay = SecondsPerMinute * MinutesPerDay
|
||||
SecondsPerWeek = SecondsPerMinute * MinutesPerWeek
|
||||
|
||||
// Erreurs
|
||||
errInvalidTZ = "Invalid timezone %q"
|
||||
errInvalidValue = "Cannot parse string %q as datetime, please make sure the value is valid"
|
||||
|
||||
// Quelques layouts
|
||||
ANSICLayout = time.ANSIC
|
||||
UnixDateLayout = time.UnixDate
|
||||
RubyDateLayout = time.RubyDate
|
||||
RFC822Layout = time.RFC822
|
||||
RFC822ZLayout = time.RFC822Z
|
||||
RFC850Layout = time.RFC850
|
||||
RFC1123Layout = time.RFC1123
|
||||
RFC1123ZLayout = time.RFC1123Z
|
||||
RssLayout = time.RFC1123Z
|
||||
KitchenLayout = time.Kitchen
|
||||
RFC2822Layout = time.RFC1123Z
|
||||
CookieLayout = "Monday, 02-Jan-2006 15:04:05 MST"
|
||||
RFC3339Layout = "2006-01-02T15:04:05Z07:00"
|
||||
RFC3339MilliLayout = "2006-01-02T15:04:05.999Z07:00"
|
||||
RFC3339MicroLayout = "2006-01-02T15:04:05.999999Z07:00"
|
||||
RFC3339NanoLayout = "2006-01-02T15:04:05.999999999Z07:00"
|
||||
ISO8601Layout = "2006-01-02T15:04:05-07:00"
|
||||
ISO8601MilliLayout = "2006-01-02T15:04:05.999-07:00"
|
||||
ISO8601MicroLayout = "2006-01-02T15:04:05.999999-07:00"
|
||||
ISO8601NanoLayout = "2006-01-02T15:04:05.999999999-07:00"
|
||||
RFC1036Layout = "Mon, 02 Jan 06 15:04:05 -0700"
|
||||
RFC7231Layout = "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
DayDateTimeLayout = "Mon, Jan 2, 2006 3:04 PM"
|
||||
DateTimeLayout = "2006-01-02 15:04:05"
|
||||
DateTimeMilliLayout = "2006-01-02 15:04:05.999"
|
||||
DateTimeMicroLayout = "2006-01-02 15:04:05.999999"
|
||||
DateTimeNanoLayout = "2006-01-02 15:04:05.999999999"
|
||||
ShortDateTimeLayout = "20060102150405"
|
||||
ShortDateTimeMilliLayout = "20060102150405.999"
|
||||
ShortDateTimeMicroLayout = "20060102150405.999999"
|
||||
ShortDateTimeNanoLayout = "20060102150405.999999999"
|
||||
DateLayout = "2006-01-02"
|
||||
DateMilliLayout = "2006-01-02.999"
|
||||
DateMicroLayout = "2006-01-02.999999"
|
||||
DateNanoLayout = "2006-01-02.999999999"
|
||||
ShortDateLayout = "20060102"
|
||||
ShortDateMilliLayout = "20060102.999"
|
||||
ShortDateMicroLayout = "20060102.999999"
|
||||
ShortDateNanoLayout = "20060102.999999999"
|
||||
TimeLayout = "15:04:05"
|
||||
TimeMilliLayout = "15:04:05.999"
|
||||
TimeMicroLayout = "15:04:05.999999"
|
||||
TimeNanoLayout = "15:04:05.999999999"
|
||||
ShortTimeLayout = "150405"
|
||||
ShortTimeMilliLayout = "150405.999"
|
||||
ShortTimeMicroLayout = "150405.999999"
|
||||
ShortTimeNanoLayout = "150405.999999999"
|
||||
|
||||
formatP = map[Precision]string{
|
||||
PrecisionMinute: "H:iT",
|
||||
PrecisionSecond: "H:i:sT",
|
||||
PrecisionMillisecond: "H:i:s.vT",
|
||||
}
|
||||
|
||||
// Fuseau horaire par défaut
|
||||
DefaultTZ = time.Local
|
||||
)
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
// Zero retourne la date correspondant au 01/01/0001 00:00:00 UTC.
|
||||
// Si tz est fourni, la date est dans le fuseau horaire demandé,
|
||||
// sinon, dans le fuseau horaire par défaut.
|
||||
func Zero(tz ...string) Result[time.Time] {
|
||||
var t time.Time
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// Now retourne la date actuelle.
|
||||
func Now(tz ...string) Result[time.Time] {
|
||||
t := time.Now()
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// Tomorrow retourne la date dans 24h.
|
||||
func Tomorrow(tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if t, ok := now.Ok(); ok {
|
||||
return Ok(AddDays(t, 1))
|
||||
}
|
||||
|
||||
return now
|
||||
}
|
||||
|
||||
// Yesterday retourne la date il y a 24h.
|
||||
func Yesterday(tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if t, ok := now.Ok(); ok {
|
||||
return Ok(AddDays(t, -1))
|
||||
}
|
||||
|
||||
return now
|
||||
}
|
||||
|
||||
// FromTimestamp convertit un timestamp (exprimé en secondes) en date.
|
||||
func FromTimestamp(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp, 0)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampMilli convertit un timestamp (exprimé en ms) en date.
|
||||
func FromTimestampMilli(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e3, (timestamp%1e3)*1e6)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampMicro convertit un timestamp (exprimé en μs) en date.
|
||||
func FromTimestampMicro(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e6, (timestamp%1e6)*1e3)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampNano convertit un timestamp (exprimé en ns) en date.
|
||||
func FromTimestampNano(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e9, timestamp%1e9)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromDateTime retourne la date à partir des données numériques complètes.
|
||||
func FromDateTime(year, month, day, hour, minute, second int, tz ...string) Result[time.Time] {
|
||||
location, ok := getTZ(tz...).Get()
|
||||
if !ok {
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz))
|
||||
}
|
||||
|
||||
t := time.Date(year, time.Month(month-1), day, hour, minute, second, 0, location)
|
||||
|
||||
return Ok(t)
|
||||
}
|
||||
|
||||
// FromDate retourne le début du jour à partir des données de dates.
|
||||
func FromDate(year, month, day int, tz ...string) Result[time.Time] {
|
||||
return FromDateTime(year, month, day, 0, 0, 0)
|
||||
}
|
||||
|
||||
// FromTime retourne la date d’aujourd’hui à l’heure indiquée.
|
||||
func FromTime(hour, minute, second int, tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if !now.IsOk() {
|
||||
return now
|
||||
}
|
||||
|
||||
n, _ := now.Ok()
|
||||
year, month, day := n.Date()
|
||||
return FromDateTime(year, int(month+1), day, hour, minute, second, tz...)
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type date int
|
||||
|
||||
func newD(y int, m, j uint) date {
|
||||
if !validD(y, m, j) {
|
||||
return dateNil
|
||||
}
|
||||
return setD(y, "y") | setD(m, "m") | setD(j, "d")
|
||||
}
|
||||
|
||||
func (t date) y() int { return getD[int](t, "y") }
|
||||
func (t date) m() uint { return getD[uint](t, "m") }
|
||||
func (t date) d() uint { return getD[uint](t, "d") }
|
||||
func (t date) t(l *time.Location) time.Time { return fromD(t.y(), t.m(), t.d(), l) }
|
||||
func (t date) dst(l *time.Location) bool { return t.t(l).IsDST() }
|
||||
func (t date) ts(l *time.Location) int64 { return t.t(l).Unix() }
|
||||
func (t date) tv(l *time.Location) int64 { return t.t(l).UnixMilli() }
|
||||
func (t date) b() bool { return bissextil(t.y()) }
|
||||
func (t date) yl() uint { return yearLen(t.y()) }
|
||||
func (t date) ml() uint { return monthLen(t.y(), t.m()) }
|
||||
|
||||
func (t date) valid() bool { return validD(t.y(), t.m(), t.d()) }
|
||||
|
||||
func (t date) yd() uint {
|
||||
if t.valid() {
|
||||
y, m, d := t.y(), t.m(), t.d()
|
||||
for mm := January; mm < m; mm++ {
|
||||
d += monthLen(y, mm)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
func (t date) wd(l *time.Location) uint { return uint(t.t(l).Weekday()) }
|
||||
|
||||
func (t date) setY(y int, e ...uint) date {
|
||||
if !t.valid() {
|
||||
return t
|
||||
}
|
||||
m, d := t.m(), t.d()
|
||||
if len(e) > 0 {
|
||||
m = e[0]
|
||||
if len(e) > 1 {
|
||||
d = e[1]
|
||||
} else if ml := monthLen(y, m); d > ml {
|
||||
d = ml
|
||||
}
|
||||
}
|
||||
return newD(y, m, d)
|
||||
}
|
||||
func (t date) setM(m uint, e ...uint) date {
|
||||
if !t.valid() {
|
||||
return t
|
||||
}
|
||||
y, d := t.y(), t.d()
|
||||
if len(e) > 0 {
|
||||
d = e[0]
|
||||
} else if ml := monthLen(y, m); d > ml {
|
||||
d = ml
|
||||
}
|
||||
return newD(y, m, d)
|
||||
}
|
||||
func (t date) setD(d uint) date {
|
||||
if !t.valid() {
|
||||
return t
|
||||
}
|
||||
t = replD(t, d, "d")
|
||||
if !t.valid() {
|
||||
return dateNil
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t date) add(y, m, d int) date {
|
||||
if !t.valid() {
|
||||
return t
|
||||
}
|
||||
y, m, d = t.y()+y, int(t.m())+m, int(t.d())+d
|
||||
if m > int(December) {
|
||||
mm := m - 1
|
||||
y, m = y+mm/MonthPerYear, (mm%MonthPerYear)+1
|
||||
} else if m < int(January) {
|
||||
mm := m - 12
|
||||
y, m = y+mm/MonthPerYear, (mm%MonthPerYear)+12
|
||||
}
|
||||
for !validD(y, uint(m), uint(d)) {
|
||||
ml := int(monthLen(y, uint(m)))
|
||||
if d > ml {
|
||||
d -= ml
|
||||
m++
|
||||
if m > int(December) {
|
||||
m -= MonthPerYear
|
||||
y++
|
||||
}
|
||||
} else if d < 1 {
|
||||
m--
|
||||
if m < int(January) {
|
||||
m += MonthPerYear
|
||||
y--
|
||||
}
|
||||
d += int(monthLen(y, uint(m)))
|
||||
}
|
||||
}
|
||||
return newD(y, uint(m), uint(d))
|
||||
}
|
||||
func (t date) addY(y int) date { return t.add(y, 0, 0) }
|
||||
func (t date) addM(m int) date { return t.add(0, m, 0) }
|
||||
func (t date) addW(w int) date { return t.addD(w * DayPerWeek) }
|
||||
func (t date) addD(d int) date { return t.add(0, 0, d) }
|
||||
func (t date) addDD(dd Duration) date {
|
||||
ad := func(ud int) func(int) date {
|
||||
return func(e int) date { return t.addD(e / ud) }
|
||||
}
|
||||
f := map[Unit]func(int) date{
|
||||
Year: t.addY,
|
||||
Month: t.addM,
|
||||
Week: t.addW,
|
||||
Day: t.addD,
|
||||
Hour: ad(HourPerDay),
|
||||
Minute: ad(MinutePerDay),
|
||||
Second: ad(SecondPerDay),
|
||||
Millisecond: ad(MillisecondPerDay),
|
||||
}
|
||||
e, u := dd.Value(), dd.Unit()
|
||||
if cb, ok := f[u]; ok {
|
||||
return cb(e)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t date) bY() date { return t.setM(January, 1) }
|
||||
func (t date) eY() date { return t.setM(December, 31) }
|
||||
func (t date) bM() date { return t.setD(1) }
|
||||
func (t date) eM() date { return t.setD(monthLen(t.y(), t.m())) }
|
||||
func (t date) bW(l *time.Location) date { return t.addD(diffBow(t.wd(l))) }
|
||||
func (t date) eW(l *time.Location) date { return t.addD(diffEow(t.wd(l))) }
|
||||
|
||||
func (t1 date) cmp(t2 date) int {
|
||||
if !t1.valid() {
|
||||
if !t2.valid() {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if !t2.valid() {
|
||||
return 1
|
||||
}
|
||||
if c := cmp(t1.y(), t2.y()); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := cmp(t1.m(), t2.m()); c != 0 {
|
||||
return c
|
||||
}
|
||||
return cmp(t1.d(), t2.d())
|
||||
}
|
||||
|
||||
func (t1 date) diff(t2 date) (dy, dm, dd int) {
|
||||
return t1.y() - t2.y(), int(t1.m()) - int(t2.m()), int(t1.d()) - int(t2.d())
|
||||
}
|
||||
func (t1 date) subD(t2 date, l1, l2 *time.Location) int {
|
||||
return int(t1.ts(l1)-t2.ts(l2)) / SecondPerDay
|
||||
}
|
||||
func (t1 date) subW(t2 date, l1, l2 *time.Location) int { return t1.subD(t2, l1, l2) / DayPerWeek }
|
||||
func (t1 date) subM(t2 date) int {
|
||||
dy, dm, dd := t1.diff(t2)
|
||||
if dd < 0 {
|
||||
dm--
|
||||
}
|
||||
return dy*MonthPerYear + dm
|
||||
}
|
||||
func (t1 date) subY(t2 date) int { return t1.subM(t2) / MonthPerYear }
|
||||
|
||||
func (t date) f(f string, l *time.Location) string {
|
||||
if !t.valid() {
|
||||
return "-"
|
||||
}
|
||||
return formatT(t.t(l), f)
|
||||
}
|
||||
func (t date) str(l *time.Location) string { return t.f("Y-m-d", l) }
|
||||
|
||||
// Date représente une date sans l’heure de la journée.
|
||||
type Date struct {
|
||||
date
|
||||
location *time.Location
|
||||
}
|
||||
|
||||
// DateNil retourne une date nulle.
|
||||
func DateNil() (t Date) { return }
|
||||
|
||||
func newDate(t date, l *time.Location) Date {
|
||||
if t.valid() {
|
||||
return Date{
|
||||
date: t,
|
||||
location: l,
|
||||
}
|
||||
}
|
||||
|
||||
return DateNil()
|
||||
}
|
||||
|
||||
// NewDate retourne la date dans le fuseau horaire demandé.
|
||||
func NewDate(y int, m, d uint, tz ...string) Date {
|
||||
return newDate(newD(y, m, d), timezone(tz...))
|
||||
}
|
||||
|
||||
// NewDateFromTime retourne la date à partir d’une date Go.
|
||||
func NewDateFromTime(t time.Time) Date {
|
||||
y, m, d := t.Date()
|
||||
l := t.Location()
|
||||
|
||||
return newDate(newD(y, uint(m+1), uint(d)), l)
|
||||
}
|
||||
|
||||
// DateNow retourne la date actuelle. Si tz est renseignée,
|
||||
// la date est placée dans le fuseau horaire indiqué.
|
||||
func DateNow(tz ...string) Date { return NewDateFromTime(now(tz...)) }
|
||||
|
||||
func (t Date) Now() Date { return DateNow(t.location.String()) }
|
||||
|
||||
// DateGuess retourne la date à partir de e en essayant de deviner le format.
|
||||
func DateGuess(e string, tz ...string) Date {
|
||||
if t, ok := guess(e, tz...).Get(); ok {
|
||||
return NewDateFromTime(t)
|
||||
}
|
||||
|
||||
return DateNil()
|
||||
}
|
||||
|
||||
// DateParse retourne la date à partir de v en spécifiant le format f.
|
||||
func DateParse(e, f string, tz ...string) Date {
|
||||
if t, ok := parse(e, f).Get(); ok {
|
||||
return NewDateFromTime(t)
|
||||
}
|
||||
|
||||
return DateNil()
|
||||
}
|
||||
|
||||
// IsNil retourne vrai si la date est nulle.
|
||||
func (t Date) IsNil() bool { return !t.valid() }
|
||||
|
||||
// Year retourne l’année.
|
||||
func (t Date) Year() int { return t.y() }
|
||||
|
||||
// Month retourne le mois (de 1 à 12).
|
||||
func (t Date) Month() uint { return t.m() }
|
||||
|
||||
// Day retourne le jour du mois (de 1 à 31).
|
||||
func (t Date) Day() uint { return t.d() }
|
||||
|
||||
// Date retourne l’année, le mois et le jour du mois.
|
||||
func (t Date) Date() (y int, m, d uint) { return t.y(), t.m(), t.d() }
|
||||
|
||||
// YearDay retourne le jour dans l’année (de 1 à 366).
|
||||
func (t Date) YearDay() uint { return t.yd() }
|
||||
|
||||
// WeekDay retourne le jour de la semaine (de 0 à 6 en commençant par dimanche).
|
||||
func (t Date) WeekDay() uint { return t.wd(t.location) }
|
||||
|
||||
// IsBissextil retourn vrai si la date est dans une année bissextile.
|
||||
func (t Date) IsBissextil() bool { return t.b() }
|
||||
|
||||
// DaysInYear retourne le nombre de jours dans l’année.
|
||||
func (t Date) DaysInYear() uint { return t.yl() }
|
||||
|
||||
// DaysInMonth retourne le nombre de jours dans le mois.
|
||||
func (t Date) DaysInMonth() uint { return t.ml() }
|
||||
|
||||
// SetYear crée une nouvelle date en changeant l’année (et facultativement le mois et le jour).
|
||||
func (t Date) SetYear(y int, md ...uint) Date { return newDate(t.setY(y, md...), t.location) }
|
||||
|
||||
// SetMonth crée une nouvelle date en changeant le mois (et facultativement le jour).
|
||||
func (t Date) SetMonth(m uint, d ...uint) Date { return newDate(t.setM(m, d...), t.location) }
|
||||
|
||||
// SetDay crée une nouvelle date en changeant le jour.
|
||||
func (t Date) SetDay(d uint) Date { return newDate(t.setD(d), t.location) }
|
||||
|
||||
// Add incrémente l’année, le mois et le jour.
|
||||
func (t Date) Add(y, m, d int) Date { return newDate(t.add(y, m, d), t.location) }
|
||||
|
||||
// AddYear incrémente l’année.
|
||||
func (t Date) AddYear(y int) Date { return newDate(t.addY(y), t.location) }
|
||||
|
||||
// AddMonth incrémente le mois.
|
||||
func (t Date) AddMonth(m int) Date { return newDate(t.addM(m), t.location) }
|
||||
|
||||
// AddWeek incrémente la date de week semaines.
|
||||
func (t Date) AddWeek(w int) Date { return newDate(t.addW(w), t.location) }
|
||||
|
||||
// AddDay incrémente le jour.
|
||||
func (t Date) AddDay(d int) Date { return newDate(t.addD(d), t.location) }
|
||||
|
||||
// AddDuration décale la date d’une durée donnée.
|
||||
func (t Date) AddDuration(dd Duration) Date { return newDate(t.addDD(dd), t.location) }
|
||||
|
||||
// BeginOfYear retourne la date du premier jour de l’année.
|
||||
func (t Date) BeginOfYear() Date { return newDate(t.bY(), t.location) }
|
||||
|
||||
// EndOfYear retourne la date du dernier jour de l’année.
|
||||
func (t Date) EndOfYear() Date { return newDate(t.eY(), t.location) }
|
||||
|
||||
// BeginOfMonth retourne la date du premier jour du mois.
|
||||
func (t Date) BeginOfMonth() Date { return newDate(t.bM(), t.location) }
|
||||
|
||||
// EndOfMonth retourne la date du dernier jour du mois.
|
||||
func (t Date) EndOfMonth() Date { return newDate(t.eM(), t.location) }
|
||||
|
||||
// BeginOfWeek retourne la date du premier jour de la semaine (ie. lundi).
|
||||
func (t Date) BeginOfWeek() Date { return newDate(t.bW(t.location), t.location) }
|
||||
|
||||
// EndOfWeek retourne la date du dernier jour de la semaine.
|
||||
func (t Date) EndOfWeek() Date { return newDate(t.eW(t.location), t.location) }
|
||||
|
||||
// ToTime convertit la date en date de type time.Time.
|
||||
func (t Date) ToTime() time.Time { return t.t(t.location) }
|
||||
|
||||
// In retourne la date dans le fuseau horaire indiqué.
|
||||
func (t Date) In(l *time.Location) Date {
|
||||
if !t.valid() {
|
||||
return t
|
||||
}
|
||||
|
||||
return NewDateFromTime(t.t(t.location).In(l))
|
||||
}
|
||||
|
||||
// ToTimezone retourne la date dans le fuseau horaire indiqué.
|
||||
func (t Date) ToTimezone(tz string) Date { return t.In(timezone(tz)) }
|
||||
|
||||
// Location retourne le fuseau horaire.
|
||||
func (t Date) Location() *time.Location { return t.location }
|
||||
|
||||
// IsDST retourne vrai si le fuseau horaire est à l’heure d’été.
|
||||
func (t Date) IsDST() bool { return t.dst(t.location) }
|
||||
|
||||
// Timestamp retourne le timestamp en secondes.
|
||||
func (t Date) Timestamp() int64 { return t.ts(t.location) }
|
||||
|
||||
// TimestampMilli retourne le timestamp en secondes.
|
||||
func (t Date) TimestampMilli() int64 { return t.tv(t.location) }
|
||||
|
||||
// Compare compare 2 dates.
|
||||
func (t1 Date) Compare(t2 Date) int { return t1.cmp(t2.In(t1.location).date) }
|
||||
|
||||
func (t1 Date) Eq(t2 Date) bool { return t1.Compare(t2) == 0 }
|
||||
func (t1 Date) Ne(t2 Date) bool { return t1.Compare(t2) != 0 }
|
||||
func (t1 Date) Gt(t2 Date) bool { return t1.Compare(t2) > 0 }
|
||||
func (t1 Date) Ge(t2 Date) bool { return t1.Compare(t2) >= 0 }
|
||||
func (t1 Date) Lt(t2 Date) bool { return t1.Compare(t2) < 0 }
|
||||
func (t1 Date) Le(t2 Date) bool { return t1.Compare(t2) <= 0 }
|
||||
|
||||
func (t Date) IsNow() bool { return t.Eq(t.Now()) }
|
||||
func (t Date) IsPast() bool { return t.Lt(t.Now()) }
|
||||
func (t Date) IsFuture() bool { return t.Gt(t.Now()) }
|
||||
|
||||
func (t1 Date) diff(t2 Date) (dy, dm, dd int) {
|
||||
return t1.y() - t2.y(), int(t1.m()) - int(t2.m()), int(t1.d()) - int(t2.d())
|
||||
}
|
||||
|
||||
func (t1 Date) DiffInDays(t2 Date) int { return t1.subD(t2.date, t1.location, t2.location) }
|
||||
func (t1 Date) DiffInWeeks(t2 Date) int { return t1.subW(t2.date, t1.location, t2.location) }
|
||||
func (t1 Date) DiffInMonths(t2 Date) int { return t1.subM(t2.date) }
|
||||
func (t1 Date) DiffInYears(t2 Date) int { return t1.subY(t2.date) }
|
||||
|
||||
// Format retourne une représentation de la date au format spécifié.
|
||||
func (t Date) Format(format string) string { return t.f(format, t.location) }
|
||||
|
||||
// String retourne la date au format Y-m-d.
|
||||
func (t Date) String() string { return t.str(t.location) }
|
|
@ -0,0 +1,522 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type datetime struct {
|
||||
date
|
||||
clock clock
|
||||
}
|
||||
|
||||
func newDT(d date, c clock) datetime {
|
||||
if d.valid() && c.valid() {
|
||||
return datetime{d, c}
|
||||
}
|
||||
return datetime{dateNil, clockNil}
|
||||
}
|
||||
|
||||
func (dt datetime) h() uint { return dt.clock.h() }
|
||||
func (dt datetime) i() uint { return dt.clock.i() }
|
||||
func (dt datetime) s() uint { return dt.clock.s() }
|
||||
func (dt datetime) v() uint { return dt.clock.v() }
|
||||
func (dt datetime) p() Precision { return dt.clock.p() }
|
||||
func (dt datetime) dv() uint { return dt.clock.dv() }
|
||||
func (dt datetime) ds() uint { return dt.clock.ds() }
|
||||
func (dt datetime) di() uint { return dt.clock.di() }
|
||||
|
||||
func (dt datetime) t(l *time.Location) time.Time {
|
||||
return fromDT(dt.y(), dt.m(), dt.d(), dt.h(), dt.i(), dt.s(), dt.v(), l)
|
||||
}
|
||||
func (dt datetime) dst(l *time.Location) bool { return dt.t(l).IsDST() }
|
||||
func (dt datetime) ts(l *time.Location) int64 { return dt.t(l).Unix() }
|
||||
func (dt datetime) tv(l *time.Location) int64 { return dt.t(l).UnixMilli() }
|
||||
|
||||
func (dt datetime) valid() bool { return dt.date.valid() && dt.clock.valid() }
|
||||
|
||||
func (dt datetime) setY(y int, e ...uint) datetime {
|
||||
ed, ec := spl(e, 2)
|
||||
d, c := dt.date.setY(y, ed...), dt.clock
|
||||
if len(ec) > 0 {
|
||||
c = c.setH(ec[0], ec[1:]...)
|
||||
}
|
||||
return newDT(d, c)
|
||||
}
|
||||
func (dt datetime) setM(m uint, e ...uint) datetime {
|
||||
ed, ec := spl(e, 1)
|
||||
d, c := dt.date.setM(m, ed...), dt.clock
|
||||
if len(ec) > 0 {
|
||||
c = c.setH(ec[0], ec[1:]...)
|
||||
}
|
||||
return newDT(d, c)
|
||||
}
|
||||
func (dt datetime) setD(d uint, e ...uint) datetime {
|
||||
t, c := dt.date.setD(d), dt.clock
|
||||
if len(e) > 0 {
|
||||
c = c.setH(e[0], e[1:]...)
|
||||
}
|
||||
return newDT(t, c)
|
||||
}
|
||||
func (dt datetime) setH(h uint, e ...uint) datetime { return newDT(dt.date, dt.clock.setH(h, e...)) }
|
||||
func (dt datetime) setI(i uint, e ...uint) datetime { return newDT(dt.date, dt.clock.setI(i, e...)) }
|
||||
func (dt datetime) setS(s uint, e ...uint) datetime { return newDT(dt.date, dt.clock.setS(s, e...)) }
|
||||
func (dt datetime) setV(v uint) datetime { return newDT(dt.date, dt.clock.setV(v)) }
|
||||
func (dt datetime) setP(p Precision) datetime { return newDT(dt.date, dt.clock.setP(p)) }
|
||||
|
||||
func (dt datetime) add(y, m, d, h, i int, e ...int) datetime {
|
||||
if !dt.valid() {
|
||||
return dt
|
||||
}
|
||||
p := dt.p()
|
||||
v := int(dt.dv()) + h*MillisecondPerHour + i*MillisecondPerMinute
|
||||
if len(e) > 0 {
|
||||
p, v = min(p, PrecisionSecond), v+e[0]*MillisecondPerSecond
|
||||
if len(e) > 1 {
|
||||
p, v = PrecisionMillisecond, v+e[1]
|
||||
}
|
||||
}
|
||||
dc := v / MillisecondPerDay
|
||||
d, v = d+dc, v-dc*MillisecondPerDay
|
||||
return newDT(dt.date.add(y, m, d), newC(uint(v), p))
|
||||
}
|
||||
func (dt datetime) addT(y, m, d int) datetime { return newDT(dt.date.add(y, m, d), dt.clock) }
|
||||
func (dt datetime) addC(h, i int, e ...int) datetime { return dt.add(0, 0, 0, h, i, e...) }
|
||||
func (dt datetime) addY(y int) datetime { return dt.addT(y, 0, 0) }
|
||||
func (dt datetime) addM(m int) datetime { return dt.addT(0, m, 0) }
|
||||
func (dt datetime) addW(w int) datetime { return dt.addD(w * DayPerWeek) }
|
||||
func (dt datetime) addD(d int) datetime { return dt.addT(0, 0, d) }
|
||||
func (dt datetime) addH(h int) datetime { return dt.addC(h, 0) }
|
||||
func (dt datetime) addI(i int) datetime { return dt.addC(0, i) }
|
||||
func (dt datetime) addS(s int) datetime { return dt.addC(0, 0, s) }
|
||||
func (dt datetime) addV(n int) datetime { return dt.addC(0, 0, 0, n) }
|
||||
func (dt datetime) addDD(dd Duration) datetime {
|
||||
f := map[Unit]func(int) datetime{
|
||||
Year: dt.addY,
|
||||
Month: dt.addM,
|
||||
Week: dt.addW,
|
||||
Day: dt.addD,
|
||||
Hour: dt.addH,
|
||||
Minute: dt.addI,
|
||||
Second: dt.addS,
|
||||
Millisecond: dt.addV,
|
||||
}
|
||||
e, u := dd.Value(), dd.Unit()
|
||||
if cb, ok := f[u]; ok {
|
||||
return cb(e)
|
||||
}
|
||||
return dt
|
||||
}
|
||||
|
||||
func (dt datetime) bY() datetime { return dt.setM(January, 1).bD() }
|
||||
func (dt datetime) eY() datetime { return dt.setM(December, 31).eD() }
|
||||
func (dt datetime) bM() datetime { return dt.setD(1).bD() }
|
||||
func (dt datetime) eM() datetime { return dt.setD(monthLen(dt.y(), dt.m())).eD() }
|
||||
func (dt datetime) bW(l *time.Location) datetime { return newDT(dt.date.bW(l), dt.clock).bD() }
|
||||
func (dt datetime) eW(l *time.Location) datetime { return newDT(dt.date.eW(l), dt.clock).eD() }
|
||||
func (dt datetime) bD() datetime { return newDT(dt.date, dt.clock.bD()) }
|
||||
func (dt datetime) eD() datetime { return newDT(dt.date, dt.clock.eD()) }
|
||||
func (dt datetime) bH() datetime { return newDT(dt.date, dt.clock.bH()) }
|
||||
func (dt datetime) eH() datetime { return newDT(dt.date, dt.clock.eH()) }
|
||||
func (dt datetime) bI() datetime { return newDT(dt.date, dt.clock.bI()) }
|
||||
func (dt datetime) eI() datetime { return newDT(dt.date, dt.clock.eI()) }
|
||||
func (dt datetime) bS() datetime { return newDT(dt.date, dt.clock.bS()) }
|
||||
func (dt datetime) eS() datetime { return newDT(dt.date, dt.clock.eS()) }
|
||||
|
||||
func (dt1 datetime) cmp(dt2 datetime) int {
|
||||
if !dt1.valid() {
|
||||
if !dt2.valid() {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if !dt2.valid() {
|
||||
return 1
|
||||
}
|
||||
if c := dt1.date.cmp(dt2.date); c != 0 {
|
||||
return c
|
||||
}
|
||||
return dt1.clock.cmp(dt2.clock)
|
||||
}
|
||||
|
||||
func (dt1 datetime) subV(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return int(dt1.tv(l1) - dt2.tv(l2))
|
||||
}
|
||||
func (dt1 datetime) subS(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return dt1.subV(dt2, l1, l2) / MillisecondPerSecond
|
||||
}
|
||||
func (dt1 datetime) subI(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return dt1.subV(dt2, l1, l2) / MillisecondPerMinute
|
||||
}
|
||||
func (dt1 datetime) subH(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return dt1.subV(dt2, l1, l2) / MillisecondPerHour
|
||||
}
|
||||
func (dt1 datetime) subD(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return dt1.subV(dt2, l1, l2) / MillisecondPerMinute
|
||||
}
|
||||
func (dt1 datetime) subW(dt2 datetime, l1, l2 *time.Location) int {
|
||||
return dt1.subV(dt2, l1, l2) / MillisecondPerWeek
|
||||
}
|
||||
func (dt1 datetime) subM(dt2 datetime) int {
|
||||
d := dt1.date.subM(dt2.date)
|
||||
if d < 0 {
|
||||
dd, dv := int(dt1.d())-int(dt2.d()), int(dt1.dv())-int(dt2.dv())
|
||||
if dd > 0 || (dd == 0 && dv > 0) {
|
||||
d++
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
func (dt1 datetime) subY(dt2 datetime) int { return dt1.subM(dt2) / MonthPerYear }
|
||||
|
||||
func (dt datetime) f(f string, l *time.Location) string {
|
||||
if !dt.valid() {
|
||||
return "-"
|
||||
}
|
||||
return formatT(dt.t(l), f)
|
||||
}
|
||||
func (dt datetime) str(l *time.Location) string { return dt.f("Y-m-d", l) }
|
||||
|
||||
// DateTime représente une indication de temps.
|
||||
type DateTime struct {
|
||||
datetime
|
||||
location *time.Location
|
||||
}
|
||||
|
||||
// DateTimeNil retourne un temps nul.
|
||||
func DateTimeNil() (dt DateTime) { return }
|
||||
|
||||
func initDT0(d date, c clock, l *time.Location) DateTime { return initDT(newDT(d, c), l) }
|
||||
func initDT(dt datetime, l *time.Location) DateTime {
|
||||
if !dt.valid() {
|
||||
return DateTimeNil()
|
||||
}
|
||||
return DateTime{dt, l}
|
||||
}
|
||||
|
||||
// NewDateTime retourne un temps dans le fuseau horaire par défaut.
|
||||
// La précision est calculée automatiquement suivant que la seconde et la milliseconde sont indiquées.
|
||||
func NewDateTime(y int, m, d, h, i uint, args ...uint) DateTime {
|
||||
return initDT0(newD(y, m, d), newC0(h, i, args...), DefaultTZ)
|
||||
}
|
||||
|
||||
// NewDateTimeTZ agit comme NewDateTime mais dans le fuseau horaire tz.
|
||||
func NewDateTimeTZ(tz string, y int, m, d, h, i uint, args ...uint) DateTime {
|
||||
dt := NewDateTime(y, m, d, h, m, args...)
|
||||
if dt.valid() {
|
||||
dt.location = timezone(tz)
|
||||
}
|
||||
|
||||
return dt
|
||||
}
|
||||
|
||||
// NewDateTimeFromTime retourne le temps à partir d’une date Go.
|
||||
func NewDateTimeFromTime(t time.Time, p ...Precision) DateTime {
|
||||
var (
|
||||
pp = PrecisionMillisecond
|
||||
y, m, d = t.Date()
|
||||
h, i, s = t.Clock()
|
||||
v = t.Nanosecond() / NanosecondPerMillisecond
|
||||
l = t.Location()
|
||||
)
|
||||
|
||||
if len(p) > 0 {
|
||||
pp = p[0]
|
||||
}
|
||||
|
||||
return initDT0(newD(y, uint(m+1), uint(d)), newC(ms(uint(h), uint(i), uint(s), uint(v)), pp), l)
|
||||
}
|
||||
|
||||
// DateTimeNow retourne la date actuelle. Si tz est renseignée,
|
||||
// la date est placée dans le fuseau horaire indiqué.
|
||||
func DateTimeNow(p Precision, tz ...string) DateTime {
|
||||
return NewDateTimeFromTime(now(tz...), p)
|
||||
}
|
||||
|
||||
func (dt DateTime) Now() DateTime { return DateTimeNow(dt.p(), dt.location.String()) }
|
||||
|
||||
// DateTimeGuess retourne la date à partir de e en essayant de deviner le format.
|
||||
func DateTimeGuess(p Precision, e string, tz ...string) DateTime {
|
||||
if t, ok := guess(e, tz...).Get(); ok {
|
||||
return NewDateTimeFromTime(t, p)
|
||||
}
|
||||
|
||||
return DateTimeNil()
|
||||
}
|
||||
|
||||
// DateTimeParse retourne la date à partir de e en spécifiant le format f.
|
||||
func DateTimeParse(p Precision, e, f string, tz ...string) DateTime {
|
||||
if t, ok := parse(e, f, tz...).Get(); ok {
|
||||
return NewDateTimeFromTime(t, p)
|
||||
}
|
||||
|
||||
return DateTimeNil()
|
||||
}
|
||||
|
||||
// Precision retourne la précision de l’heure.
|
||||
func (dt DateTime) Precision() Precision { return dt.p() }
|
||||
|
||||
// IsNil retourne vrai si la date est nulle.
|
||||
func (dt DateTime) IsNil() bool { return !dt.valid() }
|
||||
|
||||
// Year retourne l’année.
|
||||
func (dt DateTime) Year() int { return dt.y() }
|
||||
|
||||
// Month retourne le mois.
|
||||
func (dt DateTime) Month() uint { return dt.m() }
|
||||
|
||||
// Day retourne le .
|
||||
func (dt DateTime) Day() uint { return dt.d() }
|
||||
|
||||
// Hour retourne l’heure.
|
||||
func (dt DateTime) Hour() uint { return dt.h() }
|
||||
|
||||
// Minute retourne la minute.
|
||||
func (dt DateTime) Minute() uint { return dt.i() }
|
||||
|
||||
// Second retourne la seconde.
|
||||
func (dt DateTime) Second() uint { return dt.s() }
|
||||
|
||||
// Milli retourne la milliseconde.
|
||||
func (dt DateTime) Milli() uint { return dt.v() }
|
||||
|
||||
// Date retourne la partie date.
|
||||
func (dt DateTime) Date() (y int, m, d uint) { return dt.y(), dt.m(), dt.d() }
|
||||
|
||||
// Clock retourne la partie horaire.
|
||||
func (dt DateTime) Clock() (h, i, s, v uint) { return dt.h(), dt.i(), dt.s(), dt.m() }
|
||||
|
||||
// YearDay retourne le jour dans l’année (de 1 à 366).
|
||||
func (dt DateTime) YeardDay() uint { return dt.yd() }
|
||||
|
||||
// WeekDay retourne le jour dans la semaine (de 0 à 6 en commençant par dimanche).
|
||||
func (dt DateTime) WeekDay() uint { return dt.wd(dt.location) }
|
||||
|
||||
// IsBissextil retourne vrai si la date est dans une année bissextile.
|
||||
func (dt DateTime) IsBissextile() bool { return dt.b() }
|
||||
|
||||
// DaysInYear retourne le nombre de jours dans l’année.
|
||||
func (dt DateTime) DaysInYear() uint { return dt.yl() }
|
||||
|
||||
// DaysInMonth retourne le nombre de jours dans le mois.
|
||||
func (dt DateTime) DaysInMonth() uint { return dt.ml() }
|
||||
|
||||
// DayMinute retourne le nombre de minutes écoulées dans la journée.
|
||||
func (dt DateTime) DayMinute() uint { return dt.di() }
|
||||
|
||||
// DaySecond retourne le nombre de secondes écoulées dans la journée.
|
||||
func (dt DateTime) DaySecond() uint { return dt.ds() }
|
||||
|
||||
// DayMilli retourne le nombre de millisecondes écoulées dans la journée.
|
||||
func (dt DateTime) DayMilli() uint { return dt.dv() }
|
||||
|
||||
// ToDate convertit le temps en ne conservant que la partie date.
|
||||
func (dt DateTime) ToDate() Date { return newDate(dt.date, dt.location) }
|
||||
|
||||
// ToClock convertit le temps en ne conservant que la partie heure.
|
||||
func (dt DateTime) ToClock() Clock { return newClock(dt.clock, dt.location) }
|
||||
|
||||
// ToDateClock sépare la partie date et la partie heure.
|
||||
func (dt DateTime) ToDateClock() (Date, Clock) { return dt.ToDate(), dt.ToClock() }
|
||||
|
||||
// SetYear modifie l’année.
|
||||
func (dt DateTime) SetYear(y int, args ...uint) DateTime {
|
||||
return initDT(dt.setY(y, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetMonth modifie le mois.
|
||||
func (dt DateTime) SetMonth(m uint, args ...uint) DateTime {
|
||||
return initDT(dt.setM(m, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetDay modifie le jour.
|
||||
func (dt DateTime) SetDay(d uint, args ...uint) DateTime {
|
||||
return initDT(dt.setD(d, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetHour modifie l’heure.
|
||||
func (dt DateTime) SetHour(h uint, args ...uint) DateTime {
|
||||
return initDT(dt.setH(h, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetMinute modifie la minute.
|
||||
func (dt DateTime) SetMinute(i uint, args ...uint) DateTime {
|
||||
return initDT(dt.setI(i, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetSecond modifie la seconde.
|
||||
func (dt DateTime) SetSecond(s uint, args ...uint) DateTime {
|
||||
return initDT(dt.setS(s, args...), dt.location)
|
||||
}
|
||||
|
||||
// SetMilli modifie la milliseconde.
|
||||
func (dt DateTime) SetMilli(v uint) DateTime { return initDT(dt.setV(v), dt.location) }
|
||||
|
||||
// SetPrecision modifie la précision.
|
||||
func (dt DateTime) SetPrecision(p Precision) DateTime { return initDT(dt.setP(p), dt.location) }
|
||||
|
||||
// Add incrémente l’année, le mois, le jour, l’heure, la minute,
|
||||
// et facultativement la seconde et la milliseconde.
|
||||
func (dt DateTime) Add(y, m, d, h, i int, args ...int) DateTime {
|
||||
return initDT(dt.add(y, m, d, h, i, args...), dt.location)
|
||||
}
|
||||
|
||||
// AddDate incrémente l’année, le mois et le jour.
|
||||
func (dt DateTime) AddDate(y, m, d int) DateTime { return initDT(dt.addT(y, m, d), dt.location) }
|
||||
|
||||
// AddClock incrémente l’heure, la minute et facultativement la seconde et la milliseconde.
|
||||
func (dt DateTime) AddClock(h, i int, args ...int) DateTime {
|
||||
return initDT(dt.addC(h, i, args...), dt.location)
|
||||
}
|
||||
|
||||
// AddYear incrémente l’année.
|
||||
func (dt DateTime) AddYear(y int) DateTime { return initDT(dt.addY(y), dt.location) }
|
||||
|
||||
// AddMonth incrémente le mois.
|
||||
func (dt DateTime) AddMonth(m int) DateTime { return initDT(dt.addM(m), dt.location) }
|
||||
|
||||
// AddWeek incrémente la semaine.
|
||||
func (dt DateTime) AddWeek(w int) DateTime { return initDT(dt.addW(w), dt.location) }
|
||||
|
||||
// AddDay incrémente le jour.
|
||||
func (dt DateTime) AddDay(d int) DateTime { return initDT(dt.addD(d), dt.location) }
|
||||
|
||||
// AddHour incrémente l’heure.
|
||||
func (dt DateTime) AddHour(h int) DateTime { return initDT(dt.addH(h), dt.location) }
|
||||
|
||||
// AddMinute incrémente la minute.
|
||||
func (dt DateTime) AddMinute(i int) DateTime { return initDT(dt.addI(i), dt.location) }
|
||||
|
||||
// AddSecond incrémente la seconde.
|
||||
func (dt DateTime) AddSecond(s int) DateTime { return initDT(dt.addS(s), dt.location) }
|
||||
|
||||
// AddMilli incrémente la milliseconde.
|
||||
func (dt DateTime) AddMilli(v int) DateTime { return initDT(dt.addV(v), dt.location) }
|
||||
|
||||
// AddDuration incrémente une durée.
|
||||
func (dt DateTime) AddDuration(dd Duration) DateTime { return initDT(dt.addDD(dd), dt.location) }
|
||||
|
||||
// BeginOfYear retourne la date en début d’année.
|
||||
func (dt DateTime) BeginOfYear() DateTime { return initDT(dt.bY(), dt.location) }
|
||||
|
||||
// EndOfYear retourne la date en fin d’année.
|
||||
func (dt DateTime) EndOfYear() DateTime { return initDT(dt.eY(), dt.location) }
|
||||
|
||||
// BeginOfMonth retourne la date en début de mois.
|
||||
func (dt DateTime) BeginOfMonth() DateTime { return initDT(dt.bM(), dt.location) }
|
||||
|
||||
// EndOfMonth retourne la date en fin de mois.
|
||||
func (dt DateTime) EndOfMonth() DateTime { return initDT(dt.eM(), dt.location) }
|
||||
|
||||
// BeginOfWeek retourne la date en début de semaine.
|
||||
func (dt DateTime) BeginOfWeek() DateTime { return initDT(dt.bW(dt.location), dt.location) }
|
||||
|
||||
// EndOfWeek retourne la date en fin de semaine.
|
||||
func (dt DateTime) EndOfWeek() DateTime { return initDT(dt.eW(dt.location), dt.location) }
|
||||
|
||||
// BeginOfDay retourne la date en début de jour.
|
||||
func (dt DateTime) BeginOfDay() DateTime { return initDT(dt.bD(), dt.location) }
|
||||
|
||||
// EndOfDay retourne la date en fin de jour.
|
||||
func (dt DateTime) EndOfDay() DateTime { return initDT(dt.eD(), dt.location) }
|
||||
|
||||
// BeginOfHour retourne la date en début d’heure.
|
||||
func (dt DateTime) BeginOfHour() DateTime { return initDT(dt.bH(), dt.location) }
|
||||
|
||||
// EndOfHour retourne la date en fin d’heure.
|
||||
func (dt DateTime) EndOfHour() DateTime { return initDT(dt.eH(), dt.location) }
|
||||
|
||||
// BeginOfMinute retourne la date en début de minute.
|
||||
func (dt DateTime) BeginOfMinute() DateTime { return initDT(dt.bI(), dt.location) }
|
||||
|
||||
// EndOfMinute retourne la date en fin de minute.
|
||||
func (dt DateTime) EndOfMinute() DateTime { return initDT(dt.eI(), dt.location) }
|
||||
|
||||
// BeginOfSecond retourne la date en début de seconde.
|
||||
func (dt DateTime) BeginOfSecond() DateTime { return initDT(dt.bS(), dt.location) }
|
||||
|
||||
// EndOfSecond retourne la date en fin de seconde.
|
||||
func (dt DateTime) EndOfSecond() DateTime { return initDT(dt.eS(), dt.location) }
|
||||
|
||||
// ToTime retourne la date dans le fuseau horaire indiqué.
|
||||
func (dt DateTime) ToTime() time.Time { return dt.t(dt.location) }
|
||||
|
||||
// In retourne la date dans le fuseau horaire indiqué.
|
||||
func (dt DateTime) In(l *time.Location) DateTime {
|
||||
if dt.IsNil() {
|
||||
return DateTimeNil()
|
||||
}
|
||||
|
||||
return NewDateTimeFromTime(dt.ToTime().In(l), dt.p())
|
||||
}
|
||||
|
||||
// ToTimezone retourne la date dans le fuseau horaire indiqué.
|
||||
func (dt DateTime) ToTimezone(tz string) DateTime { return dt.In(timezone(tz)) }
|
||||
|
||||
// Location retourne le fuseau horaire.
|
||||
func (dt DateTime) Location() *time.Location { return dt.location }
|
||||
|
||||
// IsDST retourne vrai si le fuseau horaire est à l’heure d’été.
|
||||
func (dt DateTime) IsDST() bool { return dt.dst(dt.location) }
|
||||
|
||||
// Timestamp retourne le timestamp en secondes.
|
||||
func (dt DateTime) TimeStamp() int64 { return dt.ts(dt.location) }
|
||||
|
||||
// TimestampMilli retourne le timestamp en millisecondes.
|
||||
func (dt DateTime) TimeStampMilli() int64 { return dt.tv(dt.location) }
|
||||
|
||||
// Compare compare 2 dates.
|
||||
func (dt1 DateTime) Compare(dt2 DateTime) int { return dt1.cmp(dt2.datetime) }
|
||||
|
||||
func (dt1 DateTime) Eq(dt2 DateTime) bool { return dt1.Compare(dt2) == 0 }
|
||||
func (dt1 DateTime) Ne(dt2 DateTime) bool { return dt1.Compare(dt2) != 0 }
|
||||
func (dt1 DateTime) Gt(dt2 DateTime) bool { return dt1.Compare(dt2) > 0 }
|
||||
func (dt1 DateTime) Ge(dt2 DateTime) bool { return dt1.Compare(dt2) >= 0 }
|
||||
func (dt1 DateTime) Lt(dt2 DateTime) bool { return dt1.Compare(dt2) < 0 }
|
||||
func (dt1 DateTime) Le(dt2 DateTime) bool { return dt1.Compare(dt2) <= 0 }
|
||||
|
||||
func (dt DateTime) IsNow() bool { return dt.Eq(dt.Now()) }
|
||||
func (dt DateTime) IsPast() bool { return dt.Lt(dt.Now()) }
|
||||
func (dt DateTime) IsFuture() bool { return dt.Gt(dt.Now()) }
|
||||
|
||||
// DiffInMills retourne dt1-dt2 en millisecondes.
|
||||
func (dt1 DateTime) DiffInMillis(dt2 DateTime) int {
|
||||
return dt1.subV(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInSeconds retourne dt1 - dt2 en secondes.
|
||||
func (dt1 DateTime) DiffInSeconds(dt2 DateTime) int {
|
||||
return dt1.subS(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInMinutes retourne dt1 - dt2 en minutes.
|
||||
func (dt1 DateTime) DiffInMinutes(dt2 DateTime) int {
|
||||
return dt1.subI(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInHours retourne dt1 - dt2 en heures.
|
||||
func (dt1 DateTime) DiffInHours(dt2 DateTime) int {
|
||||
return dt1.subH(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInDays retourne dt1 - dt2 en jours.
|
||||
func (dt1 DateTime) DiffInDays(dt2 DateTime) int {
|
||||
return dt1.subD(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInWeeks retourne dt1 - dt2 en semaines.
|
||||
func (dt1 DateTime) DiffInWeeks(dt2 DateTime) int {
|
||||
return dt1.subW(dt2.datetime, dt1.location, dt2.location)
|
||||
}
|
||||
|
||||
// DiffInMonths retourne dt1 - dt2 en mois.
|
||||
func (dt1 DateTime) DiffInMonths(dt2 DateTime) int { return dt1.subM(dt2.datetime) }
|
||||
|
||||
// DiffInYears retourn dt1 - dt2 en années.
|
||||
func (dt1 DateTime) DiffInYears(dt2 DateTime) int { return dt1.subY(dt2.datetime) }
|
||||
|
||||
// Format retourne une représentation de la date au format spécifié.
|
||||
func (dt DateTime) Format(f string) string { return dt.f(f, dt.location) }
|
||||
|
||||
// String retourne une représentation de la date au format 'Y-m-d H:iT',
|
||||
// 'Y-m-d H:i:sT' ou 'Y-m-d H:i:s.vT' suivant la précision.
|
||||
func (dt DateTime) String() string { return dt.str(dt.location) }
|
|
@ -1,72 +1,55 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// DiffInYears retourne (t1 - t2) exprimé en années.
|
||||
func DiffInYears(t1, t2 time.Time) int64 {
|
||||
y1, m1, d1 := t1.Date()
|
||||
y2, m2, d2 := t2.Date()
|
||||
// Unit est une unité de durée.
|
||||
type Unit uint
|
||||
|
||||
dy, dm, dd := y1-y2, m1-m2, d1-d2
|
||||
if dm < 0 || (dm == 0 && dd < 0) {
|
||||
dy--
|
||||
}
|
||||
if dy < 0 && (dd != 0 || dm != 0) {
|
||||
dy++
|
||||
// Duration est une durée entre deux repères de temps.
|
||||
type Duration int
|
||||
|
||||
// NewDuration retourne une durée à partir d’une valeur et d’une unité.
|
||||
func NewDuration(value int, unit Unit) Duration {
|
||||
if unit == NoUnit {
|
||||
return DurationNil
|
||||
}
|
||||
|
||||
return int64(dy)
|
||||
return (Duration(value) << bitsUnit) | Duration(unit)
|
||||
}
|
||||
|
||||
// DiffInMonths retourne (t1 - t2) exprimé en mois.
|
||||
func DiffInMonths(t1, t2 time.Time) int64 {
|
||||
y1, m1, d1 := t1.Date()
|
||||
y2, m2, d2 := t2.Date()
|
||||
// Value retourne la valeur de la durée, sans l’unité.
|
||||
func (d Duration) Value() int { return int(d >> bitsUnit) }
|
||||
|
||||
dy, dm, dd := y1-y2, m1-m2, d1-d2
|
||||
if dd < 0 {
|
||||
dm--
|
||||
}
|
||||
if dy == 0 && dm == 0 {
|
||||
return int64(dm)
|
||||
}
|
||||
if dy == 0 && dm != 0 && dd != 0 {
|
||||
dh := abs(DiffInHours(t1, t2))
|
||||
if int(dh) < DaysInMonth(t1)*HoursPerDay {
|
||||
return int64(0)
|
||||
}
|
||||
return int64(dm)
|
||||
// Unit retourne l’unité de durée.
|
||||
func (d Duration) Unit() Unit { return Unit(d & maskUnit) }
|
||||
|
||||
// Duration retourne la valeur et l’unité de la durée.
|
||||
func (d Duration) Duration() (int, Unit) { return d.Value(), d.Unit() }
|
||||
|
||||
// IsNil retourne vrai si la durée ne représente pas une durée valide.
|
||||
func (d Duration) IsNil() bool { return d.Unit() == NoUnit }
|
||||
|
||||
// IsClock retourne vrai si la durée est dans une unité de temps sur 24h.
|
||||
func (d Duration) IsClock() bool { u := d.Unit(); return u > NoUnit && u < Day }
|
||||
|
||||
// IsDate retourne vrai si la durée est dans une unité de date.
|
||||
func (d Duration) IsDate() bool { return d.Unit() > Hour }
|
||||
|
||||
// Abs retourne la durée en valeur absolue.
|
||||
func (d Duration) Abs() Duration { return NewDuration(abs(d.Value()), d.Unit()) }
|
||||
|
||||
// Neg retourne l’inverse négatif de la durée.
|
||||
func (d Duration) Neg() Duration { return NewDuration(-d.Value(), d.Unit()) }
|
||||
|
||||
// String retourne la représentation textuelle de la durée.
|
||||
func (d Duration) String() string {
|
||||
if d.IsNil() {
|
||||
return "-"
|
||||
}
|
||||
|
||||
return int64(dy*MonthsPerYear + int(dm))
|
||||
}
|
||||
|
||||
// DiffInWeeks retourne (t1 - t2) exprimé en semaines.
|
||||
func DiffInWeeks(t1, t2 time.Time) int64 {
|
||||
return int64(math.Floor(float64(DiffInSeconds(t1, t2)) / float64(SecondsPerWeek)))
|
||||
}
|
||||
|
||||
// DiffInDays retourne (t1 - t2) exprimé en jours.
|
||||
func DiffInDays(t1, t2 time.Time) int64 {
|
||||
return int64(math.Floor(float64(DiffInSeconds(t1, t2)) / float64(SecondsPerDay)))
|
||||
}
|
||||
|
||||
// DiffInHours retourne (t1 - t2) exprimé en heures.
|
||||
func DiffInHours(t1, t2 time.Time) int64 {
|
||||
|
||||
return DiffInSeconds(t1, t2) / SecondsPerHour
|
||||
}
|
||||
|
||||
// DiffInMinutes retourne (t1 - t2) exprimé en minutes.
|
||||
func DiffInMinutes(t1, t2 time.Time) int64 {
|
||||
|
||||
return DiffInSeconds(t1, t2) / SecondsPerMinute
|
||||
}
|
||||
|
||||
// DiffInSeconds retourne (t1 - t2) exprimé en secondes.
|
||||
func DiffInSeconds(t1, t2 time.Time) int64 {
|
||||
return t1.Unix() - t2.Unix()
|
||||
v, u := d.Value(), d.Unit()
|
||||
|
||||
return fmt.Sprintf("%d%c", v, unitToByte[u])
|
||||
}
|
||||
|
|
|
@ -6,46 +6,13 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func abs[N ~int | ~int64](e N) N {
|
||||
if e < 0 {
|
||||
return -e
|
||||
}
|
||||
return e
|
||||
}
|
||||
func gmtDiff(t time.Time) (hour, minute int) {
|
||||
g := toTZ(t, "GMT")
|
||||
d0, h0, m0 := t.Day(), t.Hour(), t.Minute()
|
||||
d1, h1, m1 := g.Day(), g.Hour(), g.Minute()
|
||||
|
||||
func isYearBissextil(year int) bool { return year%4 == 0 && !(year%100 == 0 && year%400 != 0) }
|
||||
|
||||
func daysInMonth(year int, month time.Month) int {
|
||||
switch month {
|
||||
case time.February:
|
||||
if isYearBissextil(year) {
|
||||
return 29
|
||||
}
|
||||
return 28
|
||||
case time.April, time.June, time.September, time.November:
|
||||
return 30
|
||||
default:
|
||||
return 31
|
||||
}
|
||||
}
|
||||
|
||||
func internetHour(t time.Time) int {
|
||||
t, _ = ToTimezone(t, "CET").Ok()
|
||||
h, m, s := t.Clock()
|
||||
n := t.Nanosecond()
|
||||
d := time.Duration(n)*Nanosecond + time.Duration(s)*Second + time.Duration(m)*Minute + time.Duration(h)*Hour
|
||||
i := d * 1000 / Day
|
||||
return int(i)
|
||||
}
|
||||
|
||||
func gmtDiff(t time.Time) time.Duration {
|
||||
d0 := t.Day()
|
||||
h0, m0, _ := t.Clock()
|
||||
g, _ := ToTimezone(t, "GMT").Ok()
|
||||
d1 := g.Day()
|
||||
h1, m1, _ := t.Clock()
|
||||
|
||||
return time.Duration(d0-d1)*Day + time.Duration(h0-h1)*Hour + time.Duration(m0-m1)*Minute
|
||||
m := (d0-d1)*MinutePerDay + (h0-h1)*MinutePerHour + (m0 - m1)
|
||||
return m / MinutePerHour, m % MillisecondPerHour
|
||||
}
|
||||
|
||||
// Jour
|
||||
|
@ -68,32 +35,27 @@ func format_n(t time.Time) string { return fmt.Sprintf("%d", t.Month()+1) }
|
|||
func format_m(t time.Time) string { return fmt.Sprintf("%02d", t.Month()+1) }
|
||||
func format_M(t time.Time) string { return shortMonths[t.Month()] }
|
||||
func format_F(t time.Time) string { return longMonths[t.Month()] }
|
||||
func format_t(t time.Time) string { return fmt.Sprintf("%d", daysInMonth(t.Year(), t.Month())) }
|
||||
func format_t(t time.Time) string { return fmt.Sprintf("%d", monthLen(t.Year(), uint(t.Month())+1)) }
|
||||
|
||||
// Année
|
||||
func format_y(t time.Time) string { return fmt.Sprintf("%02d", t.Year()%100) }
|
||||
func format_Y(t time.Time) string { return fmt.Sprintf("%d", t.Year()) }
|
||||
func format_L(t time.Time) string {
|
||||
if isYearBissextil(t.Year()) {
|
||||
if bissextil(t.Year()) {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
// Heure
|
||||
func format_a(t time.Time) string {
|
||||
if t.Hour() < 12 {
|
||||
return "am"
|
||||
}
|
||||
return "pm"
|
||||
func format_a(t time.Time) string { return amPmLow[t.Hour() < 12] }
|
||||
func format_A(t time.Time) string { return amPmUp[t.Hour() < 12] }
|
||||
func format_B(t time.Time) string {
|
||||
h, m, s := t.Clock()
|
||||
n := t.Nanosecond() / NanosecondPerMillisecond
|
||||
|
||||
return fmt.Sprintf("%03d", sit(uint(h), uint(m), uint(s), uint(n)))
|
||||
}
|
||||
func format_A(t time.Time) string {
|
||||
if t.Hour() < 12 {
|
||||
return "AM"
|
||||
}
|
||||
return "PM"
|
||||
}
|
||||
func format_B(t time.Time) string { return fmt.Sprintf("%03d", internetHour(t)) }
|
||||
func format_g(t time.Time) string {
|
||||
h := t.Hour() % 12
|
||||
if h == 0 {
|
||||
|
@ -112,8 +74,12 @@ func format_h(t time.Time) string {
|
|||
func format_H(t time.Time) string { return fmt.Sprintf("%02d", t.Hour()) }
|
||||
func format_i(t time.Time) string { return fmt.Sprintf("%02d", t.Minute()) }
|
||||
func format_s(t time.Time) string { return fmt.Sprintf("%02d", t.Second()) }
|
||||
func format_v(t time.Time) string { return fmt.Sprintf("%03d", t.Nanosecond()/1000000) }
|
||||
func format_u(t time.Time) string { return fmt.Sprintf("%06d", t.Nanosecond()/1000) }
|
||||
func format_v(t time.Time) string {
|
||||
return fmt.Sprintf("%03d", t.Nanosecond()/NanosecondPerMillisecond)
|
||||
}
|
||||
func format_u(t time.Time) string {
|
||||
return fmt.Sprintf("%06d", t.Nanosecond()/NanosecondPerMicrosecond)
|
||||
}
|
||||
|
||||
// Fuseau horaire
|
||||
func format_T(t time.Time) string {
|
||||
|
@ -121,12 +87,12 @@ func format_T(t time.Time) string {
|
|||
if !strings.Contains(name, "/") {
|
||||
return name
|
||||
}
|
||||
diff := gmtDiff(t)
|
||||
h, m := diff/Hour, (diff%Hour)/Minute
|
||||
h, m := gmtDiff(t)
|
||||
s := "+"
|
||||
if h < 0 {
|
||||
s = "-"
|
||||
}
|
||||
|
||||
if m == 0 {
|
||||
return fmt.Sprintf("%s%02d", s, abs(h))
|
||||
}
|
||||
|
@ -140,33 +106,37 @@ func format_I(t time.Time) string {
|
|||
return "0"
|
||||
}
|
||||
func format_O(t time.Time) string {
|
||||
diff := gmtDiff(t)
|
||||
h, m := diff/Hour, (diff%Hour)/Minute
|
||||
h, m := gmtDiff(t)
|
||||
s := "+"
|
||||
if h < 0 {
|
||||
s = "-"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%02d%02d", s, abs(h), abs(m))
|
||||
}
|
||||
func format_P(t time.Time) string {
|
||||
diff := gmtDiff(t)
|
||||
h, m := diff/Hour, (diff%Hour)/Minute
|
||||
h, m := gmtDiff(t)
|
||||
s := "+"
|
||||
if h < 0 {
|
||||
s = "-"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%02d:%02d", s, abs(h), abs(m))
|
||||
}
|
||||
func format_p(t time.Time) string {
|
||||
diff := gmtDiff(t)
|
||||
h, m := gmtDiff(t)
|
||||
diff := h*MinutePerHour + m
|
||||
if diff == 0 {
|
||||
return "Z"
|
||||
}
|
||||
|
||||
return format_P(t)
|
||||
}
|
||||
func format_Z(t time.Time) string {
|
||||
diff := gmtDiff(t)
|
||||
return fmt.Sprintf("%d", diff/Second)
|
||||
h, m := gmtDiff(t)
|
||||
diff := h*SecondPerHour + m*SecondPerMinute
|
||||
|
||||
return fmt.Sprintf("%d", diff)
|
||||
}
|
||||
|
||||
// Date et heure complète
|
||||
|
@ -180,11 +150,7 @@ func format_r(t time.Time) string {
|
|||
}
|
||||
func format_U(t time.Time) string { return fmt.Sprintf("%d", t.Unix()) }
|
||||
|
||||
// Format retourne la représentation de la date dans le format indiqué.
|
||||
// Pour connaître toutes les options possibles pour le format, veullez consulter
|
||||
// https://www.php.net/manual/fr/datetime.format.php
|
||||
// (ou le fichier const.go).
|
||||
func Format(t time.Time, format string) string {
|
||||
func formatT(t time.Time, format string) string {
|
||||
var buffer strings.Builder
|
||||
|
||||
runes := []rune(format)
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DaysInMonth retourne le nombre de jours dans le mois.
|
||||
func DaysInMonth(t time.Time) int { return daysInMonth(t.Year(), t.Month()) }
|
||||
|
||||
// DaysInYear retourne le nombe de jours dans l’année.
|
||||
func DaysInYear(t time.Time) int {
|
||||
if IsBissextil(t) {
|
||||
return 366
|
||||
}
|
||||
return 365
|
||||
}
|
||||
|
||||
// IsBissextil retourne vrai si la date est située dans une année bissextile.
|
||||
func IsBissextil(t time.Time) bool { return isYearBissextil(t.Year()) }
|
||||
|
||||
// IsWeekend retourne vrai si la date est située dans le weekend.
|
||||
func IsWeekend(t time.Time) bool {
|
||||
d := t.Weekday()
|
||||
|
||||
return d != time.Saturday && d != time.Sunday
|
||||
}
|
||||
|
||||
// IsBeginOfMonth retourne vrai si la date est dans le premier jour du mois.
|
||||
func IsBeginOfMonth(t time.Time) bool { return t.Day() == 1 }
|
||||
|
||||
// IsEndOfMonth retourne vrai si la date est dans le dernier jour du mois.
|
||||
func IsEndOfMonth(t time.Time) bool { return t.Day() == DaysInMonth(t) }
|
||||
|
||||
// IsBeginOfYear retourne vrai si la date est dans le premier jour de l’année.
|
||||
func IsBeginOfYear(t time.Time) bool { return IsBeginOfMonth(t) && t.Month() == time.January }
|
||||
|
||||
// IsEndOfYear retourne vrai si la date est dans le dernier jour de l’année.
|
||||
func IsEndOfYear(t time.Time) bool { return IsEndOfMonth(t) && t.Month() == time.December }
|
||||
|
||||
// IsToday retourne vrai si la date est située aujourd’hui.
|
||||
func IsToday(t time.Time) bool {
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := time.Now().In(t.Location()).Date()
|
||||
|
||||
return y == y0 && m == m0 && d == d0
|
||||
}
|
||||
|
||||
// IsTomorrow retourne vrai si la date est située demain.
|
||||
func IsTomorrow(t time.Time) bool {
|
||||
n := time.Now().In(t.Location())
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := n.Date()
|
||||
|
||||
if d > 1 {
|
||||
return y == y0 && m == m0 && d == d0+1
|
||||
}
|
||||
|
||||
if m > time.January {
|
||||
return y == y0 && m == m0+1 && IsEndOfMonth(n)
|
||||
}
|
||||
|
||||
return y == y0+1 && IsEndOfYear(n)
|
||||
}
|
||||
|
||||
// IsYesterday retourne vrai si la date est située hier.
|
||||
func IsYesterday(t time.Time) bool {
|
||||
n := time.Now().In(t.Location())
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := n.Date()
|
||||
|
||||
if d < DaysInMonth(t) {
|
||||
return y == y0 && m == m0 && d == d0-1
|
||||
}
|
||||
|
||||
if m < time.December {
|
||||
return y == y0 && m == m0-1 && IsBeginOfMonth(n)
|
||||
}
|
||||
|
||||
return y == y0-1 && IsBeginOfYear(n)
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
func addUnit[I int | int64](t time.Time, duration I, unit time.Duration) time.Time {
|
||||
return t.Add(time.Duration(duration) * unit)
|
||||
}
|
||||
|
||||
// AddNanoseconds ajoute d ns à la date.
|
||||
func AddNanoseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Nanosecond)
|
||||
}
|
||||
|
||||
// AddMicroseconds ajoute d μs à la date.
|
||||
func AddMicroseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Microsecond)
|
||||
}
|
||||
|
||||
// AddMilliseconds ajoute d ms à la date.
|
||||
func AddMilliseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Millisecond)
|
||||
}
|
||||
|
||||
// AddSeconds ajoute d s à la date.
|
||||
func AddSeconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Second)
|
||||
}
|
||||
|
||||
// AddMinutes ajoute d min à la date.
|
||||
func AddMinutes[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Minute)
|
||||
}
|
||||
|
||||
// AddHours ajoute d heures à la date.
|
||||
func AddHours[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Hour)
|
||||
}
|
||||
|
||||
// AddDays ajoute d jours à la date.
|
||||
func AddDays[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Day)
|
||||
}
|
||||
|
||||
// AddWeeks ajoute d semaines à la date.
|
||||
func AddWeeks[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Week)
|
||||
}
|
||||
|
||||
// AddMonths ajoute d mois à la date.
|
||||
func AddMonths[I int | int64](t time.Time, d I) time.Time {
|
||||
return t.AddDate(0, int(d), 0)
|
||||
}
|
||||
|
||||
// AddYears ajoute d années à la date.
|
||||
func AddYears[I int | int64](t time.Time, d I) time.Time {
|
||||
return t.AddDate(int(d), 0, 0)
|
||||
}
|
||||
|
||||
// AddDecades ajoute d×10 années à la date.
|
||||
func AddDecades[I int | int64](t time.Time, d I) time.Time {
|
||||
return AddYears(t, 10*d)
|
||||
}
|
||||
|
||||
// AddCenturies ajoute d siècles à la date.
|
||||
func AddCenturies[I int | int64](t time.Time, d I) time.Time {
|
||||
return AddYears(t, 100*d)
|
||||
}
|
||||
|
||||
func getTZ(tz ...string) (result Option[*time.Location]) {
|
||||
if len(tz) > 0 {
|
||||
location, err := time.LoadLocation(tz[0])
|
||||
if err == nil {
|
||||
return Some(location)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return Some(DefaultTZ)
|
||||
}
|
||||
|
||||
func toTZ(t time.Time, tz ...string) Result[time.Time] {
|
||||
if l, ok := getTZ(tz...).Get(); ok {
|
||||
return Ok(t.In(l))
|
||||
}
|
||||
|
||||
if len(tz) == 0 {
|
||||
return Ok(t.In(DefaultTZ))
|
||||
}
|
||||
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz))
|
||||
}
|
||||
|
||||
// ToTimezone retourne la date dans le fuseau horaire indiqué.
|
||||
func ToTimezone(t time.Time, tz string) Result[time.Time] {
|
||||
return toTZ(t, tz)
|
||||
}
|
||||
|
||||
// Local retourne la date dans le fuseau horaire local.
|
||||
func Local(t time.Time) time.Time {
|
||||
return t.In(time.Local)
|
||||
}
|
||||
|
||||
// UTC retourne la date dans le fuseau UTC.
|
||||
func UTC(t time.Time) time.Time {
|
||||
return t.In(time.UTC)
|
||||
}
|
||||
|
||||
// Date modifie la date avec l’année, le mois et le jour fournis.
|
||||
func Date(t time.Time, year, month, day int) time.Time {
|
||||
h, m, s := t.Clock()
|
||||
e := t.Nanosecond()
|
||||
l := t.Location()
|
||||
|
||||
return time.Date(year, time.Month(month-1), day, h, m, s, e, l)
|
||||
}
|
||||
|
||||
// Clock modifie l’heure de la date.
|
||||
func Clock(t time.Time, hour, minute, second int, nano ...int) time.Time {
|
||||
y, m, d := t.Date()
|
||||
l := t.Location()
|
||||
e := 0
|
||||
|
||||
if len(nano) > 0 {
|
||||
e = nano[0]
|
||||
}
|
||||
|
||||
return time.Date(y, m, d, hour, minute, second, e, l)
|
||||
}
|
||||
|
||||
// SetNano modifie les nanosecondes de la date.
|
||||
func SetNano(t time.Time, nano int) time.Time {
|
||||
h, m, s := t.Clock()
|
||||
|
||||
return Clock(t, h, m, s, nano)
|
||||
}
|
||||
|
||||
// SetSecond modifie la seconde de la date (et éventuellement la ns).
|
||||
func SetSecond(t time.Time, second int, nano ...int) time.Time {
|
||||
h, m := t.Hour(), t.Minute()
|
||||
e := t.Nanosecond()
|
||||
if len(nano) > 0 {
|
||||
e = nano[0]
|
||||
}
|
||||
|
||||
return Clock(t, h, m, second, e)
|
||||
}
|
||||
|
||||
// SetMinute modifie la minute de la date (et éventuellement la s et la ns).
|
||||
func SetMinute(t time.Time, minute int, other ...int) time.Time {
|
||||
h, s, e := t.Hour(), t.Second(), t.Nanosecond()
|
||||
if len(other) > 0 {
|
||||
s = other[0]
|
||||
if len(other) > 1 {
|
||||
e = other[1]
|
||||
}
|
||||
}
|
||||
|
||||
return Clock(t, h, minute, s, e)
|
||||
}
|
||||
|
||||
// SetHour modifie l’heure de la date (et éventuellement la min, s & ns).
|
||||
func SetHour(t time.Time, hour int, other ...int) time.Time {
|
||||
m, s, e := t.Minute(), t.Second(), t.Nanosecond()
|
||||
if len(other) > 0 {
|
||||
m = other[0]
|
||||
if len(other) > 1 {
|
||||
s = other[1]
|
||||
if len(other) > 2 {
|
||||
e = other[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Clock(t, hour, m, s, e)
|
||||
}
|
||||
|
||||
// SetDay modifie le jour de la date.
|
||||
func SetDay(t time.Time, day int) time.Time {
|
||||
y, m := t.Year(), t.Month()
|
||||
|
||||
return Date(t, y, int(m+1), day)
|
||||
}
|
||||
|
||||
// SetMonth modifie le mois de la date (et éventuellement le jour).
|
||||
func SetMonth(t time.Time, month int, day ...int) time.Time {
|
||||
y, d := t.Year(), t.Day()
|
||||
if len(day) > 0 {
|
||||
d = day[0]
|
||||
}
|
||||
|
||||
return Date(t, y, month, d)
|
||||
}
|
||||
|
||||
// SetYear modifie l’année de la date (et éventuellement le mois et le jour).
|
||||
func SetYear(t time.Time, year int, other ...int) time.Time {
|
||||
m, d := int(t.Month()+1), t.Day()
|
||||
if len(other) > 0 {
|
||||
m = other[0]
|
||||
if len(other) > 1 {
|
||||
d = other[1]
|
||||
}
|
||||
}
|
||||
|
||||
return Date(t, year, m, d)
|
||||
}
|
||||
|
||||
// BeginOfSecond retourne la date au début de la seconde en cours.
|
||||
func BeginOfSecond(t time.Time) time.Time { return SetNano(t, 0) }
|
||||
|
||||
// EndOfSecond retourne la date à la fin de la seconde en cours.
|
||||
func EndOfSecond(t time.Time) time.Time { return SetNano(t, 999999999) }
|
||||
|
||||
// BeginOfMinute retourne la date au début de la minute en cours.
|
||||
func BeginOfMinute(t time.Time) time.Time { return SetSecond(t, 0, 0) }
|
||||
|
||||
// EndOfMinute retourne la date à la fin de la minute en cours.
|
||||
func EndOfMinute(t time.Time) time.Time { return SetSecond(t, 59, 999999999) }
|
||||
|
||||
// BeginOfHour retourne la date au début de l’heure en cours.
|
||||
func BeginOfHour(t time.Time) time.Time { return SetMinute(t, 0, 0, 0) }
|
||||
|
||||
// EndOfHour retourne la date à la fin de l’heure en cours.
|
||||
func EndOfHour(t time.Time) time.Time { return SetMinute(t, 59, 59, 999999999) }
|
||||
|
||||
// BeginOfDay retourne la date au début du jour en cours.
|
||||
func BeginOfDay(t time.Time) time.Time { return SetHour(t, 0, 0, 0, 0) }
|
||||
|
||||
// EndOfDay retourne la date à la fin du jour en cours.
|
||||
func EndOfDay(t time.Time) time.Time { return SetHour(t, 23, 59, 59, 999999999) }
|
||||
|
||||
// BeginOfWeek retourne la date au début de la semaine en cours.
|
||||
func BeginOfWeek(t time.Time) time.Time {
|
||||
d := t.Weekday()
|
||||
if d == time.Sunday {
|
||||
d += 7
|
||||
}
|
||||
|
||||
return BeginOfDay(t.AddDate(0, 0, 1-int(d)))
|
||||
}
|
||||
|
||||
// EndOfWeek retourne la date à la fin de la semaine en cours.
|
||||
func EndOfWeek(t time.Time) time.Time {
|
||||
d := t.Weekday()
|
||||
if d == time.Sunday {
|
||||
d += 7
|
||||
}
|
||||
|
||||
return EndOfDay(t.AddDate(0, 0, 7-int(d)))
|
||||
}
|
||||
|
||||
// BeginOfMonth retourne la date au début du mois en cours.
|
||||
func BeginOfMonth(t time.Time) time.Time { return BeginOfDay(SetDay(t, 1)) }
|
||||
|
||||
// EndOfMonth retourne la date à la fin du mois en cours.
|
||||
func EndOfMonth(t time.Time) time.Time { return EndOfDay(SetDay(t, DaysInMonth(t))) }
|
||||
|
||||
// BeginOfYear retourne la date au début de l’année en cours.
|
||||
func BeginOfYear(t time.Time) time.Time { return BeginOfDay(SetMonth(t, 1, 1)) }
|
||||
|
||||
// EndOfYear retourne la date à la fin de l’année en cours.
|
||||
func EndOfYear(t time.Time) time.Time { return EndOfDay(SetMonth(t, 12, 31)) }
|
|
@ -1,23 +1,25 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
func isValueZero(e string) bool {
|
||||
return e == "" || e == "0" || e == "0000-00-00 00:00:00" || e == "0000-00-00" || e == "00:00:00"
|
||||
}
|
||||
|
||||
func format2layout(f string) string {
|
||||
var buffer strings.Builder
|
||||
|
||||
runes := []rune(f)
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if layout, ok := parsers[runes[i]]; ok {
|
||||
buffer.WriteString(layout)
|
||||
} else {
|
||||
switch runes[i] {
|
||||
case '\\': // raw output, no parse
|
||||
case '\\': // Indique que le caractère suivant ne doit pas être parsé
|
||||
buffer.WriteRune(runes[i+1])
|
||||
i++
|
||||
continue
|
||||
|
@ -26,70 +28,43 @@ func format2layout(f string) string {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func parseInLocation(value, layout string, tz ...string) Result[time.Time] {
|
||||
location := DefaultTZ
|
||||
if len(tz) > 0 {
|
||||
var err error
|
||||
if location, err = time.LoadLocation(tz[0]); err != nil {
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz[0]))
|
||||
}
|
||||
func parseInLocation(e, f string, tz ...string) (t Option[time.Time]) {
|
||||
l := timezone(tz...)
|
||||
if tt, err := time.ParseInLocation(f, e, l); err == nil {
|
||||
t = Some(tt)
|
||||
}
|
||||
|
||||
t, err := time.ParseInLocation(layout, value, location)
|
||||
if err == nil {
|
||||
return Ok(t)
|
||||
}
|
||||
return Err[time.Time](fmt.Errorf(errInvalidValue, value))
|
||||
return
|
||||
}
|
||||
|
||||
// Guess tente de parser la chaîne de caractères en date en essayant de deviner le format.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func Guess(value string, tz ...string) Result[time.Time] {
|
||||
if value == "" || value == "0" || value == "0000-00-00 00:00:00" || value == "0000-00-00" || value == "00:00:00" {
|
||||
return Zero(tz...)
|
||||
func parseFromLayout(e, f string, tz ...string) (t Option[time.Time]) {
|
||||
if isValueZero(e) {
|
||||
return
|
||||
}
|
||||
return parseInLocation(e, f, tz...)
|
||||
}
|
||||
|
||||
switch value {
|
||||
func guess(e string, tz ...string) (t Option[time.Time]) {
|
||||
if isValueZero(e) {
|
||||
return
|
||||
}
|
||||
switch e {
|
||||
case "now":
|
||||
return Now(tz...)
|
||||
return Some(now(tz...))
|
||||
case "yesterday":
|
||||
return Yesterday(tz...)
|
||||
return Some(yesterday(tz...))
|
||||
case "tomorrow":
|
||||
return Tomorrow(tz...)
|
||||
return Some(tomorrow(tz...))
|
||||
}
|
||||
|
||||
if len(tz) > 0 {
|
||||
if _, err := time.LoadLocation(tz[0]); err != nil {
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz[0]))
|
||||
for _, l := range layouts {
|
||||
if t = parseInLocation(e, l, tz...); t.IsDefined() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, layout := range layouts {
|
||||
t := parseInLocation(layout, value, tz...)
|
||||
if t.IsOk() {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return Err[time.Time](fmt.Errorf(errInvalidValue, value))
|
||||
return
|
||||
}
|
||||
|
||||
// ParseFromLayout parse la chaîne de caractères en date à partir du layout (façon Go) fourni.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func ParseFromLayout(value, layout string, tz ...string) Result[time.Time] {
|
||||
if value == "" || value == "0" || value == "0000-00-00 00:00:00" || value == "0000-00-00" || value == "00:00:00" {
|
||||
return Zero(tz...)
|
||||
}
|
||||
|
||||
return parseInLocation(value, layout, tz...)
|
||||
}
|
||||
|
||||
// Parse parse la chaîne de caractères en date à partir du format (dans le style PHP) fourni.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func Parse(value, format string, tz ...string) Result[time.Time] {
|
||||
return ParseFromLayout(value, format2layout(format), tz...)
|
||||
func parse(e, f string, tz ...string) Option[time.Time] {
|
||||
return parseFromLayout(e, format2layout(f), tz...)
|
||||
}
|
||||
|
|
|
@ -1,119 +1,155 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
// Range représente une période entre deux dates.
|
||||
type Range struct {
|
||||
begin time.Time
|
||||
end time.Time
|
||||
// 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
|
||||
}
|
||||
|
||||
// NewRange initialise une période.
|
||||
// Si t1 > t2, la date début de la période sera t2, et inversement si t1 < t2.
|
||||
func NewRange(t1, t2 time.Time) Range {
|
||||
if Gt(t1, t2) {
|
||||
t1, t2 = t2, t1
|
||||
}
|
||||
|
||||
return Range{
|
||||
begin: t1,
|
||||
end: t2,
|
||||
// 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
|
||||
}
|
||||
|
||||
// Begin retourne la date de début de la période.
|
||||
func (r Range) Begin() time.Time { return r.begin }
|
||||
// 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
|
||||
}
|
||||
|
||||
// End retourne la date de fin de la période.
|
||||
func (r Range) End() time.Time { return r.end }
|
||||
// Range représente une période entre deux bornes temporelles.
|
||||
type Range[C TimeComparator[C]] struct {
|
||||
begin, end C
|
||||
}
|
||||
|
||||
// BeforeDate retourne vrai si la date est située avant la période.
|
||||
func (r Range) BeforeDate(t time.Time) bool { return Lt(r.end, t) }
|
||||
// NewRange retourne une période entre begin et end.
|
||||
// Si begin < end, les bornes sont inversées.
|
||||
func NewRange[C TimeComparator[C]](begin, end C) Range[C] {
|
||||
if begin.Gt(end) {
|
||||
begin, end = end, begin
|
||||
}
|
||||
|
||||
// AfterDate retourne vrai si la date est située après la période.
|
||||
func (r Range) AfterDate(t time.Time) bool { return Gt(r.begin, t) }
|
||||
return Range[C]{begin, end}
|
||||
}
|
||||
|
||||
// ContainsDate retourne vrai si t ∈ [begin; end].
|
||||
func (r Range) ContainsDate(t time.Time) bool { return Le(r.begin, t) && Ge(r.end, t) }
|
||||
// Begin retourne le début de la période.
|
||||
func (r Range[C]) Begin() C { return r.begin }
|
||||
|
||||
// ContainsDateStrictBegin retourne vrai si t ∈ ]begin; end].
|
||||
func (r Range) ContainsDateStrictBegin(t time.Time) bool { return Lt(r.begin, t) && Ge(r.end, t) }
|
||||
// End retourne la fin de la période.
|
||||
func (r Range[C]) End() C { return r.end }
|
||||
|
||||
// ContainsDateStrictEnd retourne vrai si t ∈ [begin; end[.
|
||||
func (r Range) ContainsDateStrictEnd(t time.Time) bool { return Le(r.begin, t) && Gt(r.end, t) }
|
||||
// Before retourne vrai si e est située avant la période.
|
||||
func (r Range[C]) Before(e C) bool { return e.Lt(r.begin) }
|
||||
|
||||
// ContainsDateStrict retourne vrai si t ∈ ]begin; end[.
|
||||
func (r Range) ContainsDateStrict(t time.Time) bool { return Lt(r.begin, t) && Gt(r.end, t) }
|
||||
// After retourne vrai si e est située après la période.
|
||||
func (r Range[C]) After(e C) bool { return e.Gt(r.end) }
|
||||
|
||||
// IsFuture retourne vrai si la période est située dans le futur.
|
||||
func (r Range) IsFuture() bool { return r.AfterDate(time.Now()) }
|
||||
// Contains retourne vrai si e ∈ [begin;end].
|
||||
func (r Range[C]) Contains(e C) bool { return e.Ge(r.begin) && e.Le(r.end) }
|
||||
|
||||
// IsPast retourne vrai si la période est située dans le passé.
|
||||
func (r Range) IsPast() bool { return r.BeforeDate(time.Now()) }
|
||||
// ContainsStrictLeft retourne vrai si e ∈ ]begin;end].
|
||||
func (r Range[C]) ContainsStrictLeft(e C) bool { return e.Gt(r.begin) && e.Le(r.end) }
|
||||
|
||||
// ContainsStrictRight retourne vrai si e ∈ [begin;end[.
|
||||
func (r Range[C]) ContainsStrictRight(e C) bool { return e.Ge(r.begin) && e.Lt(r.end) }
|
||||
|
||||
// ContainsStrict retourne vrai si e ∈ ]begin;end[.
|
||||
func (r Range[C]) ContainsStrict(e C) bool { return e.Gt(r.begin) && e.Lt(r.end) }
|
||||
|
||||
// IsNow retourne vrai si la période est en cours.
|
||||
func (r Range) IsNow() bool { return r.ContainsDate(time.Now()) }
|
||||
func (r Range[C]) IsNow() bool { return r.Contains(r.begin.Now()) }
|
||||
|
||||
// Before retourne vrai si r est avant r2 sans la recouvrir.
|
||||
func (r Range) Before(r2 Range) bool { return Le(r.end, r2.begin) }
|
||||
// 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()) }
|
||||
|
||||
// After retourne vrai si r est après r2 sans la recouvrir.
|
||||
func (r Range) After(r2 Range) bool { return Ge(r.begin, r2.end) }
|
||||
// IsFuture retourne vrai la période est située dans le futur.
|
||||
func (r Range[C]) IsFuture() bool { return r.After(r.begin.Now()) }
|
||||
|
||||
// Contains retourne vrai si r inclut complètement r2.
|
||||
func (r Range) Contains(r2 Range) bool { return Le(r.begin, r2.begin) && Ge(r.end, r2.end) }
|
||||
// BeforeRange retourne vrai si r1 est terminée avant que r2 commence.
|
||||
func (r1 Range[C]) BeforeRange(r2 Range[C]) bool { return r1.end.Le(r2.begin) }
|
||||
|
||||
// In retourne vrai si r est complètement inclus dans r2.
|
||||
func (r Range) In(r2 Range) bool { return r2.Contains(r) }
|
||||
// AfterRange retourne vrai si r2 est terminée avec que r1 commence.
|
||||
func (r1 Range[C]) AfterRange(r2 Range[C]) bool { return r2.BeforeRange(r1) }
|
||||
|
||||
// Excludes retourne vrai si les périodes ne se chevauchent pas.
|
||||
func (r Range) Excludes(r2 Range) bool { return Le(r.end, r2.begin) || Ge(r.begin, r2.end) }
|
||||
// ContainsRange retourne vrai si r2 est intégralement comprise dans r1.
|
||||
func (r1 Range[C]) ContainsRange(r2 Range[C]) bool {
|
||||
return r1.begin.Le(r2.begin) && r1.end.Ge(r2.end)
|
||||
}
|
||||
|
||||
// Overlaps retourne vrai si les périodes se chevauchent.
|
||||
func (r Range) Overlaps(r2 Range) bool { return Lt(r.begin, r2.end) && Gt(r.end, r2.begin) }
|
||||
// InRange retourne vrai si r2 comprend intégralement r1.
|
||||
func (r1 Range[C]) InRange(r2 Range[C]) bool { return r2.ContainsRange(r1) }
|
||||
|
||||
// Intersection retourne la période commune aux deux périodes, si elle existe.
|
||||
func (r Range) Intersection(r2 Range) (result Option[Range]) {
|
||||
if r.Overlaps(r2) {
|
||||
result = Some(NewRange(Max(r.begin, r2.begin), Min(r.end, r2.end)))
|
||||
// Excludes retourne vrai si r1 et r2 ne se chevauchent pas.
|
||||
func (r1 Range[C]) Excludes(r2 Range[C]) bool {
|
||||
return r1.end.Le(r2.begin) || r1.begin.Ge(r2.end)
|
||||
}
|
||||
|
||||
// Overlaps retourne vrai si r1 et r2 se chevauchent.
|
||||
func (r1 Range[C]) Overlaps(r2 Range[C]) bool {
|
||||
return r1.begin.Lt(r2.end) && r1.end.Gt(r2.begin)
|
||||
}
|
||||
|
||||
// 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(Max(r1.begin, r2.begin), Min(r1.end, r2.end)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Joins retourne la plus grande période contiguë entre deux période, si elle existe.
|
||||
func (r Range) Joins(r2 Range) (result Option[Range]) {
|
||||
if Le(r.begin, r2.end) && Ge(r.end, r2.begin) {
|
||||
result = Some(NewRange(Min(r.begin, r2.begin), Max(r.end, r2.end)))
|
||||
// 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.Le(r2.end) && r1.end.Ge(r2.begin) {
|
||||
result = Some(NewRange(Min(r1.begin, r2.begin), Max(r1.end, r2.end)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Diff retourne l’ensemble des périodes non communes aux deux périodes.
|
||||
func (r Range) Diff(r2 Range) (result []Range) {
|
||||
if Ne(r.begin, r2.begin) {
|
||||
begin := Min(r.begin, r2.begin)
|
||||
var end time.Time
|
||||
if begin == r.begin {
|
||||
end = Min(r.end, r2.begin)
|
||||
// Diff retourne la liste des périodes qui ne se chevauchent pas.
|
||||
func (r1 Range[C]) Diff(r2 Range[C]) (result []Range[C]) {
|
||||
if r1.begin.Ne(r2.begin) {
|
||||
begin := Min(r1.begin, r2.begin)
|
||||
var end C
|
||||
if begin.Eq(r1.begin) {
|
||||
end = Min(r1.end, r2.begin)
|
||||
} else {
|
||||
end = Min(r2.end, r.begin)
|
||||
end = Min(r2.end, r1.begin)
|
||||
}
|
||||
result = append(result, NewRange(begin, end))
|
||||
}
|
||||
|
||||
if Ne(r.end, r2.end) {
|
||||
end := Max(r.end, r2.end)
|
||||
var begin time.Time
|
||||
if end == r.end {
|
||||
begin = Max(r.begin, r2.end)
|
||||
if r1.end.Ne(r2.end) {
|
||||
end := Max(r1.end, r2.end)
|
||||
var begin C
|
||||
if end.Eq(r1.end) {
|
||||
begin = Max(r1.begin, r2.end)
|
||||
} else {
|
||||
begin = Max(r2.begin, r.end)
|
||||
begin = Max(r2.begin, r1.end)
|
||||
}
|
||||
result = append(result, NewRange(begin, end))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type integer interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
type uinteger interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
||||
}
|
||||
|
||||
type float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
type number interface {
|
||||
integer | uinteger | float
|
||||
}
|
||||
|
||||
func cmp[N number](n1, n2 N) int {
|
||||
switch {
|
||||
case n1 < n2:
|
||||
return -1
|
||||
case n1 == n2:
|
||||
return 0
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
func abs[N integer | float](n N) N {
|
||||
if n < 0 {
|
||||
return -n
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func spl[N integer | uinteger](e []N, n int) (ed, ec []N) {
|
||||
ed = e
|
||||
if len(e) > n {
|
||||
ed, ec = e[:n], e[n:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bissextil(y int) bool { return y%4 == 0 && !(y%100 == 0 && y%400 != 0) }
|
||||
func monthLen(y int, m uint) uint {
|
||||
switch m {
|
||||
case February:
|
||||
if bissextil(y) {
|
||||
return 29
|
||||
}
|
||||
return 28
|
||||
case April, June, September, November:
|
||||
return 30
|
||||
case January, March, May, July, August, October, December:
|
||||
return 31
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
func yearLen(y int) uint {
|
||||
if bissextil(y) {
|
||||
return 366
|
||||
}
|
||||
return 365
|
||||
}
|
||||
|
||||
func sit(h, i uint, args ...uint) float64 {
|
||||
var s, v uint
|
||||
if len(args) > 0 {
|
||||
s = args[0]
|
||||
if len(args) > 1 {
|
||||
v = args[1]
|
||||
}
|
||||
}
|
||||
v += s*MillisecondPerSecond + i*MillisecondPerMinute + h*MillisecondPerHour
|
||||
return float64(v) * float64(BeatPerDay) / float64(MillisecondPerDay)
|
||||
}
|
||||
|
||||
func timezone(tz ...string) *time.Location {
|
||||
if len(tz) > 0 {
|
||||
if l, err := time.LoadLocation(tz[0]); err == nil {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return DefaultTZ
|
||||
}
|
||||
func toTZ(t time.Time, tz ...string) time.Time { return t.In(timezone(tz...)) }
|
||||
func now(tz ...string) time.Time { return toTZ(time.Now(), tz...) }
|
||||
func yesterday(tz ...string) time.Time { return now(tz...).AddDate(0, 0, -1) }
|
||||
func tomorrow(tz ...string) time.Time { return now(tz...).AddDate(0, 0, 1) }
|
||||
|
||||
func fromDT(y int, m, d, h, i, s, v uint, l *time.Location) time.Time {
|
||||
return time.Date(y, time.Month(m-1), int(d), int(h), int(i), int(s), int(d*NanosecondPerMillisecond), l)
|
||||
}
|
||||
func fromD(y int, m, d uint, l *time.Location) time.Time { return fromDT(y, m, d, 0, 0, 0, 0, l) }
|
||||
func fromT(h, i, s, v uint, l *time.Location) time.Time {
|
||||
t := now().In(l)
|
||||
y, m, d := t.Year(), uint(t.Month()-1), uint(t.Day())
|
||||
return fromDT(y, m, d, h, i, s, v, l)
|
||||
}
|
||||
|
||||
// Clock
|
||||
func formatC(h, i uint, args ...uint) (uint, uint, uint, uint, Precision) {
|
||||
l := len(args)
|
||||
p, s, v := PrecisionMinute-Precision(l), uint(0), uint(0)
|
||||
if l > 0 {
|
||||
s = args[0]
|
||||
if l > 1 {
|
||||
v = args[1]
|
||||
}
|
||||
}
|
||||
return h, i, s, v, p
|
||||
}
|
||||
func ms(h, i, s, n uint) uint {
|
||||
return h*MillisecondPerHour + i*MillisecondPerMinute + s*MillisecondPerSecond + n
|
||||
}
|
||||
|
||||
// Date
|
||||
func validM(m uint) bool { return m >= January && m <= December }
|
||||
func validD(y int, m, d uint) bool { return validM(m) && d > 0 && d < monthLen(y, m) }
|
||||
func getD[N integer | uinteger](d date, t string) N { return N((d & maskDate[t]) >> shiftDate[t]) }
|
||||
func setD[N integer | uinteger](e N, t string) date { return date(e<<shiftDate[t]) & maskDate[t] }
|
||||
func replD[N integer | uinteger](d date, e N, t string) date {
|
||||
return (d & ^maskDate[t]) | setD(e, t)
|
||||
}
|
||||
|
||||
func diffBow(d uint) int { return int(Monday) - ((int(d) + DayPerWeek) % DayPerWeek) }
|
||||
func diffEow(d uint) int { return DayPerWeek - ((int(d) + DayPerWeek) % DayPerWeek) }
|
Loading…
Reference in New Issue