Module datetime
This commit is contained in:
parent
79b5b534f3
commit
0700fced7b
|
@ -0,0 +1,78 @@
|
|||
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()) }
|
|
@ -0,0 +1,257 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Fuseau horaire par défaut
|
||||
DefaultTZ = time.Local
|
||||
|
||||
// Jours de la semaine (format court)
|
||||
shortDays = []string{
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
}
|
||||
|
||||
// Jours de la semaine (format long)
|
||||
longDays = []string{
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
}
|
||||
|
||||
// Mois (format court)
|
||||
shortMonths = []string{
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
}
|
||||
|
||||
// Mois (format long)
|
||||
longMonths = []string{
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
}
|
||||
|
||||
formatters = map[rune]func(time.Time) string{
|
||||
// Jour
|
||||
'j': format_j, // Jour du mois sans les 0 initiaux (1-31)
|
||||
'd': format_d, // Jour du mois, sur deux chiffres (avec un 0 initial) (01-31)
|
||||
'N': format_N, // Représentation numérique ISO 8601 du jour de la semaine (1-7)
|
||||
'w': format_w, // Jour de la semaine au format numérique (0-6)
|
||||
'z': format_z, // Jour de l’année (0-365)
|
||||
'D': format_D, // Jour de la semaine, en 3 lettres (Mon-Sun)
|
||||
'l': format_l, // Jour de la semaine, textuel, version longue (Monday-Sunday)
|
||||
|
||||
// Semaine
|
||||
'W': format_W, // N° de semaine dans l’année ISO 8601, les semaines commencent le lundi (0-53)
|
||||
|
||||
// Mois
|
||||
'n': format_n, // Mois sans les 0 initiaux (1-12)
|
||||
'm': format_m, // Mois au format numérique, avec 0 initiaux (01-12)
|
||||
't': format_t, // Nombre de jours dans le mois (28-31)
|
||||
'M': format_M, // Mois, en trois lettres (Jan-Dec)
|
||||
'F': format_F, // Mois, textuel, version longue (January-December)
|
||||
|
||||
// Année
|
||||
'y': format_y, // Année sur 2 chiffres (Exemples : 99 ou 03)
|
||||
'Y': format_Y, // Année sur au moins 4 chiffres, avec - pour les années av. J.-C. (Exemples : -0055, 0787, 1999, 2003, 10191)
|
||||
'L': format_L, // Est ce que l’année est bissextile (1 si bissextile, 0 sinon)
|
||||
|
||||
// Heure
|
||||
'a': format_a, // Ante meridiem et Post meridiem en minuscules (am ou pm)
|
||||
'A': format_A, // Ante meridiem et Post meridiem en majuscules (AM ou PM)
|
||||
'B': format_B, // Heure Internet Swatch (000-999)
|
||||
'g': format_g, // Heure, au format 12h, sans les 0 initiaux (1-12)
|
||||
'G': format_G, // Heure, au format 24h, sans les 0 initiaux (0-23)
|
||||
'h': format_h, // Heure, au format 12h, avec les 0 initiaux (01-12)
|
||||
'H': format_H, // Heure, au format 24h, avec les 0 initiaux (00-23)
|
||||
'i': format_i, // Minutes avec les 0 initiaux (00-59)
|
||||
's': format_s, // Secondes avec les 0 initiaux (00-59)
|
||||
'v': format_v, // Nillisecondes avec les 0 initiaux (000-999)
|
||||
'u': format_u, // Microsecondes avec les 0 initiaux (000000-999999)
|
||||
|
||||
// Fuseau horaire
|
||||
'T': format_T, // Abréviation du fuseau horaire, si connu ; sinon décalage depuis GMT (Exemples : EST, MDT, +05)
|
||||
'e': format_e, // L’identifiant du fuseau horaire (Exemples : UTC, GMT, Atlantic/Azores)
|
||||
'I': format_I, // L’heure d’été est activée ou pas (1 si oui, 0 sinon)
|
||||
'O': format_O, // Différence d’heures avec l’heure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
|
||||
'P': format_P, // Différence d’heures avec l’heure de Greenwich (GMT), avec deux-points entre les heures et les minutes (Exemple : +02:00)
|
||||
'p': format_p, // Identique à P, mais retourne Z au lieu de +00:00
|
||||
'Z': format_Z, // Décalage horaire en secondes. Le décalage des zones à l’ouest de la zone UTC est négatif, et à l’est, il est positif.(-43200 à 50400)
|
||||
|
||||
// Date et heure complète
|
||||
'c': format_c, // Date au format ISO 8601 (2004-02-12T15:19:21+00:00)
|
||||
'r': format_r, // Date au format RFC 5322 (Thu, 21 Dec 2000 16:01:070200)
|
||||
'U': format_U, // Secondes depuis l’époque Unix (1er Janvier 1970, 0h00 00s GMT)
|
||||
}
|
||||
|
||||
parsers = map[rune]string{
|
||||
// Jours
|
||||
'j': "2", // Jour du mois sans les 0 initiaux (1-31)
|
||||
'd': "02", // Jour du mois, sur deux chiffres (avec un 0 initial) (01-31)
|
||||
'D': "Mon", // Jour de la semaine, en 3 lettres (Mon-Sun)
|
||||
'l': "Monday", // Jour de la semaine, textuel, version longue (Monday-Sunday)
|
||||
|
||||
// Mois
|
||||
'n': "1", // Mois sans les 0 initiaux (1-12)
|
||||
'm': "01", // Mois au format numérique, avec 0 initiaux (01-12)
|
||||
'M': "Jan", // Mois, en trois lettres (Jan-Dec)
|
||||
'F': "January", // Mois, textuel, version longue (January-December)
|
||||
|
||||
// Année
|
||||
'y': "06", // Année sur 2 chiffres (Exemples : 99 ou 03)
|
||||
'Y': "2006", // Année sur au moins 4 chiffres, avec - pour les années av. J.-C. (Exemples : -0055, 0787, 1999, 2003, 10191)
|
||||
|
||||
// Heure
|
||||
'a': "pm", // Ante meridiem et Post meridiem en minuscules (am ou pm)
|
||||
'A': "PM", // Ante meridiem et Post meridiem en majuscules (AM ou PM)
|
||||
'g': "3", // Heure, au format 12h, sans les 0 initiaux (1-12)
|
||||
'h': "03", // Heure, au format 12h, avec les 0 initiaux (01-12)
|
||||
'H': "15", // Heure, au format 24h, avec les 0 initiaux (00-23)
|
||||
'i': "04", // Minutes avec les 0 initiaux (00-59)
|
||||
's': "05", // Secondes avec les 0 initiaux (00-59)
|
||||
|
||||
// Fuseau horaire
|
||||
'T': "MST", // Abréviation du fuseau horaire, si connu ; sinon décalage depuis GMT (Exemples : EST, MDT, +05)
|
||||
'O': "-0700", // Différence d’heures avec l’heure de Greenwich (GMT), sans deux-points entre les heures et les minutes (Exemple : +0200)
|
||||
'P': "-07:00", // Différence d’heures avec l’heure de Greenwich (GMT), avec deux-points entre les heures et les minutes (Exemple : +02:00)
|
||||
|
||||
// Date et heure complète
|
||||
'c': "2006-01-02T15:04:05-07:00", // Date au format ISO 8601 (2004-02-12T15:19:21+00:00)
|
||||
'r': "Thu, 21 Dec 2000 16:01:07 +0200", // Date au format RFC 5322 (Thu, 21 Dec 2000 16:01:070200)
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
|
@ -0,0 +1,101 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
// Zero retourne la date correspondant au 01/01/0001 00:00:00 UTC.
|
||||
// Si tz est fourni, la date est dans le fuseau horaire demandé,
|
||||
// sinon, dans le fuseau horaire par défaut.
|
||||
func Zero(tz ...string) Result[time.Time] {
|
||||
var t time.Time
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// Now retourne la date actuelle.
|
||||
func Now(tz ...string) Result[time.Time] {
|
||||
t := time.Now()
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// Tomorrow retourne la date dans 24h.
|
||||
func Tomorrow(tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if t, ok := now.Ok(); ok {
|
||||
return Ok(AddDays(t, 1))
|
||||
}
|
||||
|
||||
return now
|
||||
}
|
||||
|
||||
// Yesterday retourne la date il y a 24h.
|
||||
func Yesterday(tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if t, ok := now.Ok(); ok {
|
||||
return Ok(AddDays(t, -1))
|
||||
}
|
||||
|
||||
return now
|
||||
}
|
||||
|
||||
// FromTimestamp convertit un timestamp (exprimé en secondes) en date.
|
||||
func FromTimestamp(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp, 0)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampMilli convertit un timestamp (exprimé en ms) en date.
|
||||
func FromTimestampMilli(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e3, (timestamp%1e3)*1e6)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampMicro convertit un timestamp (exprimé en μs) en date.
|
||||
func FromTimestampMicro(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e6, (timestamp%1e6)*1e3)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromTimestampNano convertit un timestamp (exprimé en ns) en date.
|
||||
func FromTimestampNano(timestamp int64, tz ...string) Result[time.Time] {
|
||||
t := time.Unix(timestamp/1e9, timestamp%1e9)
|
||||
|
||||
return toTZ(t, tz...)
|
||||
}
|
||||
|
||||
// FromDateTime retourne la date à partir des données numériques complètes.
|
||||
func FromDateTime(year, month, day, hour, minute, second int, tz ...string) Result[time.Time] {
|
||||
location, ok := getTZ(tz...).Get()
|
||||
if !ok {
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz))
|
||||
}
|
||||
|
||||
t := time.Date(year, time.Month(month-1), day, hour, minute, second, 0, location)
|
||||
|
||||
return Ok(t)
|
||||
}
|
||||
|
||||
// FromDate retourne le début du jour à partir des données de dates.
|
||||
func FromDate(year, month, day int, tz ...string) Result[time.Time] {
|
||||
return FromDateTime(year, month, day, 0, 0, 0)
|
||||
}
|
||||
|
||||
// FromTime retourne la date d’aujourd’hui à l’heure indiquée.
|
||||
func FromTime(hour, minute, second int, tz ...string) Result[time.Time] {
|
||||
now := Now(tz...)
|
||||
if !now.IsOk() {
|
||||
return now
|
||||
}
|
||||
|
||||
n, _ := now.Ok()
|
||||
year, month, day := n.Date()
|
||||
return FromDateTime(year, int(month+1), day, hour, minute, second, tz...)
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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()
|
||||
|
||||
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++
|
||||
}
|
||||
|
||||
return int64(dy)
|
||||
}
|
||||
|
||||
// DiffInMonths retourne (t1 - t2) exprimé en mois.
|
||||
func DiffInMonths(t1, t2 time.Time) int64 {
|
||||
y1, m1, d1 := t1.Date()
|
||||
y2, m2, d2 := t2.Date()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func abs[N ~int | ~int64](e N) N {
|
||||
if e < 0 {
|
||||
return -e
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Jour
|
||||
func format_j(t time.Time) string { return fmt.Sprintf("%d", t.Day()) }
|
||||
func format_d(t time.Time) string { return fmt.Sprintf("%02d", t.Day()) }
|
||||
func format_N(t time.Time) string { return fmt.Sprintf("%d", t.Weekday()+1) }
|
||||
func format_w(t time.Time) string { return fmt.Sprintf("%d", t.Weekday()) }
|
||||
func format_D(t time.Time) string { return shortDays[t.Weekday()] }
|
||||
func format_l(t time.Time) string { return longDays[t.Weekday()] }
|
||||
func format_z(t time.Time) string { return fmt.Sprintf("%d", t.YearDay()) }
|
||||
|
||||
// Semaine
|
||||
func format_W(t time.Time) string {
|
||||
_, w := t.ISOWeek()
|
||||
return fmt.Sprintf("%d", w)
|
||||
}
|
||||
|
||||
// Mois
|
||||
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())) }
|
||||
|
||||
// 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()) {
|
||||
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 {
|
||||
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 {
|
||||
h = 12
|
||||
}
|
||||
return fmt.Sprintf("%d", h)
|
||||
}
|
||||
func format_G(t time.Time) string { return fmt.Sprintf("%d", t.Hour()) }
|
||||
func format_h(t time.Time) string {
|
||||
h := t.Hour() % 12
|
||||
if h == 0 {
|
||||
h = 12
|
||||
}
|
||||
return fmt.Sprintf("%02d", h)
|
||||
}
|
||||
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) }
|
||||
|
||||
// Fuseau horaire
|
||||
func format_T(t time.Time) string {
|
||||
name := t.Location().String()
|
||||
if !strings.Contains(name, "/") {
|
||||
return name
|
||||
}
|
||||
diff := gmtDiff(t)
|
||||
h, m := diff/Hour, (diff%Hour)/Minute
|
||||
s := "+"
|
||||
if h < 0 {
|
||||
s = "-"
|
||||
}
|
||||
if m == 0 {
|
||||
return fmt.Sprintf("%s%02d", s, abs(h))
|
||||
}
|
||||
return fmt.Sprintf("%s%02d:%02d", s, abs(h), abs(m))
|
||||
}
|
||||
func format_e(t time.Time) string { return fmt.Sprintf("%s", t.Location()) }
|
||||
func format_I(t time.Time) string {
|
||||
if t.IsDST() {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
func format_O(t time.Time) string {
|
||||
diff := gmtDiff(t)
|
||||
h, m := diff/Hour, (diff%Hour)/Minute
|
||||
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
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
// Date et heure complète
|
||||
func format_c(t time.Time) string {
|
||||
// Équivalent à "Y-m-d\TH:i:sP"
|
||||
return fmt.Sprintf("%s-%s-%sT%s:%s:%s%s", format_Y(t), format_m(t), format_d(t), format_H(t), format_i(t), format_s(t), format_P(t))
|
||||
}
|
||||
func format_r(t time.Time) string {
|
||||
// Équivalent à "D, j M Y H:i:sO"
|
||||
return fmt.Sprintf("%s, %s %s %s %s:%s:%s%s", format_D(t), format_j(t), format_M(t), format_Y(t), format_H(t), format_i(t), format_s(t), format_P(t))
|
||||
}
|
||||
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 {
|
||||
var buffer strings.Builder
|
||||
|
||||
runes := []rune(format)
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if f, ok := formatters[runes[i]]; ok {
|
||||
buffer.WriteString(f(t))
|
||||
} else {
|
||||
switch runes[i] {
|
||||
case '\\': // raw output, no parse
|
||||
buffer.WriteRune(runes[i+1])
|
||||
i++
|
||||
default:
|
||||
buffer.WriteRune(runes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DaysInMonth retourne le nombre de jours dans le mois.
|
||||
func DaysInMonth(t time.Time) int { return daysInMonth(t.Year(), t.Month()) }
|
||||
|
||||
// DaysInYear retourne le nombe de jours dans l’année.
|
||||
func DaysInYear(t time.Time) int {
|
||||
if IsBissextil(t) {
|
||||
return 366
|
||||
}
|
||||
return 365
|
||||
}
|
||||
|
||||
// IsBissextil retourne vrai si la date est située dans une année bissextile.
|
||||
func IsBissextil(t time.Time) bool { return isYearBissextil(t.Year()) }
|
||||
|
||||
// IsWeekend retourne vrai si la date est située dans le weekend.
|
||||
func IsWeekend(t time.Time) bool {
|
||||
d := t.Weekday()
|
||||
|
||||
return d != time.Saturday && d != time.Sunday
|
||||
}
|
||||
|
||||
// IsBeginOfMonth retourne vrai si la date est dans le premier jour du mois.
|
||||
func IsBeginOfMonth(t time.Time) bool { return t.Day() == 1 }
|
||||
|
||||
// IsEndOfMonth retourne vrai si la date est dans le dernier jour du mois.
|
||||
func IsEndOfMonth(t time.Time) bool { return t.Day() == DaysInMonth(t) }
|
||||
|
||||
// IsBeginOfYear retourne vrai si la date est dans le premier jour de l’année.
|
||||
func IsBeginOfYear(t time.Time) bool { return IsBeginOfMonth(t) && t.Month() == time.January }
|
||||
|
||||
// IsEndOfYear retourne vrai si la date est dans le dernier jour de l’année.
|
||||
func IsEndOfYear(t time.Time) bool { return IsEndOfMonth(t) && t.Month() == time.December }
|
||||
|
||||
// IsToday retourne vrai si la date est située aujourd’hui.
|
||||
func IsToday(t time.Time) bool {
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := time.Now().In(t.Location()).Date()
|
||||
|
||||
return y == y0 && m == m0 && d == d0
|
||||
}
|
||||
|
||||
// IsTomorrow retourne vrai si la date est située demain.
|
||||
func IsTomorrow(t time.Time) bool {
|
||||
n := time.Now().In(t.Location())
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := n.Date()
|
||||
|
||||
if d > 1 {
|
||||
return y == y0 && m == m0 && d == d0+1
|
||||
}
|
||||
|
||||
if m > time.January {
|
||||
return y == y0 && m == m0+1 && IsEndOfMonth(n)
|
||||
}
|
||||
|
||||
return y == y0+1 && IsEndOfYear(n)
|
||||
}
|
||||
|
||||
// IsYesterday retourne vrai si la date est située hier.
|
||||
func IsYesterday(t time.Time) bool {
|
||||
n := time.Now().In(t.Location())
|
||||
y, m, d := t.Date()
|
||||
y0, m0, d0 := n.Date()
|
||||
|
||||
if d < DaysInMonth(t) {
|
||||
return y == y0 && m == m0 && d == d0-1
|
||||
}
|
||||
|
||||
if m < time.December {
|
||||
return y == y0 && m == m0-1 && IsBeginOfMonth(n)
|
||||
}
|
||||
|
||||
return y == y0-1 && IsBeginOfYear(n)
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
func addUnit[I int | int64](t time.Time, duration I, unit time.Duration) time.Time {
|
||||
return t.Add(time.Duration(duration) * unit)
|
||||
}
|
||||
|
||||
// AddNanoseconds ajoute d ns à la date.
|
||||
func AddNanoseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Nanosecond)
|
||||
}
|
||||
|
||||
// AddMicroseconds ajoute d μs à la date.
|
||||
func AddMicroseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Microsecond)
|
||||
}
|
||||
|
||||
// AddMilliseconds ajoute d ms à la date.
|
||||
func AddMilliseconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Millisecond)
|
||||
}
|
||||
|
||||
// AddSeconds ajoute d s à la date.
|
||||
func AddSeconds[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Second)
|
||||
}
|
||||
|
||||
// AddMinutes ajoute d min à la date.
|
||||
func AddMinutes[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Minute)
|
||||
}
|
||||
|
||||
// AddHours ajoute d heures à la date.
|
||||
func AddHours[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Hour)
|
||||
}
|
||||
|
||||
// AddDays ajoute d jours à la date.
|
||||
func AddDays[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Day)
|
||||
}
|
||||
|
||||
// AddWeeks ajoute d semaines à la date.
|
||||
func AddWeeks[I int | int64](t time.Time, d I) time.Time {
|
||||
return addUnit(t, d, Week)
|
||||
}
|
||||
|
||||
// AddMonths ajoute d mois à la date.
|
||||
func AddMonths[I int | int64](t time.Time, d I) time.Time {
|
||||
return t.AddDate(0, int(d), 0)
|
||||
}
|
||||
|
||||
// AddYears ajoute d années à la date.
|
||||
func AddYears[I int | int64](t time.Time, d I) time.Time {
|
||||
return t.AddDate(int(d), 0, 0)
|
||||
}
|
||||
|
||||
// AddDecades ajoute d×10 années à la date.
|
||||
func AddDecades[I int | int64](t time.Time, d I) time.Time {
|
||||
return AddYears(t, 10*d)
|
||||
}
|
||||
|
||||
// AddCenturies ajoute d siècles à la date.
|
||||
func AddCenturies[I int | int64](t time.Time, d I) time.Time {
|
||||
return AddYears(t, 100*d)
|
||||
}
|
||||
|
||||
func getTZ(tz ...string) (result Option[*time.Location]) {
|
||||
if len(tz) > 0 {
|
||||
location, err := time.LoadLocation(tz[0])
|
||||
if err == nil {
|
||||
return Some(location)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return Some(DefaultTZ)
|
||||
}
|
||||
|
||||
func toTZ(t time.Time, tz ...string) Result[time.Time] {
|
||||
if l, ok := getTZ(tz...).Get(); ok {
|
||||
return Ok(t.In(l))
|
||||
}
|
||||
|
||||
if len(tz) == 0 {
|
||||
return Ok(t.In(DefaultTZ))
|
||||
}
|
||||
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz))
|
||||
}
|
||||
|
||||
// ToTimezone retourne la date dans le fuseau horaire indiqué.
|
||||
func ToTimezone(t time.Time, tz string) Result[time.Time] {
|
||||
return toTZ(t, tz)
|
||||
}
|
||||
|
||||
// Local retourne la date dans le fuseau horaire local.
|
||||
func Local(t time.Time) time.Time {
|
||||
return t.In(time.Local)
|
||||
}
|
||||
|
||||
// UTC retourne la date dans le fuseau UTC.
|
||||
func UTC(t time.Time) time.Time {
|
||||
return t.In(time.UTC)
|
||||
}
|
||||
|
||||
// Date modifie la date avec l’année, le mois et le jour fournis.
|
||||
func Date(t time.Time, year, month, day int) time.Time {
|
||||
h, m, s := t.Clock()
|
||||
e := t.Nanosecond()
|
||||
l := t.Location()
|
||||
|
||||
return time.Date(year, time.Month(month-1), day, h, m, s, e, l)
|
||||
}
|
||||
|
||||
// Clock modifie l’heure de la date.
|
||||
func Clock(t time.Time, hour, minute, second int, nano ...int) time.Time {
|
||||
y, m, d := t.Date()
|
||||
l := t.Location()
|
||||
e := 0
|
||||
|
||||
if len(nano) > 0 {
|
||||
e = nano[0]
|
||||
}
|
||||
|
||||
return time.Date(y, m, d, hour, minute, second, e, l)
|
||||
}
|
||||
|
||||
// SetNano modifie les nanosecondes de la date.
|
||||
func SetNano(t time.Time, nano int) time.Time {
|
||||
h, m, s := t.Clock()
|
||||
|
||||
return Clock(t, h, m, s, nano)
|
||||
}
|
||||
|
||||
// SetSecond modifie la seconde de la date (et éventuellement la ns).
|
||||
func SetSecond(t time.Time, second int, nano ...int) time.Time {
|
||||
h, m := t.Hour(), t.Minute()
|
||||
e := t.Nanosecond()
|
||||
if len(nano) > 0 {
|
||||
e = nano[0]
|
||||
}
|
||||
|
||||
return Clock(t, h, m, second, e)
|
||||
}
|
||||
|
||||
// SetMinute modifie la minute de la date (et éventuellement la s et la ns).
|
||||
func SetMinute(t time.Time, minute int, other ...int) time.Time {
|
||||
h, s, e := t.Hour(), t.Second(), t.Nanosecond()
|
||||
if len(other) > 0 {
|
||||
s = other[0]
|
||||
if len(other) > 1 {
|
||||
e = other[1]
|
||||
}
|
||||
}
|
||||
|
||||
return Clock(t, h, minute, s, e)
|
||||
}
|
||||
|
||||
// SetHour modifie l’heure de la date (et éventuellement la min, s & ns).
|
||||
func SetHour(t time.Time, hour int, other ...int) time.Time {
|
||||
m, s, e := t.Minute(), t.Second(), t.Nanosecond()
|
||||
if len(other) > 0 {
|
||||
m = other[0]
|
||||
if len(other) > 1 {
|
||||
s = other[1]
|
||||
if len(other) > 2 {
|
||||
e = other[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Clock(t, hour, m, s, e)
|
||||
}
|
||||
|
||||
// SetDay modifie le jour de la date.
|
||||
func SetDay(t time.Time, day int) time.Time {
|
||||
y, m := t.Year(), t.Month()
|
||||
|
||||
return Date(t, y, int(m+1), day)
|
||||
}
|
||||
|
||||
// SetMonth modifie le mois de la date (et éventuellement le jour).
|
||||
func SetMonth(t time.Time, month int, day ...int) time.Time {
|
||||
y, d := t.Year(), t.Day()
|
||||
if len(day) > 0 {
|
||||
d = day[0]
|
||||
}
|
||||
|
||||
return Date(t, y, month, d)
|
||||
}
|
||||
|
||||
// SetYear modifie l’année de la date (et éventuellement le mois et le jour).
|
||||
func SetYear(t time.Time, year int, other ...int) time.Time {
|
||||
m, d := int(t.Month()+1), t.Day()
|
||||
if len(other) > 0 {
|
||||
m = other[0]
|
||||
if len(other) > 1 {
|
||||
d = other[1]
|
||||
}
|
||||
}
|
||||
|
||||
return Date(t, year, m, d)
|
||||
}
|
||||
|
||||
// BeginOfSecond retourne la date au début de la seconde en cours.
|
||||
func BeginOfSecond(t time.Time) time.Time { return SetNano(t, 0) }
|
||||
|
||||
// EndOfSecond retourne la date à la fin de la seconde en cours.
|
||||
func EndOfSecond(t time.Time) time.Time { return SetNano(t, 999999999) }
|
||||
|
||||
// BeginOfMinute retourne la date au début de la minute en cours.
|
||||
func BeginOfMinute(t time.Time) time.Time { return SetSecond(t, 0, 0) }
|
||||
|
||||
// EndOfMinute retourne la date à la fin de la minute en cours.
|
||||
func EndOfMinute(t time.Time) time.Time { return SetSecond(t, 59, 999999999) }
|
||||
|
||||
// BeginOfHour retourne la date au début de l’heure en cours.
|
||||
func BeginOfHour(t time.Time) time.Time { return SetMinute(t, 0, 0, 0) }
|
||||
|
||||
// EndOfHour retourne la date à la fin de l’heure en cours.
|
||||
func EndOfHour(t time.Time) time.Time { return SetMinute(t, 59, 59, 999999999) }
|
||||
|
||||
// BeginOfDay retourne la date au début du jour en cours.
|
||||
func BeginOfDay(t time.Time) time.Time { return SetHour(t, 0, 0, 0, 0) }
|
||||
|
||||
// EndOfDay retourne la date à la fin du jour en cours.
|
||||
func EndOfDay(t time.Time) time.Time { return SetHour(t, 23, 59, 59, 999999999) }
|
||||
|
||||
// BeginOfWeek retourne la date au début de la semaine en cours.
|
||||
func BeginOfWeek(t time.Time) time.Time {
|
||||
d := t.Weekday()
|
||||
if d == time.Sunday {
|
||||
d += 7
|
||||
}
|
||||
|
||||
return BeginOfDay(t.AddDate(0, 0, 1-int(d)))
|
||||
}
|
||||
|
||||
// EndOfWeek retourne la date à la fin de la semaine en cours.
|
||||
func EndOfWeek(t time.Time) time.Time {
|
||||
d := t.Weekday()
|
||||
if d == time.Sunday {
|
||||
d += 7
|
||||
}
|
||||
|
||||
return EndOfDay(t.AddDate(0, 0, 7-int(d)))
|
||||
}
|
||||
|
||||
// BeginOfMonth retourne la date au début du mois en cours.
|
||||
func BeginOfMonth(t time.Time) time.Time { return BeginOfDay(SetDay(t, 1)) }
|
||||
|
||||
// EndOfMonth retourne la date à la fin du mois en cours.
|
||||
func EndOfMonth(t time.Time) time.Time { return EndOfDay(SetDay(t, DaysInMonth(t))) }
|
||||
|
||||
// BeginOfYear retourne la date au début de l’année en cours.
|
||||
func BeginOfYear(t time.Time) time.Time { return BeginOfDay(SetMonth(t, 1, 1)) }
|
||||
|
||||
// EndOfYear retourne la date à la fin de l’année en cours.
|
||||
func EndOfYear(t time.Time) time.Time { return EndOfDay(SetMonth(t, 12, 31)) }
|
|
@ -0,0 +1,95 @@
|
|||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
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
|
||||
buffer.WriteRune(runes[i+1])
|
||||
i++
|
||||
continue
|
||||
default:
|
||||
buffer.WriteRune(runes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
t, err := time.ParseInLocation(layout, value, location)
|
||||
if err == nil {
|
||||
return Ok(t)
|
||||
}
|
||||
return Err[time.Time](fmt.Errorf(errInvalidValue, value))
|
||||
}
|
||||
|
||||
// Guess tente de parser la chaîne de caractères en date en essayant de deviner le format.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func Guess(value string, tz ...string) Result[time.Time] {
|
||||
if value == "" || value == "0" || value == "0000-00-00 00:00:00" || value == "0000-00-00" || value == "00:00:00" {
|
||||
return Zero(tz...)
|
||||
}
|
||||
|
||||
switch value {
|
||||
case "now":
|
||||
return Now(tz...)
|
||||
case "yesterday":
|
||||
return Yesterday(tz...)
|
||||
case "tomorrow":
|
||||
return Tomorrow(tz...)
|
||||
}
|
||||
|
||||
if len(tz) > 0 {
|
||||
if _, err := time.LoadLocation(tz[0]); err != nil {
|
||||
return Err[time.Time](fmt.Errorf(errInvalidTZ, tz[0]))
|
||||
}
|
||||
}
|
||||
|
||||
for _, layout := range layouts {
|
||||
t := parseInLocation(layout, value, tz...)
|
||||
if t.IsOk() {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return Err[time.Time](fmt.Errorf(errInvalidValue, value))
|
||||
}
|
||||
|
||||
// ParseFromLayout parse la chaîne de caractères en date à partir du layout (façon Go) fourni.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func ParseFromLayout(value, layout string, tz ...string) Result[time.Time] {
|
||||
if value == "" || value == "0" || value == "0000-00-00 00:00:00" || value == "0000-00-00" || value == "00:00:00" {
|
||||
return Zero(tz...)
|
||||
}
|
||||
|
||||
return parseInLocation(value, layout, tz...)
|
||||
}
|
||||
|
||||
// Parse parse la chaîne de caractères en date à partir du format (dans le style PHP) fourni.
|
||||
// Si le fuseau horaire n’est pas précisé dans la chaîne, le fuseau utilisé est tz (si fourni) ou le fuseau horaire par défaut.
|
||||
func Parse(value, format string, tz ...string) Result[time.Time] {
|
||||
return ParseFromLayout(value, format2layout(format), tz...)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
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
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Begin retourne la date de début de la période.
|
||||
func (r Range) Begin() time.Time { return r.begin }
|
||||
|
||||
// End retourne la date de fin de la période.
|
||||
func (r Range) End() time.Time { return r.end }
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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) }
|
||||
|
||||
// ContainsDate retourne vrai si t ∈ [begin; end].
|
||||
func (r Range) ContainsDate(t time.Time) bool { return Le(r.begin, t) && Ge(r.end, t) }
|
||||
|
||||
// ContainsDateStrictBegin retourne vrai si t ∈ ]begin; end].
|
||||
func (r Range) ContainsDateStrictBegin(t time.Time) bool { return Lt(r.begin, t) && Ge(r.end, t) }
|
||||
|
||||
// ContainsDateStrictEnd retourne vrai si t ∈ [begin; end[.
|
||||
func (r Range) ContainsDateStrictEnd(t time.Time) bool { return Le(r.begin, t) && Gt(r.end, t) }
|
||||
|
||||
// ContainsDateStrict retourne vrai si t ∈ ]begin; end[.
|
||||
func (r Range) ContainsDateStrict(t time.Time) bool { return Lt(r.begin, t) && Gt(r.end, t) }
|
||||
|
||||
// IsFuture retourne vrai si la période est située dans le futur.
|
||||
func (r Range) IsFuture() bool { return r.AfterDate(time.Now()) }
|
||||
|
||||
// IsPast retourne vrai si la période est située dans le passé.
|
||||
func (r Range) IsPast() bool { return r.BeforeDate(time.Now()) }
|
||||
|
||||
// IsNow retourne vrai si la période est en cours.
|
||||
func (r Range) IsNow() bool { return r.ContainsDate(time.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) }
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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) }
|
||||
|
||||
// In retourne vrai si r est complètement inclus dans r2.
|
||||
func (r Range) In(r2 Range) bool { return r2.Contains(r) }
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Diff retourne l’ensemble des périodes non communes aux deux périodes.
|
||||
func (r Range) Diff(r2 Range) (result []Range) {
|
||||
if Ne(r.begin, r2.begin) {
|
||||
begin := Min(r.begin, r2.begin)
|
||||
var end time.Time
|
||||
if begin == r.begin {
|
||||
end = Min(r.end, r2.begin)
|
||||
} else {
|
||||
end = Min(r2.end, r.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)
|
||||
} else {
|
||||
begin = Max(r2.begin, r.end)
|
||||
}
|
||||
result = append(result, NewRange(begin, end))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue