From 976ac750b10896f5feeffc7e8bceceb9b2232ee6 Mon Sep 17 00:00:00 2001 From: Benjamin VAUDOUR Date: Sat, 4 Nov 2023 11:39:54 +0100 Subject: [PATCH] =?UTF-8?q?M=C3=A0j=20du=20README=20+=20commentaires=20dat?= =?UTF-8?q?etime=20plus=20pr=C3=A9cis=20+=20ajout=20de=20la=20m=C3=A9thode?= =?UTF-8?q?=20String()=20=C3=A0=20Range=20&=20Duration=20+=20Parsage=20d?= =?UTF-8?q?=E2=80=99une=20dur=C3=A9e=20+=20gestion=20des=20p=C3=A9riodes?= =?UTF-8?q?=20avec=20borne=20infinie=20=C3=A0=20droite=20et/ou=20=C3=A0=20?= =?UTF-8?q?gauche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++ datetime/clock.go | 65 ++++++++++++++------- datetime/const.go | 22 +++---- datetime/date.go | 49 +++++++++++----- datetime/datetime.go | 92 ++++++++++++++++------------- datetime/duration.go | 26 ++++++++- datetime/format.go | 2 +- datetime/range.go | 135 ++++++++++++++++++++++++++++++++----------- 8 files changed, 277 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index ffc776c..4fa1a86 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,15 @@ Le paquet **convert** fournit le nécessaire pour convertir des variables typée Tous ces types sont convertibles entre eux. +### datetime + +Le paquet **datetime** implémente des structures pour gérer des temps : + +- datetime.Clock : pour gérer une heure dans la journée, +- datetime.Date : pour gérer une date, sans indication de l’heure, +- datetime.DateTime : pour gérer une date avec indication de l’heure. +- Range : pour gérer une période entre deux temps. + ### format Le paquet **format** fournit le nécessaire pour formater la sortie terminal : diff --git a/datetime/clock.go b/datetime/clock.go index c4a0d4f..2a91ea0 100644 --- a/datetime/clock.go +++ b/datetime/clock.go @@ -7,6 +7,14 @@ import ( type clock uint // Precision représente la précision d’une horloge. +// Par exemple : +// - 15:06 est une horloge précise à la minute, +// - 15:06:02 est précise à la seconde, +// - 15:06:02.257 est précise à la milliseconde. +// +// La précision est utilisée dans Clock pour les comparaisons : +// les comparaisons entre deux horloges se font à la précision +// le l’horloge la moins précise. type Precision uint func newC0(h, i uint, e ...uint) clock { @@ -213,11 +221,13 @@ 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. +// NewClock retourne une horloge à l’heure h et la minute i dans le fuseau horaire par défaut. +// La précision est calculée automatiquement suivant que la seconde et la milliseconde sont également +// présentes dans args. 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. +// tz peut être mis sous la forme "Europe/Paris", "CET", "Local", etc. func NewClockTZ(tz string, h uint, i uint, args ...uint) Clock { c := NewClock(h, i, args...) if c.valid() { @@ -227,7 +237,8 @@ func NewClockTZ(tz string, h uint, i uint, args ...uint) Clock { return c } -// NewClockFromTime retourne l’heure à partir d’une date Go. +// NewClockFromTime retourne l’heure à partir d’une date Go t. +// Si la précision p n’est pas définie, c’est une précision à la ms. func NewClockFromTime(t time.Time, p ...Precision) Clock { var ( pp = PrecisionMillisecond @@ -243,15 +254,16 @@ func NewClockFromTime(t time.Time, p ...Precision) Clock { return setClock(ms(uint(h), uint(i), uint(s), uint(v)), pp, l) } -// ClockNow retourne l’heure actuelle. Si tz est renseignée, +// ClockNow retourne l’heure actuelle avec une précision p. Si tz est renseignée, // l’heure est placée dans le fuseau horaire indiqué. func ClockNow(p Precision, tz ...string) Clock { return NewClockFromTime(now(tz...), p) } +// Now retourne l’heure actuelle avec la même précision et le même fuseau horaire que c. func (c Clock) Now() Clock { return ClockNow(c.p(), c.location.String()) } -// ClockGuess retourne l’heure à partir de e en essayant de deviner le format. +// ClockGuess retourne l’heure à partir de e et à la précision p 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) @@ -260,7 +272,7 @@ func ClockGuess(p Precision, e string, tz ...string) Clock { return ClockNil() } -// ClockParse retourne l’heure à partir de e en spécifiant le format f. +// ClockParse retourne l’heure à partir de e et à la précision p 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) @@ -299,19 +311,19 @@ func (c Clock) Hour() uint { return c.h() } // Clock retourne l’heure, la minute, la seconde et la milliseconde. func (c Clock) Clock() (h, i, s, v uint) { return c.h(), c.i(), c.s(), c.v() } -// SetMilli modifie la milliseconde. +// SetMilli modifie la milliseconde de la seconde. func (c Clock) SetMilli(v uint) Clock { return newClock(c.setV(v), c.location) } -// SetSecond modifie la seconde. +// SetSecond modifie la seconde de la minute, et la milliseconde, si v est renseignée. func (c Clock) SetSecond(s uint, v ...uint) Clock { return newClock(c.setS(s, v...), c.location) } -// SetMinute modifie la minute. +// SetMinute modifie la minute de l’heure, ainsi que la seconde et la millisecondes si elles sont renseignées. func (c Clock) SetMinute(i uint, args ...uint) Clock { return newClock(c.setI(i, args...), c.location) } -// SetHour modifie l’heure. +// SetHour modifie l’heure et, facultativement, la minute, la seconde et la milliseconde. func (c Clock) SetHour(h uint, args ...uint) Clock { return newClock(c.setH(h, args...), c.location) } -// SetPrecision modifie la précision. +// SetPrecision retourne l’horloge avec la précision p. func (c Clock) SetPrecision(p Precision) Clock { return newClock(c.setP(p), c.location) } // Add ajoute un nombre d’heures et de minutes, et, facultativement, @@ -327,7 +339,7 @@ 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. +// AddMilli ajoute une durée en millisecondes. func (c Clock) AddMilli(v int) Clock { return newClock(c.addV(v), c.location) } // AddDuration ajoute une durée. @@ -369,11 +381,12 @@ func (c Clock) In(l *time.Location) Clock { return NewClockFromTime(c.ToTime().In(l)) } -// ToTimezone retourne l’heure dans le fuseau horaire indiqué. -func (c Clock) ToTimezone(tz string) Clock { return c.In(timezone(tz)) } +// InTZ agit comme In mais en fournissant le fuseau horaire sous forme +// de chaîne de caractères. +func (c Clock) inTZ(tz string) Clock { return c.In(timezone(tz)) } -// Location retourne le fuseau horaire. -func (c Clock) Location() *time.Location { return c.location } +// TZ retourne le fuseau horaire. +func (c Clock) TZ() *time.Location { return c.location } // IsDST retourne vrai si le fuseau horaire est à l’heure d’été. func (c Clock) IsDST() bool { return c.dst(c.location) } @@ -398,14 +411,26 @@ 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()) } +// IsNow retourne vrai si l’horloge est à l’heure actuelle. +func (c Clock) IsNow() bool { return c.Eq(c.Now()) } + +// IsPast retourne vrai si l’horloge est située dans le passé. +func (c Clock) IsPast() bool { return c.Lt(c.Now()) } + +// IsFuture retourne vrai si l’horloge est située dans le futur. func (c Clock) IsFuture() bool { return c.Gt(c.Now()) } -func (c1 Clock) DiffInMillis(c2 Clock) int { return c1.subV(c2.clock) } +// DiffInMillis retourne c1 - c2 en millisecondes. +func (c1 Clock) DiffInMillis(c2 Clock) int { return c1.subV(c2.clock) } + +// DiffInSeconds retourne c1 - c2 en secondes. func (c1 Clock) DiffInSeconds(c2 Clock) int { return c1.subS(c2.clock) } + +// DiffInMinutes retourne c1 - c2 en minutes. func (c1 Clock) DiffInMinutes(c2 Clock) int { return c1.subI(c2.clock) } -func (c1 Clock) DiffInHours(c2 Clock) int { return c1.subH(c2.clock) } + +// DiffInHours retourne c1 - c2 en heures. +func (c1 Clock) DiffInHours(c2 Clock) int { return c1.subH(c2.clock) } // Format retourne une représentation de l’heure au format spécifié. func (c Clock) Format(format string) string { return c.f(format, c.location) } diff --git a/datetime/const.go b/datetime/const.go index 9e270d4..c98ef83 100644 --- a/datetime/const.go +++ b/datetime/const.go @@ -103,17 +103,17 @@ const ( ) var ( - // représentation de l’unité sous forme de caractère - unitToByte = map[Unit]byte{ - NoUnit: 0, - Millisecond: 'N', - Second: 'S', - Minute: 'M', - Hour: 'H', - Day: 'd', - Week: 'w', - Month: 'm', - Year: 'y', + // Représentation de l’unité sous forme de caractères + unitToString = map[Unit]string{ + NoUnit: "", + Millisecond: "ms", + Second: "s", + Minute: "m", + Hour: "h", + Day: "J", + Week: "S", + Month: "M", + Year: "A", } maskDate = map[string]date{ diff --git a/datetime/date.go b/datetime/date.go index 9cb9f83..803d0cb 100644 --- a/datetime/date.go +++ b/datetime/date.go @@ -205,12 +205,14 @@ func newDate(t date, l *time.Location) Date { return DateNil() } -// NewDate retourne la date dans le fuseau horaire demandé. +// 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. +// NewDateFromTime retourne la date à partir d’une date Go t. func NewDateFromTime(t time.Time) Date { y, m, d := t.Date() l := t.Location() @@ -222,6 +224,7 @@ func NewDateFromTime(t time.Time) Date { // 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. @@ -233,7 +236,7 @@ func DateGuess(e string, tz ...string) Date { return DateNil() } -// DateParse retourne la date à partir de v en spécifiant le format f. +// 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) @@ -263,7 +266,7 @@ 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. +// 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. @@ -290,7 +293,7 @@ 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. +// 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. @@ -314,7 +317,7 @@ 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. +// 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. @@ -329,11 +332,12 @@ func (t Date) In(l *time.Location) Date { return NewDateFromTime(t.t(t.location).In(l)) } -// ToTimezone retourne la date dans le fuseau horaire indiqué. +// 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)) } -// Location retourne le fuseau horaire. -func (t Date) Location() *time.Location { return t.location } +// 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) } @@ -341,10 +345,11 @@ 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. +// 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 } @@ -354,21 +359,33 @@ 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()) } +// 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()) } -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) } +// 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) } -func (t1 Date) DiffInYears(t2 Date) int { return t1.subY(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(format string) string { return t.f(format, t.location) } +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) } diff --git a/datetime/datetime.go b/datetime/datetime.go index 46ae3ae..ebb92bc 100644 --- a/datetime/datetime.go +++ b/datetime/datetime.go @@ -176,7 +176,7 @@ func (dt datetime) f(f string, l *time.Location) string { } func (dt datetime) str(l *time.Location) string { return dt.f("Y-m-d", l) } -// DateTime représente une indication de temps. +// DateTime représente une indication de temps (date + heure). type DateTime struct { datetime location *time.Location @@ -193,7 +193,10 @@ func initDT(dt datetime, l *time.Location) DateTime { return DateTime{dt, l} } -// NewDateTime retourne un temps dans le fuseau horaire par défaut. +// NewDateTime retourne un temps dans le fuseau horaire par défaut avec : +// - y, m, d : année, mois, jour +// - h, i : heure, minute, +// - args : seconde, puis milliseconde. // 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) @@ -209,7 +212,9 @@ func NewDateTimeTZ(tz string, y int, m, d, h, i uint, args ...uint) DateTime { return dt } -// NewDateTimeFromTime retourne le temps à partir d’une date Go. +// NewDateTimeFromTime retourne le temps à partir d’une date Go t. +// Si la précision p est fournie, c’est la précision utilisée, +// sinon, c’est la précision à la milliseconde. func NewDateTimeFromTime(t time.Time, p ...Precision) DateTime { var ( pp = PrecisionMillisecond @@ -226,15 +231,16 @@ func NewDateTimeFromTime(t time.Time, p ...Precision) DateTime { 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, +// DateTimeNow retourne le temps à la précision p. 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) } +// Now retourne le temps actuel avec la précision et le fuseau horaire de dt. 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. +// DateTimeGuess retourne le temps à 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) @@ -243,7 +249,7 @@ func DateTimeGuess(p Precision, e string, tz ...string) DateTime { return DateTimeNil() } -// DateTimeParse retourne la date à partir de e en spécifiant le format f. +// DateTimeParse retourne le temps à 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) @@ -255,7 +261,7 @@ func DateTimeParse(p Precision, e, f string, tz ...string) DateTime { // Precision retourne la précision de l’heure. func (dt DateTime) Precision() Precision { return dt.p() } -// IsNil retourne vrai si la date est nulle. +// IsNil retourne vrai si le temps est nul. func (dt DateTime) IsNil() bool { return !dt.valid() } // Year retourne l’année. @@ -264,7 +270,7 @@ func (dt DateTime) Year() int { return dt.y() } // Month retourne le mois. func (dt DateTime) Month() uint { return dt.m() } -// Day retourne le . +// Day retourne le jour. func (dt DateTime) Day() uint { return dt.d() } // Hour retourne l’heure. @@ -318,32 +324,32 @@ func (dt DateTime) ToClock() Clock { return newClock(dt.clock, dt.location) } // ToDateClock sépare la partie date et la partie heure. func (dt DateTime) ToDateClock() (Date, Clock) { return dt.ToDate(), dt.ToClock() } -// SetYear modifie l’année. +// SetYear modifie l’année, et facultativement, le mois, le jour, etc. func (dt DateTime) SetYear(y int, args ...uint) DateTime { return initDT(dt.setY(y, args...), dt.location) } -// SetMonth modifie le mois. +// SetMonth modifie le mois et facultativement le jour, l’heure, etc. func (dt DateTime) SetMonth(m uint, args ...uint) DateTime { return initDT(dt.setM(m, args...), dt.location) } -// SetDay modifie le jour. +// SetDay modifie le jour et facultativement l’heure, la minute, etc.. func (dt DateTime) SetDay(d uint, args ...uint) DateTime { return initDT(dt.setD(d, args...), dt.location) } -// SetHour modifie l’heure. +// SetHour modifie l’heure et facultativement la minute, la seconde, etc. func (dt DateTime) SetHour(h uint, args ...uint) DateTime { return initDT(dt.setH(h, args...), dt.location) } -// SetMinute modifie la minute. +// SetMinute modifie la minute et facultativement la seconde et la milliseconde. func (dt DateTime) SetMinute(i uint, args ...uint) DateTime { return initDT(dt.setI(i, args...), dt.location) } -// SetSecond modifie la seconde. +// SetSecond modifie la seconde et facultativement la milliseconde. func (dt DateTime) SetSecond(s uint, args ...uint) DateTime { return initDT(dt.setS(s, args...), dt.location) } @@ -395,52 +401,52 @@ func (dt DateTime) AddMilli(v int) DateTime { return initDT(dt.addV(v), dt.locat // AddDuration incrémente une durée. func (dt DateTime) AddDuration(dd Duration) DateTime { return initDT(dt.addDD(dd), dt.location) } -// BeginOfYear retourne la date en début d’année. +// BeginOfYear retourne le temps en début d’année au début du jour. func (dt DateTime) BeginOfYear() DateTime { return initDT(dt.bY(), dt.location) } -// EndOfYear retourne la date en fin d’année. +// EndOfYear retourne le temps en fin d’année à la fin du jour. func (dt DateTime) EndOfYear() DateTime { return initDT(dt.eY(), dt.location) } -// BeginOfMonth retourne la date en début de mois. +// BeginOfMonth retourne le temps en début de mois au début du jour. func (dt DateTime) BeginOfMonth() DateTime { return initDT(dt.bM(), dt.location) } -// EndOfMonth retourne la date en fin de mois. +// EndOfMonth retourne le temps en fin de mois à la fin du jour. func (dt DateTime) EndOfMonth() DateTime { return initDT(dt.eM(), dt.location) } -// BeginOfWeek retourne la date en début de semaine. +// BeginOfWeek retourne le temps en début de semaine au début du jour. func (dt DateTime) BeginOfWeek() DateTime { return initDT(dt.bW(dt.location), dt.location) } -// EndOfWeek retourne la date en fin de semaine. +// EndOfWeek retourne le temps en fin de semaine à la fin du jour. func (dt DateTime) EndOfWeek() DateTime { return initDT(dt.eW(dt.location), dt.location) } -// BeginOfDay retourne la date en début de jour. +// BeginOfDay retourne le temps en début de jour. func (dt DateTime) BeginOfDay() DateTime { return initDT(dt.bD(), dt.location) } -// EndOfDay retourne la date en fin de jour. +// EndOfDay retourne le temps en fin de jour. func (dt DateTime) EndOfDay() DateTime { return initDT(dt.eD(), dt.location) } -// BeginOfHour retourne la date en début d’heure. +// BeginOfHour retourne le temps en début d’heure. func (dt DateTime) BeginOfHour() DateTime { return initDT(dt.bH(), dt.location) } -// EndOfHour retourne la date en fin d’heure. +// EndOfHour retourne le temps en fin d’heure. func (dt DateTime) EndOfHour() DateTime { return initDT(dt.eH(), dt.location) } -// BeginOfMinute retourne la date en début de minute. +// BeginOfMinute retourne le temps en début de minute. func (dt DateTime) BeginOfMinute() DateTime { return initDT(dt.bI(), dt.location) } -// EndOfMinute retourne la date en fin de minute. +// EndOfMinute retourne le temps en fin de minute. func (dt DateTime) EndOfMinute() DateTime { return initDT(dt.eI(), dt.location) } -// BeginOfSecond retourne la date en début de seconde. +// BeginOfSecond retourne le temps en début de seconde. func (dt DateTime) BeginOfSecond() DateTime { return initDT(dt.bS(), dt.location) } -// EndOfSecond retourne la date en fin de seconde. +// EndOfSecond retourne le temps en fin de seconde. func (dt DateTime) EndOfSecond() DateTime { return initDT(dt.eS(), dt.location) } -// ToTime retourne la date dans le fuseau horaire indiqué. +// ToTime retourne le temps 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é. +// In retourne le temps dans le fuseau horaire indiqué. func (dt DateTime) In(l *time.Location) DateTime { if dt.IsNil() { return DateTimeNil() @@ -449,11 +455,12 @@ func (dt DateTime) In(l *time.Location) DateTime { 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)) } +// InTZ agit comme In mais en fournissant le fuseau horaire sous forme +// de chaîne de caractères. +func (dt DateTime) InTZ(tz string) DateTime { return dt.In(timezone(tz)) } -// Location retourne le fuseau horaire. -func (dt DateTime) Location() *time.Location { return dt.location } +// TZ retourne le fuseau horaire. +func (dt DateTime) TZ() *time.Location { return dt.location } // IsDST retourne vrai si le fuseau horaire est à l’heure d’été. func (dt DateTime) IsDST() bool { return dt.dst(dt.location) } @@ -464,7 +471,7 @@ 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. +// Compare compare 2 temps après normalisation du fuseau horaire et de la précision. func (dt1 DateTime) Compare(dt2 DateTime) int { return dt1.cmp(dt2.datetime) } func (dt1 DateTime) Eq(dt2 DateTime) bool { return dt1.Compare(dt2) == 0 } @@ -474,11 +481,16 @@ 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()) } +// IsNow retourne vrai si le temps est le temps actuel. +func (dt DateTime) IsNow() bool { return dt.Eq(dt.Now()) } + +// IsPast retourne vrai si le temps est situé dans le passé. +func (dt DateTime) IsPast() bool { return dt.Lt(dt.Now()) } + +// IsPast retourne vrai si le temps est situé dans le futur. func (dt DateTime) IsFuture() bool { return dt.Gt(dt.Now()) } -// DiffInMills retourne dt1-dt2 en millisecondes. +// DiffInMills retourne dt1 - dt2 en millisecondes. func (dt1 DateTime) DiffInMillis(dt2 DateTime) int { return dt1.subV(dt2.datetime, dt1.location, dt2.location) } @@ -514,9 +526,9 @@ func (dt1 DateTime) DiffInMonths(dt2 DateTime) int { return dt1.subM(dt2.datetim // 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é. +// Format retourne une représentation du temps 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', +// String retourne une représentation du temps 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) } diff --git a/datetime/duration.go b/datetime/duration.go index c6687a0..17b214b 100644 --- a/datetime/duration.go +++ b/datetime/duration.go @@ -2,11 +2,15 @@ package datetime import ( "fmt" + "regexp" + "strconv" ) // Unit est une unité de durée. type Unit uint +func (u Unit) String() string { return unitToString[u] } + // Duration est une durée entre deux repères de temps. type Duration int @@ -19,6 +23,26 @@ func NewDuration(value int, unit Unit) Duration { return (Duration(value) << bitsUnit) | Duration(unit) } +// ParseDuration retourne une durée à partir d’une chaîne de caractères. +func ParseDuration(e string) Duration { + r := regexp.MustCompile(`^(\d+)(\w+)$`) + if !r.MatchString(e) { + return DurationNil + } + spl := r.FindAllStringSubmatch(e, 1)[0] + + v, _ := strconv.Atoi(spl[1]) + u := NoUnit + for uu, su := range unitToString { + if su == spl[2] { + u = uu + break + } + } + + return NewDuration(v, u) +} + // Value retourne la valeur de la durée, sans l’unité. func (d Duration) Value() int { return int(d >> bitsUnit) } @@ -51,5 +75,5 @@ func (d Duration) String() string { v, u := d.Value(), d.Unit() - return fmt.Sprintf("%d%c", v, unitToByte[u]) + return fmt.Sprintf("%d%s", v, u) } diff --git a/datetime/format.go b/datetime/format.go index 10eb2fe..60c562f 100644 --- a/datetime/format.go +++ b/datetime/format.go @@ -54,7 +54,7 @@ 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))) + return fmt.Sprintf("%03d", uint(sit(uint(h), uint(m), uint(s), uint(n)))) } func format_g(t time.Time) string { h := t.Hour() % 12 diff --git a/datetime/range.go b/datetime/range.go index 0efeefe..aa2b42e 100644 --- a/datetime/range.go +++ b/datetime/range.go @@ -1,6 +1,8 @@ package datetime import ( + "fmt" + . "gitea.zaclys.com/bvaudour/gob/option" ) @@ -16,6 +18,8 @@ type TimeComparator[E any] interface { IsPast() bool IsFuture() bool Now() E + String() string + IsNil() bool } // Min retourne le temps le plus petit. @@ -40,21 +44,74 @@ func Max[C TimeComparator[C]](e C, args ...C) C { return out } -// Range représente une période entre deux bornes temporelles. +func minB[C TimeComparator[C]](e1, e2 C) C { + if e1.IsNil() { + return e1 + } else if e2.IsNil() { + return e2 + } + return Min(e1, e2) +} +func maxB[C TimeComparator[C]](e1, e2 C) C { + if e1.IsNil() { + return e2 + } else if e2.IsNil() { + return e1 + } + return Max(e1, e2) +} +func minE[C TimeComparator[C]](e1, e2 C) C { + if e1.IsNil() { + return e2 + } else if e2.IsNil() { + return e1 + } + return Min(e1, e2) +} +func maxE[C TimeComparator[C]](e1, e2 C) C { + if e1.IsNil() { + return e1 + } else if e2.IsNil() { + return e2 + } + return Max(e1, e2) +} + +// Range représente une période entre deux bornes temporelles begin et end. +// Si begin est nul, cela représente ]-∞ ; end]. +// Se end est nul, cela représent [begin; +∞[. type Range[C TimeComparator[C]] struct { begin, end C } // NewRange retourne une période entre begin et end. -// Si begin < end, les bornes sont inversées. +// Si begin < end, le programme panique. func NewRange[C TimeComparator[C]](begin, end C) Range[C] { - if begin.Gt(end) { - begin, end = end, begin + if !begin.IsNil() && !end.IsNil() && begin.Gt(end) { + panic("begin should be located in past of end.") } return Range[C]{begin, end} } +func gt[C TimeComparator[C]](e C, strict ...bool) func(C) bool { + if len(strict) == 0 || !strict[0] { + return e.Ge + } + return e.Ge +} +func lt[C TimeComparator[C]](e C, strict ...bool) func(C) bool { + if len(strict) == 0 || !strict[0] { + return e.Le + } + return e.Le +} + +func (r Range[C]) bE(e C, strict ...bool) bool { return r.end.IsNil() || lt(e, strict...)(r.end) } +func (r Range[C]) aE(e C, strict ...bool) bool { return !r.end.IsNil() && gt(e, strict...)(r.end) } +func (r Range[C]) bB(e C, strict ...bool) bool { return !r.begin.IsNil() || lt(e, strict...)(r.begin) } +func (r Range[C]) aB(e C, strict ...bool) bool { return r.begin.IsNil() || gt(e, strict...)(r.begin) } + // Begin retourne le début de la période. func (r Range[C]) Begin() C { return r.begin } @@ -62,22 +119,22 @@ func (r Range[C]) Begin() C { return r.begin } func (r Range[C]) End() C { return r.end } // 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) } +func (r Range[C]) Before(e C) bool { return r.bB(e, true) } // 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) } +func (r Range[C]) After(e C) bool { return r.aE(e, true) } // Contains retourne vrai si e ∈ [begin;end]. -func (r Range[C]) Contains(e C) bool { return e.Ge(r.begin) && e.Le(r.end) } +func (r Range[C]) Contains(e C) bool { return r.aB(e) && r.bE(e) } // ContainsStrictLeft retourne vrai si e ∈ ]begin;end]. -func (r Range[C]) ContainsStrictLeft(e C) bool { return e.Gt(r.begin) && e.Le(r.end) } +func (r Range[C]) ContainsStrictLeft(e C) bool { return r.aB(e, true) && r.bE(e) } // ContainsStrictRight retourne vrai si e ∈ [begin;end[. -func (r Range[C]) ContainsStrictRight(e C) bool { return e.Ge(r.begin) && e.Lt(r.end) } +func (r Range[C]) ContainsStrictRight(e C) bool { return r.aB(e) && r.bE(e, true) } // ContainsStrict retourne vrai si e ∈ ]begin;end[. -func (r Range[C]) ContainsStrict(e C) bool { return e.Gt(r.begin) && e.Lt(r.end) } +func (r Range[C]) ContainsStrict(e C) bool { return r.aB(e, true) && r.bE(e, true) } // IsNow retourne vrai si la période est en cours. func (r Range[C]) IsNow() bool { return r.Contains(r.begin.Now()) } @@ -89,14 +146,14 @@ func (r Range[C]) IsPast() bool { return r.Before(r.begin.Now()) } func (r Range[C]) IsFuture() bool { return r.After(r.begin.Now()) } // 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) } +func (r1 Range[C]) BeforeRange(r2 Range[C]) bool { return !r1.end.IsNil() && r2.bB(r1.end) } // 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) } +func (r1 Range[C]) AfterRange(r2 Range[C]) bool { return !r1.begin.IsNil() && r2.aE(r1.begin) } // 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) + return ((r2.begin.IsNil() && r1.begin.IsNil()) || r1.aB(r2.begin)) && ((r2.end.IsNil() && r1.end.IsNil()) || r1.bE(r2.end)) } // InRange retourne vrai si r2 comprend intégralement r1. @@ -104,18 +161,18 @@ func (r1 Range[C]) InRange(r2 Range[C]) bool { return r2.ContainsRange(r1) } // 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) + return (!r1.end.IsNil() && r2.bB(r1.end)) || (!r1.begin.IsNil() && r2.aE(r1.begin)) } // 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) + return (r1.begin.IsNil() || r2.bE(r1.begin, true)) && (r1.end.IsNil() || r2.aB(r1.end, true)) } // 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))) + result = Some(NewRange(maxB(r1.begin, r2.begin), minE(r1.end, r2.end))) } return @@ -123,8 +180,8 @@ func (r1 Range[C]) Intersection(r2 Range[C]) (result Option[Range[C]]) { // 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))) + if (r1.begin.IsNil() || r2.bE(r1.begin)) && (r1.end.IsNil() || r2.aB(r1.end)) { + result = Some(NewRange(minB(r1.begin, r2.begin), maxE(r1.end, r2.end))) } return @@ -132,27 +189,37 @@ func (r1 Range[C]) Joins(r2 Range[C]) (result Option[Range[C]]) { // 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, r1.begin) + b1, b2 := minB(r1.begin, r2.begin), maxB(r1.begin, r2.begin) + e1, e2 := minE(r1.end, r2.end), maxE(r1.end, r2.end) + + if b1.Ne(b2) { + e := b2 + if !e1.IsNil() { + e = Min(b2, e1) } - result = append(result, NewRange(begin, end)) + result = append(result, NewRange(b1, e)) } - 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, r1.end) + if e1.Ne(e2) { + b := e1 + if !b2.IsNil() { + b = Max(b2, e1) } - result = append(result, NewRange(begin, end)) + result = append(result, NewRange(b, e2)) } return } + +// String retourne la représentation textuelle de la période. +func (r Range[C]) String() string { + b, e := r.begin.String(), r.end.String() + if r.begin.IsNil() { + b = "-∞" + } + if r.end.IsNil() { + e = "+∞" + } + + return fmt.Sprintf("[%s ; %s]", b, e) +}