Skip to content

Commit

Permalink
add date interval utils
Browse files Browse the repository at this point in the history
  • Loading branch information
miku committed Nov 30, 2021
1 parent 2f61c4c commit e424bb9
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
79 changes: 79 additions & 0 deletions dateutil/intervals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Package dateutil provides interval handling and a custom flag.
package dateutil

import (
"time"

"github.com/araddon/dateparse"
"github.com/jinzhu/now"
)

var (
// EveryMinute will chop up a timespan into 60s intervals;
// https://english.stackexchange.com/questions/3091/weekly-daily-hourly-minutely.
EveryMinute = makeIntervalFunc(padLMinute, padRMinute)
Hourly = makeIntervalFunc(padLHour, padRHour)
Daily = makeIntervalFunc(padLDay, padRDay)
Weekly = makeIntervalFunc(padLWeek, padRWeek)
Monthly = makeIntervalFunc(padLMonth, padRMonth)

padLMinute = func(t time.Time) time.Time { return now.With(t).BeginningOfMinute() }
padRMinute = func(t time.Time) time.Time { return now.With(t).EndOfMinute() }
padLHour = func(t time.Time) time.Time { return now.With(t).BeginningOfHour() }
padRHour = func(t time.Time) time.Time { return now.With(t).EndOfHour() }
padLDay = func(t time.Time) time.Time { return now.With(t).BeginningOfDay() }
padRDay = func(t time.Time) time.Time { return now.With(t).EndOfDay() }
padLWeek = func(t time.Time) time.Time { return now.With(t).BeginningOfWeek() }
padRWeek = func(t time.Time) time.Time { return now.With(t).EndOfWeek() }
padLMonth = func(t time.Time) time.Time { return now.With(t).BeginningOfMonth() }
padRMonth = func(t time.Time) time.Time { return now.With(t).EndOfMonth() }
)

// Interval groups start and end.
type Interval struct {
Start time.Time
End time.Time
}

type (
// padFunc allows to move a given time back and forth.
padFunc func(t time.Time) time.Time
// intervalFunc takes a start and endtime and returns a number of
// intervals. How intervals are generated is flexible.
intervalFunc func(s, e time.Time) []Interval
)

// makeIntervalFunc is a helper to create daily, weekly and other intervals.
// Given two shiftFuncs (to mark the beginning of an interval and the end), we
// return a function, that will allow us to generate intervals.
// TODO: We only need right pad, no?
func makeIntervalFunc(padLeft, padRight padFunc) intervalFunc {
return func(s, e time.Time) (result []Interval) {
if e.Before(s) || e.Equal(s) {
return
}
e = e.Add(-1 * time.Second)
var (
l time.Time = s
r time.Time
)
for {
r = padRight(l)
result = append(result, Interval{l, r})
l = padLeft(r.Add(1 * time.Second))
if l.After(e) {
break
}
}
return result
}
}

// MustParse will panic on an unparsable date string.
func MustParse(value string) time.Time {
t, err := dateparse.ParseStrict(value)
if err != nil {
panic(err)
}
return t
}
76 changes: 76 additions & 0 deletions dateutil/intervals_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package dateutil

import (
"testing"
"time"
)

