123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- package humanize
- import (
- "bytes"
- "fmt"
- "git.nspix.com/golang/kos/util/bs"
- "math"
- "sort"
- "time"
- )
- const (
- Day = 24 * time.Hour
- Week = 7 * Day
- Month = 30 * Day
- Year = 12 * Month
- LongTime = 37 * Year
- )
- // A RelTimeMagnitude struct contains a relative time point at which
- // the relative format of time will switch to a new format string. A
- // slice of these in ascending order by their "D" field is passed to
- // CustomRelTime to format durations.
- //
- // The Format field is a string that may contain a "%s" which will be
- // replaced with the appropriate signed label (e.g. "ago" or "from
- // now") and a "%d" that will be replaced by the quantity.
- //
- // The DivBy field is the amount of time the time difference must be
- // divided by in order to display correctly.
- //
- // e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
- // DivBy should be time.Minute so whatever the duration is will be
- // expressed in minutes.
- type RelTimeMagnitude struct {
- D time.Duration
- Format string
- DivBy time.Duration
- }
- var defaultMagnitudes = []RelTimeMagnitude{
- {time.Second, "now", time.Second},
- {2 * time.Second, "1 second %s", 1},
- {time.Minute, "%d seconds %s", time.Second},
- {2 * time.Minute, "1 minute %s", 1},
- {time.Hour, "%d minutes %s", time.Minute},
- {2 * time.Hour, "1 hour %s", 1},
- {Day, "%d hours %s", time.Hour},
- {2 * Day, "1 day %s", 1},
- {Week, "%d days %s", Day},
- {2 * Week, "1 week %s", 1},
- {Month, "%d weeks %s", Week},
- {2 * Month, "1 month %s", 1},
- {Year, "%d months %s", Month},
- {18 * Month, "1 year %s", 1},
- {2 * Year, "2 years %s", 1},
- {LongTime, "%d years %s", Year},
- {math.MaxInt64, "a long while %s", 1},
- }
- // RelTime formats a time into a relative string.
- //
- // It takes two times and two labels. In addition to the generic time
- // delta string (e.g. 5 minutes), the labels are used applied so that
- // the label corresponding to the smaller time is applied.
- //
- // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
- func RelTime(a, b time.Time, albl, blbl string) string {
- return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
- }
- // CustomRelTime formats a time into a relative string.
- //
- // It takes two times two labels and a table of relative time formats.
- // In addition to the generic time delta string (e.g. 5 minutes), the
- // labels are used applied so that the label corresponding to the
- // smaller time is applied.
- func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
- lbl := albl
- diff := b.Sub(a)
- if a.After(b) {
- lbl = blbl
- diff = a.Sub(b)
- }
- n := sort.Search(len(magnitudes), func(i int) bool {
- return magnitudes[i].D > diff
- })
- if n >= len(magnitudes) {
- n = len(magnitudes) - 1
- }
- mag := magnitudes[n]
- args := []interface{}{}
- escaped := false
- for _, ch := range mag.Format {
- if escaped {
- switch ch {
- case 's':
- args = append(args, lbl)
- case 'd':
- args = append(args, diff/mag.DivBy)
- }
- escaped = false
- } else {
- escaped = ch == '%'
- }
- }
- return fmt.Sprintf(mag.Format, args...)
- }
- type Time struct {
- tm time.Time
- }
- func Now() Time {
- return Time{tm: time.Now()}
- }
- func WrapTime(t time.Time) Time {
- return Time{tm: t}
- }
- func (t Time) Add(d Duration) Time {
- t.tm = t.tm.Add(d.Duration())
- return t
- }
- func (t Time) AddDuration(d time.Duration) Time {
- t.tm = t.tm.Add(d)
- return t
- }
- func (t Time) After(u Time) bool {
- return t.tm.After(u.tm)
- }
- func (t Time) AfterTime(u time.Time) bool {
- return t.tm.After(u)
- }
- func (t Time) Sub(u Time) Duration {
- return Duration(t.tm.Sub(u.tm))
- }
- func (t Time) SubTime(u time.Time) Duration {
- return Duration(t.tm.Sub(u))
- }
- func (t Time) Time() time.Time {
- return t.tm
- }
- func (t Time) String() string {
- return t.tm.Format(time.DateTime)
- }
- func (t Time) MarshalJSON() ([]byte, error) {
- s := `"` + t.tm.Format(time.DateTime) + `"`
- return bs.StringToBytes(s), nil
- }
- func (t Time) Ago() string {
- return RelTime(t.tm, time.Now(), "ago", "from now")
- }
- func (t *Time) UnmarshalJSON(buf []byte) (err error) {
- buf = bytes.TrimFunc(buf, func(r rune) bool {
- if r == '"' {
- return true
- }
- return false
- })
- t.tm, err = time.ParseInLocation(time.DateTime, bs.BytesToString(buf), time.Local)
- return err
- }
|