registry/time/functions.go
package time
import (
"strconv"
"strings"
"time"
)
// Date formats a given date or current time into a specified format string.
//
// Parameters:
//
// fmt string - the format string.
// date any - the date to format or the current time if not a date type.
//
// Returns:
//
// string - the formatted date.
// error - when the timezone is invalid or the date is not in a valid format.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: date].
//
// [Sprout Documentation: date]: https://docs.atom.codes/sprout/registries/time#date
func (tr *TimeRegistry) Date(fmt string, date any) (string, error) {
return tr.DateInZone(fmt, date, "Local")
}
// DateInZone formats a given date or current time into a specified format string in a specified timezone.
//
// Parameters:
//
// fmt string - the format string.
// date any - the date to format, in various acceptable formats.
// zone string - the timezone name.
//
// Returns:
//
// string - the formatted date.
// error - when the timezone is invalid or the date is not in a valid format.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: dateInZone].
//
// [Sprout Documentation: dateInZone]: https://docs.atom.codes/sprout/registries/time#dateinzone
func (tr *TimeRegistry) DateInZone(fmt string, date any, zone string) (string, error) {
// TODO: Change signature
var t time.Time
switch date := date.(type) {
default:
t = time.Now()
case time.Time:
t = date
case *time.Time:
t = *date
case int64:
t = time.Unix(date, 0)
case int:
t = time.Unix(int64(date), 0)
case int32:
t = time.Unix(int64(date), 0)
}
loc, err := time.LoadLocation(zone)
if err != nil {
return t.In(time.UTC).Format(fmt), err
}
return t.In(loc).Format(fmt), nil
}
// Duration converts seconds into a human-readable duration string.
//
// Parameters:
//
// sec any - the duration in seconds.
//
// Returns:
//
// string - the human-readable duration.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: duration].
//
// [Sprout Documentation: duration]: https://docs.atom.codes/sprout/registries/time#duration
func (tr *TimeRegistry) Duration(sec any) string {
var n int64
switch value := sec.(type) {
case string:
n, _ = strconv.ParseInt(value, 10, 64)
case int64:
n = value
case int:
n = int64(value)
case float64:
n = int64(value)
case float32:
n = int64(value)
default:
n = 0
}
return (time.Duration(n) * time.Second).String()
}
// DateAgo calculates how much time has passed since the given date.
//
// Parameters:
//
// date any - the starting date for the calculation.
//
// Returns:
//
// string - a human-readable string describing how long ago the date was.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: dateAgo].
//
// [Sprout Documentation: dateAgo]: https://docs.atom.codes/sprout/registries/time#dateago
func (tr *TimeRegistry) DateAgo(date any) string {
var t time.Time
switch date := date.(type) {
default:
t = time.Now()
case time.Time:
t = date
case *time.Time:
t = *date
case int64:
t = time.Unix(date, 0)
case int32:
t = time.Unix(int64(date), 0)
case int:
t = time.Unix(int64(date), 0)
}
// Drop resolution to seconds
duration := time.Since(t).Round(time.Second)
return duration.String()
}
// Now returns the current time.
//
// Returns:
//
// time.Time - the current time.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: now].
//
// [Sprout Documentation: now]: https://docs.atom.codes/sprout/registries/time#now
func (tr *TimeRegistry) Now() time.Time {
return time.Now()
}
// UnixEpoch returns the Unix epoch timestamp of a given date.
//
// Parameters:
//
// date time.Time - the date to convert to a Unix timestamp.
//
// Returns:
//
// string - the Unix timestamp as a string.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: unixEpoch].
//
// [Sprout Documentation: unixEpoch]: https://docs.atom.codes/sprout/registries/time#unixepoch
func (tr *TimeRegistry) UnixEpoch(date time.Time) string {
return strconv.FormatInt(date.Unix(), 10)
}
// DateModify adjusts a given date by a specified duration. If the duration
// format is incorrect, it returns an error.
//
// Parameters:
//
// fmt string - the duration string to add to the date, such as "2h" for two hours.
// date time.Time - the date to modify.
//
// Returns:
//
// time.Time - the modified date after adding the duration
// error - an error if the duration format is incorrect
//
// For an example of this function in a Go template, refer to [Sprout Documentation: dateModify].
//
// [Sprout Documentation: dateModify]: https://docs.atom.codes/sprout/registries/time#datemodify
func (tr *TimeRegistry) DateModify(fmt string, date time.Time) (time.Time, error) {
d, err := time.ParseDuration(fmt)
if err != nil {
return time.Time{}, err
}
return date.Add(d), nil
}
// DurationRound rounds a duration to the nearest significant unit, such as years or seconds.
//
// Parameters:
//
// duration any - the duration to round.
//
// Returns:
//
// string - the rounded duration.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: durationRound].
//
// [Sprout Documentation: durationRound]: https://docs.atom.codes/sprout/registries/time#durationround
func (tr *TimeRegistry) DurationRound(duration any) string {
var d time.Duration
switch duration := duration.(type) {
case string:
d, _ = time.ParseDuration(duration)
case int64:
d = time.Duration(duration)
case time.Duration:
d = duration
case time.Time:
d = time.Since(duration)
default:
d = 0
}
u := uint64(d)
neg := d < 0
if neg {
u = -u
}
if u == 0 {
return "0s"
}
const (
year = uint64(time.Hour) * 24 * 365
month = uint64(time.Hour) * 24 * 30
day = uint64(time.Hour) * 24
hour = uint64(time.Hour)
minute = uint64(time.Minute)
second = uint64(time.Second)
)
var b strings.Builder
b.Grow(3)
if neg {
b.WriteByte('-')
}
switch {
case u > year:
b.WriteString(strconv.FormatUint(u/year, 10))
b.WriteRune('y')
case u > month:
b.WriteString(strconv.FormatUint(u/month, 10))
b.WriteString("mo")
case u > day:
b.WriteString(strconv.FormatUint(u/day, 10))
b.WriteRune('d')
case u > hour:
b.WriteString(strconv.FormatUint(u/hour, 10))
b.WriteRune('h')
case u > minute:
b.WriteString(strconv.FormatUint(u/minute, 10))
b.WriteRune('m')
case u > second:
b.WriteString(strconv.FormatUint(u/second, 10))
b.WriteRune('s')
}
return b.String()
}
// HtmlDate formats a date into a standard HTML date format (YYYY-MM-DD).
//
// Parameters:
//
// date any - the date to format.
//
// Returns:
//
// string - the formatted date in HTML format.
// error - when the timezone is invalid or the date is not in a valid format.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: htmlDate].
//
// [Sprout Documentation: htmlDate]: https://docs.atom.codes/sprout/registries/time#htmldate
func (tr *TimeRegistry) HtmlDate(date any) (string, error) {
return tr.DateInZone("2006-01-02", date, "Local")
}
// HtmlDateInZone formats a date into a standard HTML date format (YYYY-MM-DD) in a specified timezone.
//
// Parameters:
//
// date any - the date to format.
// zone string - the timezone name.
//
// Returns:
//
// string - the formatted date in HTML format.
// error - when the timezone is invalid or the date is not in a valid format.
//
// For an example of this function in a Go template, refer to [Sprout Documentation: htmlDateInZone].
//
// [Sprout Documentation: htmlDateInZone]: https://docs.atom.codes/sprout/registries/time#htmldateinzone
func (tr *TimeRegistry) HtmlDateInZone(date any, zone string) (string, error) {
// TODO: Change signature
return tr.DateInZone("2006-01-02", date, zone)
}