392 lines
12 KiB
Go
392 lines
12 KiB
Go
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 à partir de l’année, du mois et du jour.
|
||
// Si le fuseau horaire tz est renseigné, c’est le fuseau utilisé,
|
||
// sinon, c’est le fuseau par défaut.
|
||
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 t.
|
||
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...)) }
|
||
|
||
// Now retourne la date actuelle dans le même fuseau horaire que t.
|
||
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 e 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 retourne 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 w 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 (e. dimanche).
|
||
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))
|
||
}
|
||
|
||
// InTZ agit comme In mais en fournissant le fuseau horaire sous forme
|
||
// de chaîne de caractères.
|
||
func (t Date) ToTimezone(tz string) Date { return t.In(timezone(tz)) }
|
||
|
||
// TZ retourne le fuseau horaire.
|
||
func (t Date) TZ() *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 millisecondes.
|
||
func (t Date) TimestampMilli() int64 { return t.tv(t.location) }
|
||
|
||
// Compare compare 2 dates.
|
||
// La comparaison s’effectue après normalisation, ie. dans le même fuseau horaire.
|
||
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 }
|
||
|
||
// IsNow retourne vrai si la date est située aujourd’hui.
|
||
func (t Date) IsNow() bool { return t.Eq(t.Now()) }
|
||
|
||
// IsPast retourne vrai si la date est situé dans le passé.
|
||
func (t Date) IsPast() bool { return t.Lt(t.Now()) }
|
||
|
||
// IsFuture retourne vrai si la date est situé dans le futur.
|
||
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())
|
||
}
|
||
|
||
// DiffInDays retourne t1 - t2 en jours.
|
||
func (t1 Date) DiffInDays(t2 Date) int { return t1.subD(t2.date, t1.location, t2.location) }
|
||
|
||
// DiffInWeeks retourne t1 - t2 en semaines.
|
||
func (t1 Date) DiffInWeeks(t2 Date) int { return t1.subW(t2.date, t1.location, t2.location) }
|
||
|
||
// DiffInMonths retourne t1 - t2 en mois.
|
||
func (t1 Date) DiffInMonths(t2 Date) int { return t1.subM(t2.date) }
|
||
|
||
// DiffInYears retourne t1 - t2 en années.
|
||
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(f string) string { return t.f(f, t.location) }
|
||
|
||
// String retourne la date au format Y-m-d.
|
||
func (t Date) String() string { return t.str(t.location) }
|