package datetime import ( "time" ) type date int func newD(y int, m, j uint) date { if !validD(y, m, j) { return dateNil } return setD(y, "y") | setD(m, "m") | setD(j, "d") } func (t date) y() int { return getD[int](t, "y") } func (t date) m() uint { return getD[uint](t, "m") } func (t date) d() uint { return getD[uint](t, "d") } func (t date) t(l *time.Location) time.Time { return fromD(t.y(), t.m(), t.d(), l) } func (t date) dst(l *time.Location) bool { return t.t(l).IsDST() } func (t date) ts(l *time.Location) int64 { return t.t(l).Unix() } func (t date) tv(l *time.Location) int64 { return t.t(l).UnixMilli() } func (t date) b() bool { return bissextil(t.y()) } func (t date) yl() uint { return yearLen(t.y()) } func (t date) ml() uint { return monthLen(t.y(), t.m()) } func (t date) valid() bool { return validD(t.y(), t.m(), t.d()) } func (t date) yd() uint { if t.valid() { y, m, d := t.y(), t.m(), t.d() for mm := January; mm < m; mm++ { d += monthLen(y, mm) } return d } return 0 } func (t date) wd(l *time.Location) uint { return uint(t.t(l).Weekday()) } func (t date) setY(y int, e ...uint) date { if !t.valid() { return t } m, d := t.m(), t.d() if len(e) > 0 { m = e[0] if len(e) > 1 { d = e[1] } else if ml := monthLen(y, m); d > ml { d = ml } } return newD(y, m, d) } func (t date) setM(m uint, e ...uint) date { if !t.valid() { return t } y, d := t.y(), t.d() if len(e) > 0 { d = e[0] } else if ml := monthLen(y, m); d > ml { d = ml } return newD(y, m, d) } func (t date) setD(d uint) date { if !t.valid() { return t } t = replD(t, d, "d") if !t.valid() { return dateNil } return t } func (t date) add(y, m, d int) date { if !t.valid() { return t } y, m, d = t.y()+y, int(t.m())+m, int(t.d())+d if m > int(December) { mm := m - 1 y, m = y+mm/MonthPerYear, (mm%MonthPerYear)+1 } else if m < int(January) { mm := m - 12 y, m = y+mm/MonthPerYear, (mm%MonthPerYear)+12 } for !validD(y, uint(m), uint(d)) { ml := int(monthLen(y, uint(m))) if d > ml { d -= ml m++ if m > int(December) { m -= MonthPerYear y++ } } else if d < 1 { m-- if m < int(January) { m += MonthPerYear y-- } d += int(monthLen(y, uint(m))) } } return newD(y, uint(m), uint(d)) } func (t date) addY(y int) date { return t.add(y, 0, 0) } func (t date) addM(m int) date { return t.add(0, m, 0) } func (t date) addW(w int) date { return t.addD(w * DayPerWeek) } func (t date) addD(d int) date { return t.add(0, 0, d) } func (t date) addDD(dd Duration) date { ad := func(ud int) func(int) date { return func(e int) date { return t.addD(e / ud) } } f := map[Unit]func(int) date{ Year: t.addY, Month: t.addM, Week: t.addW, Day: t.addD, Hour: ad(HourPerDay), Minute: ad(MinutePerDay), Second: ad(SecondPerDay), Millisecond: ad(MillisecondPerDay), } e, u := dd.Value(), dd.Unit() if cb, ok := f[u]; ok { return cb(e) } return t } func (t date) bY() date { return t.setM(January, 1) } func (t date) eY() date { return t.setM(December, 31) } func (t date) bM() date { return t.setD(1) } func (t date) eM() date { return t.setD(monthLen(t.y(), t.m())) } func (t date) bW(l *time.Location) date { return t.addD(diffBow(t.wd(l))) } func (t date) eW(l *time.Location) date { return t.addD(diffEow(t.wd(l))) } func (t1 date) cmp(t2 date) int { if !t1.valid() { if !t2.valid() { return 0 } return -1 } if !t2.valid() { return 1 } if c := cmp(t1.y(), t2.y()); c != 0 { return c } if c := cmp(t1.m(), t2.m()); c != 0 { return c } return cmp(t1.d(), t2.d()) } func (t1 date) diff(t2 date) (dy, dm, dd int) { return t1.y() - t2.y(), int(t1.m()) - int(t2.m()), int(t1.d()) - int(t2.d()) } func (t1 date) subD(t2 date, l1, l2 *time.Location) int { return int(t1.ts(l1)-t2.ts(l2)) / SecondPerDay } func (t1 date) subW(t2 date, l1, l2 *time.Location) int { return t1.subD(t2, l1, l2) / DayPerWeek } func (t1 date) subM(t2 date) int { dy, dm, dd := t1.diff(t2) if dd < 0 { dm-- } return dy*MonthPerYear + dm } func (t1 date) subY(t2 date) int { return t1.subM(t2) / MonthPerYear } func (t date) f(f string, l *time.Location) string { if !t.valid() { return "-" } return formatT(t.t(l), f) } func (t date) str(l *time.Location) string { return t.f("Y-m-d", l) } // Date représente une date sans l’heure de la journée. type Date struct { date location *time.Location } // DateNil retourne une date nulle. func DateNil() (t Date) { return } func newDate(t date, l *time.Location) Date { if t.valid() { return Date{ date: t, location: l, } } return DateNil() } // NewDate retourne la date dans le fuseau horaire demandé. func NewDate(y int, m, d uint, tz ...string) Date { return newDate(newD(y, m, d), timezone(tz...)) } // NewDateFromTime retourne la date à partir d’une date Go. func NewDateFromTime(t time.Time) Date { y, m, d := t.Date() l := t.Location() return newDate(newD(y, uint(m+1), uint(d)), l) } // DateNow retourne la date actuelle. Si tz est renseignée, // la date est placée dans le fuseau horaire indiqué. func DateNow(tz ...string) Date { return NewDateFromTime(now(tz...)) } func (t Date) Now() Date { return DateNow(t.location.String()) } // DateGuess retourne la date à partir de e en essayant de deviner le format. func DateGuess(e string, tz ...string) Date { if t, ok := guess(e, tz...).Get(); ok { return NewDateFromTime(t) } return DateNil() } // DateParse retourne la date à partir de v en spécifiant le format f. func DateParse(e, f string, tz ...string) Date { if t, ok := parse(e, f).Get(); ok { return NewDateFromTime(t) } return DateNil() } // IsNil retourne vrai si la date est nulle. func (t Date) IsNil() bool { return !t.valid() } // Year retourne l’année. func (t Date) Year() int { return t.y() } // Month retourne le mois (de 1 à 12). func (t Date) Month() uint { return t.m() } // Day retourne le jour du mois (de 1 à 31). func (t Date) Day() uint { return t.d() } // Date retourne l’année, le mois et le jour du mois. func (t Date) Date() (y int, m, d uint) { return t.y(), t.m(), t.d() } // YearDay retourne le jour dans l’année (de 1 à 366). func (t Date) YearDay() uint { return t.yd() } // WeekDay retourne le jour de la semaine (de 0 à 6 en commençant par dimanche). func (t Date) WeekDay() uint { return t.wd(t.location) } // IsBissextil retourn vrai si la date est dans une année bissextile. func (t Date) IsBissextil() bool { return t.b() } // DaysInYear retourne le nombre de jours dans l’année. func (t Date) DaysInYear() uint { return t.yl() } // DaysInMonth retourne le nombre de jours dans le mois. func (t Date) DaysInMonth() uint { return t.ml() } // SetYear crée une nouvelle date en changeant l’année (et facultativement le mois et le jour). func (t Date) SetYear(y int, md ...uint) Date { return newDate(t.setY(y, md...), t.location) } // SetMonth crée une nouvelle date en changeant le mois (et facultativement le jour). func (t Date) SetMonth(m uint, d ...uint) Date { return newDate(t.setM(m, d...), t.location) } // SetDay crée une nouvelle date en changeant le jour. func (t Date) SetDay(d uint) Date { return newDate(t.setD(d), t.location) } // Add incrémente l’année, le mois et le jour. func (t Date) Add(y, m, d int) Date { return newDate(t.add(y, m, d), t.location) } // AddYear incrémente l’année. func (t Date) AddYear(y int) Date { return newDate(t.addY(y), t.location) } // AddMonth incrémente le mois. func (t Date) AddMonth(m int) Date { return newDate(t.addM(m), t.location) } // AddWeek incrémente la date de week semaines. func (t Date) AddWeek(w int) Date { return newDate(t.addW(w), t.location) } // AddDay incrémente le jour. func (t Date) AddDay(d int) Date { return newDate(t.addD(d), t.location) } // AddDuration décale la date d’une durée donnée. func (t Date) AddDuration(dd Duration) Date { return newDate(t.addDD(dd), t.location) } // BeginOfYear retourne la date du premier jour de l’année. func (t Date) BeginOfYear() Date { return newDate(t.bY(), t.location) } // EndOfYear retourne la date du dernier jour de l’année. func (t Date) EndOfYear() Date { return newDate(t.eY(), t.location) } // BeginOfMonth retourne la date du premier jour du mois. func (t Date) BeginOfMonth() Date { return newDate(t.bM(), t.location) } // EndOfMonth retourne la date du dernier jour du mois. func (t Date) EndOfMonth() Date { return newDate(t.eM(), t.location) } // BeginOfWeek retourne la date du premier jour de la semaine (ie. lundi). func (t Date) BeginOfWeek() Date { return newDate(t.bW(t.location), t.location) } // EndOfWeek retourne la date du dernier jour de la semaine. func (t Date) EndOfWeek() Date { return newDate(t.eW(t.location), t.location) } // ToTime convertit la date en date de type time.Time. func (t Date) ToTime() time.Time { return t.t(t.location) } // In retourne la date dans le fuseau horaire indiqué. func (t Date) In(l *time.Location) Date { if !t.valid() { return t } return NewDateFromTime(t.t(t.location).In(l)) } // ToTimezone retourne la date dans le fuseau horaire indiqué. func (t Date) ToTimezone(tz string) Date { return t.In(timezone(tz)) } // Location retourne le fuseau horaire. func (t Date) Location() *time.Location { return t.location } // IsDST retourne vrai si le fuseau horaire est à l’heure d’été. func (t Date) IsDST() bool { return t.dst(t.location) } // Timestamp retourne le timestamp en secondes. func (t Date) Timestamp() int64 { return t.ts(t.location) } // TimestampMilli retourne le timestamp en secondes. func (t Date) TimestampMilli() int64 { return t.tv(t.location) } // Compare compare 2 dates. func (t1 Date) Compare(t2 Date) int { return t1.cmp(t2.In(t1.location).date) } func (t1 Date) Eq(t2 Date) bool { return t1.Compare(t2) == 0 } func (t1 Date) Ne(t2 Date) bool { return t1.Compare(t2) != 0 } func (t1 Date) Gt(t2 Date) bool { return t1.Compare(t2) > 0 } func (t1 Date) Ge(t2 Date) bool { return t1.Compare(t2) >= 0 } func (t1 Date) Lt(t2 Date) bool { return t1.Compare(t2) < 0 } func (t1 Date) Le(t2 Date) bool { return t1.Compare(t2) <= 0 } func (t Date) IsNow() bool { return t.Eq(t.Now()) } func (t Date) IsPast() bool { return t.Lt(t.Now()) } func (t Date) IsFuture() bool { return t.Gt(t.Now()) } func (t1 Date) diff(t2 Date) (dy, dm, dd int) { return t1.y() - t2.y(), int(t1.m()) - int(t2.m()), int(t1.d()) - int(t2.d()) } func (t1 Date) DiffInDays(t2 Date) int { return t1.subD(t2.date, t1.location, t2.location) } func (t1 Date) DiffInWeeks(t2 Date) int { return t1.subW(t2.date, t1.location, t2.location) } func (t1 Date) DiffInMonths(t2 Date) int { return t1.subM(t2.date) } func (t1 Date) DiffInYears(t2 Date) int { return t1.subY(t2.date) } // Format retourne une représentation de la date au format spécifié. func (t Date) Format(format string) string { return t.f(format, t.location) } // String retourne la date au format Y-m-d. func (t Date) String() string { return t.str(t.location) }