time sequence stuff
This commit is contained in:
parent
73e3e439c5
commit
566d798b32
10 changed files with 438 additions and 72 deletions
15
seq/array.go
15
seq/array.go
|
@ -1,5 +1,7 @@
|
||||||
package seq
|
package seq
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// NewArray creates a new array.
|
// NewArray creates a new array.
|
||||||
func NewArray(values ...float64) Array {
|
func NewArray(values ...float64) Array {
|
||||||
return Array(values)
|
return Array(values)
|
||||||
|
@ -17,3 +19,16 @@ func (a Array) Len() int {
|
||||||
func (a Array) GetValue(index int) float64 {
|
func (a Array) GetValue(index int) float64 {
|
||||||
return a[index]
|
return a[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArrayOfTimes wraps an array of times as a sequence provider.
|
||||||
|
type ArrayOfTimes []time.Time
|
||||||
|
|
||||||
|
// Len returns the length of the array.
|
||||||
|
func (aot ArrayOfTimes) Len() int {
|
||||||
|
return len(aot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue returns the time at the given index as a time.Time.
|
||||||
|
func (aot ArrayOfTimes) GetValue(index int) time.Time {
|
||||||
|
return aot[index]
|
||||||
|
}
|
||||||
|
|
15
seq/provider.go
Normal file
15
seq/provider.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package seq
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Provider is a provider for values for a seq.
|
||||||
|
type Provider interface {
|
||||||
|
Len() int
|
||||||
|
GetValue(int) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeProvider is a provider for values for a seq.
|
||||||
|
type TimeProvider interface {
|
||||||
|
Len() int
|
||||||
|
GetValue(int) time.Time
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ func RandomValues(count int) []float64 {
|
||||||
return Seq{NewRandom().WithLen(count)}.Array()
|
return Seq{NewRandom().WithLen(count)}.Array()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomValuesWithAverage returns an array of random values with a given average.
|
// RandomValuesWithMax returns an array of random values with a given average.
|
||||||
func RandomValuesWithMax(count int, max float64) []float64 {
|
func RandomValuesWithMax(count int, max float64) []float64 {
|
||||||
return Seq{NewRandom().WithMax(max).WithLen(count)}.Array()
|
return Seq{NewRandom().WithMax(max).WithLen(count)}.Array()
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,6 @@ func Values(values ...float64) Seq {
|
||||||
return Seq{Provider: Array(values)}
|
return Seq{Provider: Array(values)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider is a provider for values for a seq.
|
|
||||||
type Provider interface {
|
|
||||||
Len() int
|
|
||||||
GetValue(int) float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seq is a utility wrapper for seq providers.
|
// Seq is a utility wrapper for seq providers.
|
||||||
type Seq struct {
|
type Seq struct {
|
||||||
Provider
|
Provider
|
||||||
|
@ -28,12 +22,13 @@ type Seq struct {
|
||||||
|
|
||||||
// Array enumerates the seq into a slice.
|
// Array enumerates the seq into a slice.
|
||||||
func (s Seq) Array() (output []float64) {
|
func (s Seq) Array() (output []float64) {
|
||||||
if s.Len() == 0 {
|
slen := s.Len()
|
||||||
|
if slen == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output = make([]float64, s.Len())
|
output = make([]float64, slen)
|
||||||
for i := 0; i < s.Len(); i++ {
|
for i := 0; i < slen; i++ {
|
||||||
output[i] = s.GetValue(i)
|
output[i] = s.GetValue(i)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -149,7 +144,43 @@ func (s Seq) Sort() Seq {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
values := s.Array()
|
values := s.Array()
|
||||||
sort.Float64s(values)
|
sort.Slice(values, func(i, j int) bool {
|
||||||
|
return values[i] < values[j]
|
||||||
|
})
|
||||||
|
return Seq{Provider: Array(values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortDescending returns the seq sorted in descending order.
|
||||||
|
// This fully enumerates the seq.
|
||||||
|
func (s Seq) SortDescending() Seq {
|
||||||
|
if s.Len() == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
values := s.Array()
|
||||||
|
sort.Slice(values, func(i, j int) bool {
|
||||||
|
return values[i] > values[j]
|
||||||
|
})
|
||||||
|
return Seq{Provider: Array(values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse reverses the sequence's order.
|
||||||
|
func (s Seq) Reverse() Seq {
|
||||||
|
slen := s.Len()
|
||||||
|
if slen == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
slen2 := slen >> 1
|
||||||
|
values := s.Array()
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
j := slen - 1
|
||||||
|
for i < slen2 {
|
||||||
|
values[i], values[j] = values[j], values[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
|
||||||
return Seq{Provider: Array(values)}
|
return Seq{Provider: Array(values)}
|
||||||
}
|
}
|
||||||
|
|
64
seq/time.go
64
seq/time.go
|
@ -6,21 +6,12 @@ import (
|
||||||
"github.com/wcharczuk/go-chart/util"
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Time is a utility singleton with helper functions for time seq generation.
|
// TimeUtil is a utility singleton with helper functions for time seq generation.
|
||||||
var Time timeSequence
|
var TimeUtil timeUtil
|
||||||
|
|
||||||
type timeSequence struct{}
|
type timeUtil struct{}
|
||||||
|
|
||||||
// Days generates a seq of timestamps by day, from -days to today.
|
func (tu timeUtil) MarketHours(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
||||||
func (ts timeSequence) Days(days int) []time.Time {
|
|
||||||
var values []time.Time
|
|
||||||
for day := days; day >= 0; day-- {
|
|
||||||
values = append(values, time.Now().AddDate(0, 0, -day))
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts timeSequence) MarketHours(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
|
||||||
var times []time.Time
|
var times []time.Time
|
||||||
cursor := util.Date.On(marketOpen, from)
|
cursor := util.Date.On(marketOpen, from)
|
||||||
toClose := util.Date.On(marketClose, to)
|
toClose := util.Date.On(marketClose, to)
|
||||||
|
@ -41,7 +32,7 @@ func (ts timeSequence) MarketHours(from, to time.Time, marketOpen, marketClose t
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts timeSequence) MarketHourQuarters(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
func (tu timeUtil) MarketHourQuarters(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
||||||
var times []time.Time
|
var times []time.Time
|
||||||
cursor := util.Date.On(marketOpen, from)
|
cursor := util.Date.On(marketOpen, from)
|
||||||
toClose := util.Date.On(marketClose, to)
|
toClose := util.Date.On(marketClose, to)
|
||||||
|
@ -62,7 +53,7 @@ func (ts timeSequence) MarketHourQuarters(from, to time.Time, marketOpen, market
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts timeSequence) MarketDayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
func (tu timeUtil) MarketDayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
||||||
var times []time.Time
|
var times []time.Time
|
||||||
cursor := util.Date.On(marketOpen, from)
|
cursor := util.Date.On(marketOpen, from)
|
||||||
toClose := util.Date.On(marketClose, to)
|
toClose := util.Date.On(marketClose, to)
|
||||||
|
@ -78,7 +69,7 @@ func (ts timeSequence) MarketDayCloses(from, to time.Time, marketOpen, marketClo
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts timeSequence) MarketDayAlternateCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
func (tu timeUtil) MarketDayAlternateCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
||||||
var times []time.Time
|
var times []time.Time
|
||||||
cursor := util.Date.On(marketOpen, from)
|
cursor := util.Date.On(marketOpen, from)
|
||||||
toClose := util.Date.On(marketClose, to)
|
toClose := util.Date.On(marketClose, to)
|
||||||
|
@ -94,7 +85,7 @@ func (ts timeSequence) MarketDayAlternateCloses(from, to time.Time, marketOpen,
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts timeSequence) MarketDayMondayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
func (tu timeUtil) MarketDayMondayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday util.HolidayProvider) []time.Time {
|
||||||
var times []time.Time
|
var times []time.Time
|
||||||
cursor := util.Date.On(marketClose, from)
|
cursor := util.Date.On(marketClose, from)
|
||||||
toClose := util.Date.On(marketClose, to)
|
toClose := util.Date.On(marketClose, to)
|
||||||
|
@ -109,7 +100,7 @@ func (ts timeSequence) MarketDayMondayCloses(from, to time.Time, marketOpen, mar
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts timeSequence) Hours(start time.Time, totalHours int) []time.Time {
|
func (tu timeUtil) Hours(start time.Time, totalHours int) []time.Time {
|
||||||
times := make([]time.Time, totalHours)
|
times := make([]time.Time, totalHours)
|
||||||
|
|
||||||
last := start
|
last := start
|
||||||
|
@ -122,13 +113,12 @@ func (ts timeSequence) Hours(start time.Time, totalHours int) []time.Time {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HoursFilled adds zero values for the data bounded by the start and end of the xdata array.
|
// HoursFilled adds zero values for the data bounded by the start and end of the xdata array.
|
||||||
func (ts timeSequence) HoursFilled(xdata []time.Time, ydata []float64) ([]time.Time, []float64) {
|
func (tu timeUtil) HoursFilled(xdata []time.Time, ydata []float64) ([]time.Time, []float64) {
|
||||||
start := Time.Start(xdata)
|
start, end := Times(xdata...).MinAndMax()
|
||||||
end := Time.End(xdata)
|
|
||||||
|
|
||||||
totalHours := util.Math.AbsInt(util.Date.DiffHours(start, end))
|
totalHours := util.Math.AbsInt(util.Date.DiffHours(start, end))
|
||||||
|
|
||||||
finalTimes := ts.Hours(start, totalHours+1)
|
finalTimes := tu.Hours(start, totalHours+1)
|
||||||
finalValues := make([]float64, totalHours+1)
|
finalValues := make([]float64, totalHours+1)
|
||||||
|
|
||||||
var hoursFromStart int
|
var hoursFromStart int
|
||||||
|
@ -139,33 +129,3 @@ func (ts timeSequence) HoursFilled(xdata []time.Time, ydata []float64) ([]time.T
|
||||||
|
|
||||||
return finalTimes, finalValues
|
return finalTimes, finalValues
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start returns the earliest (min) time in a list of times.
|
|
||||||
func (ts timeSequence) Start(times []time.Time) time.Time {
|
|
||||||
if len(times) == 0 {
|
|
||||||
return time.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
start := times[0]
|
|
||||||
for _, t := range times[1:] {
|
|
||||||
if t.Before(start) {
|
|
||||||
start = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return start
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start returns the earliest (min) time in a list of times.
|
|
||||||
func (ts timeSequence) End(times []time.Time) time.Time {
|
|
||||||
if len(times) == 0 {
|
|
||||||
return time.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
end := times[0]
|
|
||||||
for _, t := range times[1:] {
|
|
||||||
if t.After(end) {
|
|
||||||
end = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return end
|
|
||||||
}
|
|
||||||
|
|
261
seq/time_seq.go
Normal file
261
seq/time_seq.go
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
package seq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// TimeZero is the zero time.
|
||||||
|
TimeZero = time.Time{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Times returns a new time sequence.
|
||||||
|
func Times(values ...time.Time) TimeSeq {
|
||||||
|
return TimeSeq{TimeProvider: ArrayOfTimes(values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeSeq is a sequence of times.
|
||||||
|
type TimeSeq struct {
|
||||||
|
TimeProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array converts the sequence to times.
|
||||||
|
func (ts TimeSeq) Array() (output []time.Time) {
|
||||||
|
slen := ts.Len()
|
||||||
|
if slen == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output = make([]time.Time, slen)
|
||||||
|
for i := 0; i < slen; i++ {
|
||||||
|
output[i] = ts.GetValue(i)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each applies the `mapfn` to all values in the value provider.
|
||||||
|
func (ts TimeSeq) Each(mapfn func(int, time.Time)) {
|
||||||
|
for i := 0; i < ts.Len(); i++ {
|
||||||
|
mapfn(i, ts.GetValue(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map applies the `mapfn` to all values in the value provider,
|
||||||
|
// returning a new seq.
|
||||||
|
func (ts TimeSeq) Map(mapfn func(int, time.Time) time.Time) TimeSeq {
|
||||||
|
output := make([]time.Time, ts.Len())
|
||||||
|
for i := 0; i < ts.Len(); i++ {
|
||||||
|
mapfn(i, ts.GetValue(i))
|
||||||
|
}
|
||||||
|
return TimeSeq{ArrayOfTimes(output)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldLeft collapses a seq from left to right.
|
||||||
|
func (ts TimeSeq) FoldLeft(mapfn func(i int, v0, v time.Time) time.Time) (v0 time.Time) {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return TimeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
if tslen == 1 {
|
||||||
|
return ts.GetValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
v0 = ts.GetValue(0)
|
||||||
|
for i := 1; i < tslen; i++ {
|
||||||
|
v0 = mapfn(i, v0, ts.GetValue(i))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FoldRight collapses a seq from right to left.
|
||||||
|
func (ts TimeSeq) FoldRight(mapfn func(i int, v0, v time.Time) time.Time) (v0 time.Time) {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return TimeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
if tslen == 1 {
|
||||||
|
return ts.GetValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
v0 = ts.GetValue(tslen - 1)
|
||||||
|
for i := tslen - 2; i >= 0; i-- {
|
||||||
|
v0 = mapfn(i, v0, ts.GetValue(i))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort returns the seq in ascending order.
|
||||||
|
func (ts TimeSeq) Sort() TimeSeq {
|
||||||
|
if ts.Len() == 0 {
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
values := ts.Array()
|
||||||
|
sort.Slice(values, func(i, j int) bool {
|
||||||
|
return values[i].Before(values[j])
|
||||||
|
})
|
||||||
|
return TimeSeq{TimeProvider: ArrayOfTimes(values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortDescending returns the seq in descending order.
|
||||||
|
func (ts TimeSeq) SortDescending() TimeSeq {
|
||||||
|
if ts.Len() == 0 {
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
values := ts.Array()
|
||||||
|
sort.Slice(values, func(i, j int) bool {
|
||||||
|
return values[i].After(values[j])
|
||||||
|
})
|
||||||
|
return TimeSeq{TimeProvider: ArrayOfTimes(values)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min returns the minimum (or earliest) time in the sequence.
|
||||||
|
func (ts TimeSeq) Min() (min time.Time) {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
min = ts.GetValue(0)
|
||||||
|
var tv time.Time
|
||||||
|
for i := 1; i < tslen; i++ {
|
||||||
|
tv = ts.GetValue(i)
|
||||||
|
if tv.Before(min) {
|
||||||
|
min = tv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is an alias to `Min`.
|
||||||
|
func (ts TimeSeq) Start() time.Time {
|
||||||
|
return ts.Min()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the maximum (or latest) time in the sequence.
|
||||||
|
func (ts TimeSeq) Max() (max time.Time) {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
max = ts.GetValue(0)
|
||||||
|
var tv time.Time
|
||||||
|
for i := 1; i < tslen; i++ {
|
||||||
|
tv = ts.GetValue(i)
|
||||||
|
if tv.After(max) {
|
||||||
|
max = tv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// End is an alias to `Max`.
|
||||||
|
func (ts TimeSeq) End() time.Time {
|
||||||
|
return ts.Max()
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returns the first value in the sequence.
|
||||||
|
func (ts TimeSeq) First() time.Time {
|
||||||
|
if ts.Len() == 0 {
|
||||||
|
return TimeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.GetValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last returns the last value in the sequence.
|
||||||
|
func (ts TimeSeq) Last() time.Time {
|
||||||
|
if ts.Len() == 0 {
|
||||||
|
return TimeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.GetValue(ts.Len() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinAndMax returns both the earliest and latest value from a sequence in one pass.
|
||||||
|
func (ts TimeSeq) MinAndMax() (min, max time.Time) {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
min = ts.GetValue(0)
|
||||||
|
max = ts.GetValue(0)
|
||||||
|
var tv time.Time
|
||||||
|
for i := 1; i < tslen; i++ {
|
||||||
|
tv = ts.GetValue(i)
|
||||||
|
if tv.Before(min) {
|
||||||
|
min = tv
|
||||||
|
}
|
||||||
|
if tv.After(max) {
|
||||||
|
max = tv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapDistinct maps values given a map function to their distinct outputs.
|
||||||
|
func (ts TimeSeq) MapDistinct(mapFn func(time.Time) time.Time) TimeSeq {
|
||||||
|
tslen := ts.Len()
|
||||||
|
if tslen == 0 {
|
||||||
|
return TimeSeq{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var output []time.Time
|
||||||
|
hourLookup := SetOfTime{}
|
||||||
|
|
||||||
|
// add the initial value
|
||||||
|
tv := ts.GetValue(0)
|
||||||
|
tvh := mapFn(tv)
|
||||||
|
hourLookup.Add(tvh)
|
||||||
|
output = append(output, tvh)
|
||||||
|
|
||||||
|
for i := 1; i < tslen; i++ {
|
||||||
|
tv = ts.GetValue(i)
|
||||||
|
tvh = mapFn(tv)
|
||||||
|
if !hourLookup.Has(tvh) {
|
||||||
|
hourLookup.Add(tvh)
|
||||||
|
output = append(output, tvh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TimeSeq{ArrayOfTimes(output)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hours returns times in each distinct hour represented by the sequence.
|
||||||
|
func (ts TimeSeq) Hours() TimeSeq {
|
||||||
|
return ts.MapDistinct(ts.trimToHour)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Days returns times in each distinct day represented by the sequence.
|
||||||
|
func (ts TimeSeq) Days() TimeSeq {
|
||||||
|
return ts.MapDistinct(ts.trimToDay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Months returns times in each distinct months represented by the sequence.
|
||||||
|
func (ts TimeSeq) Months() TimeSeq {
|
||||||
|
return ts.MapDistinct(ts.trimToMonth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Years returns times in each distinc year represented by the sequence.
|
||||||
|
func (ts TimeSeq) Years() TimeSeq {
|
||||||
|
return ts.MapDistinct(ts.trimToYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TimeSeq) trimToHour(tv time.Time) time.Time {
|
||||||
|
return time.Date(tv.Year(), tv.Month(), tv.Day(), tv.Hour(), 0, 0, 0, tv.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TimeSeq) trimToDay(tv time.Time) time.Time {
|
||||||
|
return time.Date(tv.Year(), tv.Month(), tv.Day(), 0, 0, 0, 0, tv.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TimeSeq) trimToMonth(tv time.Time) time.Time {
|
||||||
|
return time.Date(tv.Year(), tv.Month(), 1, 0, 0, 0, 0, tv.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TimeSeq) trimToYear(tv time.Time) time.Time {
|
||||||
|
return time.Date(tv.Year(), 1, 1, 0, 0, 0, 0, tv.Location())
|
||||||
|
}
|
60
seq/time_seq_test.go
Normal file
60
seq/time_seq_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package seq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTimeSeqTimes(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
seq := Times(time.Now(), time.Now(), time.Now())
|
||||||
|
assert.Equal(3, seq.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTime(str string) time.Time {
|
||||||
|
tv, _ := time.Parse("2006-01-02 15:04:05", str)
|
||||||
|
return tv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeSeqSort(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
seq := Times(
|
||||||
|
parseTime("2016-05-14 12:00:00"),
|
||||||
|
parseTime("2017-05-14 12:00:00"),
|
||||||
|
parseTime("2015-05-14 12:00:00"),
|
||||||
|
parseTime("2017-05-13 12:00:00"),
|
||||||
|
)
|
||||||
|
|
||||||
|
sorted := seq.Sort()
|
||||||
|
assert.Equal(4, sorted.Len())
|
||||||
|
min, max := sorted.MinAndMax()
|
||||||
|
assert.Equal(parseTime("2015-05-14 12:00:00"), min)
|
||||||
|
assert.Equal(parseTime("2017-05-14 12:00:00"), max)
|
||||||
|
|
||||||
|
first, last := sorted.First(), sorted.Last()
|
||||||
|
assert.Equal(min, first)
|
||||||
|
assert.Equal(max, last)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeSeqDays(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
seq := Times(
|
||||||
|
parseTime("2017-05-10 12:00:00"),
|
||||||
|
parseTime("2017-05-10 16:00:00"),
|
||||||
|
parseTime("2017-05-11 12:00:00"),
|
||||||
|
parseTime("2015-05-12 12:00:00"),
|
||||||
|
parseTime("2015-05-12 16:00:00"),
|
||||||
|
parseTime("2017-05-13 12:00:00"),
|
||||||
|
parseTime("2017-05-14 12:00:00"),
|
||||||
|
)
|
||||||
|
|
||||||
|
days := seq.Days()
|
||||||
|
assert.Equal(5, days.Len())
|
||||||
|
assert.Equal(10, days.First().Day())
|
||||||
|
assert.Equal(14, days.Last().Day())
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ func TestTimeMarketHours(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
today := time.Date(2016, 07, 01, 12, 0, 0, 0, util.Date.Eastern())
|
today := time.Date(2016, 07, 01, 12, 0, 0, 0, util.Date.Eastern())
|
||||||
mh := Time.MarketHours(today, today, util.NYSEOpen(), util.NYSEClose(), util.Date.IsNYSEHoliday)
|
mh := TimeUtil.MarketHours(today, today, util.NYSEOpen(), util.NYSEClose(), util.Date.IsNYSEHoliday)
|
||||||
assert.Len(mh, 8)
|
assert.Len(mh, 8)
|
||||||
assert.Equal(util.Date.Eastern(), mh[0].Location())
|
assert.Equal(util.Date.Eastern(), mh[0].Location())
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ func TestTimeMarketHours(t *testing.T) {
|
||||||
func TestTimeMarketHourQuarters(t *testing.T) {
|
func TestTimeMarketHourQuarters(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
today := time.Date(2016, 07, 01, 12, 0, 0, 0, util.Date.Eastern())
|
today := time.Date(2016, 07, 01, 12, 0, 0, 0, util.Date.Eastern())
|
||||||
mh := Time.MarketHourQuarters(today, today, util.NYSEOpen(), util.NYSEClose(), util.Date.IsNYSEHoliday)
|
mh := TimeUtil.MarketHourQuarters(today, today, util.NYSEOpen(), util.NYSEClose(), util.Date.IsNYSEHoliday)
|
||||||
assert.Len(mh, 4)
|
assert.Len(mh, 4)
|
||||||
assert.Equal(9, mh[0].Hour())
|
assert.Equal(9, mh[0].Hour())
|
||||||
assert.Equal(30, mh[0].Minute())
|
assert.Equal(30, mh[0].Minute())
|
||||||
|
@ -39,9 +39,9 @@ func TestTimeHours(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
today := time.Date(2016, 07, 01, 12, 0, 0, 0, time.UTC)
|
today := time.Date(2016, 07, 01, 12, 0, 0, 0, time.UTC)
|
||||||
seq := Time.Hours(today, 24)
|
seq := TimeUtil.Hours(today, 24)
|
||||||
|
|
||||||
end := Time.End(seq)
|
end := Times(seq...).Max()
|
||||||
assert.Len(seq, 24)
|
assert.Len(seq, 24)
|
||||||
assert.Equal(2016, end.Year())
|
assert.Equal(2016, end.Year())
|
||||||
assert.Equal(07, int(end.Month()))
|
assert.Equal(07, int(end.Month()))
|
||||||
|
@ -72,8 +72,8 @@ func TestSequenceHoursFill(t *testing.T) {
|
||||||
0.6,
|
0.6,
|
||||||
}
|
}
|
||||||
|
|
||||||
filledTimes, filledValues := Time.HoursFilled(xdata, ydata)
|
filledTimes, filledValues := TimeUtil.HoursFilled(xdata, ydata)
|
||||||
assert.Len(filledTimes, util.Date.DiffHours(Time.Start(xdata), Time.End(xdata))+1)
|
assert.Len(filledTimes, util.Date.DiffHours(Times(xdata...).Start(), Times(xdata...).End())+1)
|
||||||
assert.Equal(len(filledValues), len(filledTimes))
|
assert.Equal(len(filledValues), len(filledTimes))
|
||||||
|
|
||||||
assert.NotZero(filledValues[0])
|
assert.NotZero(filledValues[0])
|
||||||
|
@ -93,7 +93,7 @@ func TestTimeStart(t *testing.T) {
|
||||||
time.Now().AddDate(0, 0, -5),
|
time.Now().AddDate(0, 0, -5),
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.InTimeDelta(Time.Start(times), times[4], time.Millisecond)
|
assert.InTimeDelta(Times(times...).Start(), times[4], time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeEnd(t *testing.T) {
|
func TestTimeEnd(t *testing.T) {
|
||||||
|
@ -107,5 +107,5 @@ func TestTimeEnd(t *testing.T) {
|
||||||
time.Now().AddDate(0, 0, -5),
|
time.Now().AddDate(0, 0, -5),
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.InTimeDelta(Time.End(times), times[2], time.Millisecond)
|
assert.InTimeDelta(Times(times...).End(), times[2], time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
26
seq/util.go
26
seq/util.go
|
@ -1,6 +1,11 @@
|
||||||
package seq
|
package seq
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
func round(input float64, places int) (rounded float64) {
|
func round(input float64, places int) (rounded float64) {
|
||||||
if math.IsNaN(input) {
|
if math.IsNaN(input) {
|
||||||
|
@ -30,3 +35,22 @@ func f64i(value float64) int {
|
||||||
r := round(value, 0)
|
r := round(value, 0)
|
||||||
return int(r)
|
return int(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetOfTime is a simple hash set for timestamps as float64s.
|
||||||
|
type SetOfTime map[float64]bool
|
||||||
|
|
||||||
|
// Add adds the value to the hash set.
|
||||||
|
func (sot SetOfTime) Add(tv time.Time) {
|
||||||
|
sot[util.Time.ToFloat64(tv)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has returns if the set contains a given time.
|
||||||
|
func (sot SetOfTime) Has(tv time.Time) bool {
|
||||||
|
_, hasValue := sot[util.Time.ToFloat64(tv)]
|
||||||
|
return hasValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the value from the set.
|
||||||
|
func (sot SetOfTime) Remove(tv time.Time) {
|
||||||
|
delete(sot, util.Time.ToFloat64(tv))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue