Module datetime réécrit

This commit is contained in:
Benjamin VAUDOUR 2023-11-03 20:33:26 +01:00
parent 0700fced7b
commit 41e37d8398
14 changed files with 1894 additions and 917 deletions

414
datetime/clock.go Normal file
View File

@ -0,0 +1,414 @@
package datetime
import (
"time"
)
type clock uint
// Precision représente la précision dune 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 lheure à partir dune 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 lheure actuelle. Si tz est renseignée,
// lheure 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 lheure à 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 lheure à 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 lhorloge.
func (c Clock) Precision() Precision { return c.p() }
// IsNil retourne vrai si lhorloge 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 lheure.
func (c Clock) Minute() uint { return c.i() }
// Hour retourne lheure du jour.
func (c Clock) Hour() uint { return c.h() }
// Clock retourne lheure, 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 lheure.
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 dheures 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 lheure en cours.
func (c Clock) BeginOfHour() Clock { return newClock(c.bH(), c.location) }
// EndOfHour retourne la fin de lheure 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 lheure en date de type time.Time.
func (c Clock) ToTime() time.Time { return c.t(c.location) }
// In retourne lheure 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 lheure 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 à lheure 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 seffectue 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 lheure 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) }

View File

@ -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()) }

View File

@ -4,9 +4,229 @@ import (
"time"
)
const (
// Conversions dunité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 lunité 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 dheures avec lheure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
'P': "-07:00", // Différence dheures avec lheure 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 dheures avec lheure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
'P': "-07:00", // Différence dheures avec lheure 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
)

View File

@ -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 daujourdhui à lheure 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...)
}

374
datetime/date.go Normal file
View File

@ -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 lheure 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 dune 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 lanné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 lanné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 lanné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 lanné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 lanné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 lanné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 lanné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 dune 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 lannée.
func (t Date) BeginOfYear() Date { return newDate(t.bY(), t.location) }
// EndOfYear retourne la date du dernier jour de lanné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 à lheure 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) }

522
datetime/datetime.go Normal file
View File

@ -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 dune 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 lheure.
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 lanné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 lheure.
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 lanné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 lanné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 lanné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 lheure.
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 lannée, le mois, le jour, lheure, 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 lanné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 lheure, 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 lanné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 lheure.
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 dannée.
func (dt DateTime) BeginOfYear() DateTime { return initDT(dt.bY(), dt.location) }
// EndOfYear retourne la date en fin danné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 dheure.
func (dt DateTime) BeginOfHour() DateTime { return initDT(dt.bH(), dt.location) }
// EndOfHour retourne la date en fin dheure.
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 à lheure 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) }

View File

@ -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 dune valeur et dune 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 lunité.
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 lunité de durée.
func (d Duration) Unit() Unit { return Unit(d & maskUnit) }
// Duration retourne la valeur et lunité 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 linverse 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])
}

View File

@ -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)

View File

@ -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 lanné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 lanné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 lannée.
func IsEndOfYear(t time.Time) bool { return IsEndOfMonth(t) && t.Month() == time.December }
// IsToday retourne vrai si la date est située aujourdhui.
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)
}

View File

@ -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 lanné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 lheure 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 lheure 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 lanné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 lheure en cours.
func BeginOfHour(t time.Time) time.Time { return SetMinute(t, 0, 0, 0) }
// EndOfHour retourne la date à la fin de lheure 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 lannée en cours.
func BeginOfYear(t time.Time) time.Time { return BeginOfDay(SetMonth(t, 1, 1)) }
// EndOfYear retourne la date à la fin de lannée en cours.
func EndOfYear(t time.Time) time.Time { return EndOfDay(SetMonth(t, 12, 31)) }

View File

@ -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 nest 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 nest 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 nest 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...)
}

View File

@ -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 dindication de temps.
type TimeComparator[E any] interface {
Eq(E) bool
Ne(E) bool
Gt(E) bool
Ge(E) bool
Lt(E) bool
Le(E) bool
IsNow() bool
IsPast() bool
IsFuture() bool
Now() E
}
// 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 lensemble 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))
}

133
datetime/util.go Normal file
View File

@ -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) }

4
go.mod
View File

@ -1,5 +1,5 @@
module gitea.zaclys.com/bvaudour/gob
go 1.21.1
go 1.21
require golang.org/x/crypto v0.13.0 // indirect
require golang.org/x/crypto v0.13.0