func TestMakeIntervalFunc(t *testing.T) {
var cases = []struct {
padLeft padFunc
padRight padFunc
start time.Time
end time.Time
numIntervals int
}{
{
padLeft: padLDay,
padRight: padRDay,
start: MustParse("2000-01-01"),
end: MustParse("2000-01-01"),
numIntervals: 0,
},
{
padLeft: padLDay,
padRight: padRDay,
start: MustParse("2000-01-01"),
end: MustParse("1999-01-01"),
numIntervals: 0,
},
{
padLeft: padLDay,
padRight: padRDay,
start: MustParse("2000-01-01"),
end: MustParse("2001-01-01"),
numIntervals: 366,
},
{
padLeft: padLHour,
padRight: padRHour,
start: MustParse("2000-01-01 10:00"),
end: MustParse("2000-01-01 12:00"),
numIntervals: 2,
},
{
padLeft: padLHour,
padRight: padRHour,
start: MustParse("2000-01-01 10:30"),
end: MustParse("2000-01-01 12:00"),
numIntervals: 2,
},
{
padLeft: padLMonth,
padRight: padRMonth,
start: MustParse("2000-01-01 10:30"),
end: MustParse("2000-01-01 12:00"),
numIntervals: 1,
},
}
for i, c := range cases {
f := makeIntervalFunc(c.padLeft, c.padRight)
ivs := f(c.start, c.end)
t.Logf("[%d] start: %v, end: %v", i, c.start, c.end)
switch len(ivs) {
case 0:
case 1:
t.Logf("[%d] [%v]", i, ivs[0])
case 2:
t.Logf("[%d] [%v, %v]", i, ivs[0], ivs[1])
default:
t.Logf("[%d] [%v, ..., %v]", i, ivs[0], ivs[len(ivs)-1])
}
if len(ivs) != c.numIntervals {
t.Fatalf("[%d] got %d, want %d", i, len(ivs), c.numIntervals)
}
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/abadojack/whatlanggo v1.0.1
github.com/adrg/xdg v0.3.4
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/beevik/etree v1.1.0
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
github.com/dustin/go-humanize v1.0.0
Expand All @@ -16,13 +17,15 @@ require (
github.com/gorilla/mux v1.8.0
github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69 // indirect
github.com/ilyakaznacheev/cleanenv v1.2.5
github.com/jinzhu/now v1.1.3
github.com/jmoiron/sqlx v1.3.4
github.com/joho/godotenv v1.4.0 // indirect
github.com/kennygrant/sanitize v1.2.4
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/klauspost/pgzip v1.2.5
github.com/kr/pretty v0.3.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lytics/logrus v0.0.0-20170528191427-4389a17ed024
github.com/mattn/go-colorable v0.1.10 // indirect
github.com/mattn/go-sqlite3 v1.14.8
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ github.com/adrg/xdg v0.3.4 h1:0BivHfQ0LSGQrFTaEZ0hyQLm/HAidci7m+1cT6wKKdA=
github.com/adrg/xdg v0.3.4/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZOs7ygH5BgQp4N+aYrZ2DNpWZ1KG3VOSOM=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
Expand Down Expand Up @@ -35,6 +37,8 @@ github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69 h1:umaj0TCQ9lWUUKy
github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69/go.mod h1:zdLK9ilQRSMjSeLKoZ4BqUfBT7jswTGF8zRlKEsiRXA=
github.com/ilyakaznacheev/cleanenv v1.2.5 h1:/SlcF9GaIvefWqFJzsccGG/NJdoaAwb7Mm7ImzhO3DM=
github.com/ilyakaznacheev/cleanenv v1.2.5/go.mod h1:/i3yhzwZ3s7hacNERGFwvlhwXMDcaqwIzmayEhbRplk=
github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
Expand All @@ -56,6 +60,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lytics/logrus v0.0.0-20170528191427-4389a17ed024 h1:QaKVrqyQRNPbdBNCpiU0Ei3iDQko3qoiUUXMiTWhzZM=
Expand All @@ -66,6 +72,7 @@ github.com/mattn/go-colorable v0.1.10/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMop
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
Expand All @@ -83,8 +90,10 @@ github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4 h1:JnVsYEQzhEcOspy
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4/go.mod h1:RL5+WRxWTAXqqCi9i+eZlHrUtO7AQujUqWi+xMohmc4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
github.com/segmentio/asm v1.0.1 h1:g9VK62hXylgXI4yJV+dLTu/1j7kTxG9bkUSYBxL9dpg=
github.com/segmentio/asm v1.0.1/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4=
github.com/segmentio/encoding v0.2.21 h1:hlRQz3Pv+/mBj+jqr46TVqqv6AuTwvP5aAxQ0Usd4gY=
Expand Down
28 changes: 28 additions & 0 deletions xflag/date.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package xflag

import (
"time"

"github.com/araddon/dateparse"
)

// Date can be used to parse command line args into dates.
type Date struct {
time.Time
}

// String returns a formatted date.
func (d *Date) String() string {
return d.Format("2006-01-02")
}

// Set parses a value into a date, relatively flexible due to
// araddon/dateparse, 2014-04-26 will work, but oct. 7, 1970, too.
func (d *Date) Set(value string) error {
t, err := dateparse.ParseStrict(value)
if err != nil {
return err
}
*d = Date{t}
return nil
}

0 comments on commit e424bb9

Please sign in to comment.