sequence tweaks, removing market hours anything
This commit is contained in:
parent
1a09989055
commit
0e849b11bb
17 changed files with 413 additions and 877 deletions
322
util/date.go
322
util/date.go
|
|
@ -1,7 +1,6 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -45,192 +44,88 @@ var (
|
|||
Epoch = time.Unix(0, 0)
|
||||
)
|
||||
|
||||
var (
|
||||
_easternLock sync.Mutex
|
||||
_eastern *time.Location
|
||||
)
|
||||
|
||||
// NYSEOpen is when the NYSE opens.
|
||||
func NYSEOpen() time.Time { return Date.Time(9, 30, 0, 0, Date.Eastern()) }
|
||||
|
||||
// NYSEClose is when the NYSE closes.
|
||||
func NYSEClose() time.Time { return Date.Time(16, 0, 0, 0, Date.Eastern()) }
|
||||
|
||||
// NASDAQOpen is when NASDAQ opens.
|
||||
func NASDAQOpen() time.Time { return Date.Time(9, 30, 0, 0, Date.Eastern()) }
|
||||
|
||||
// NASDAQClose is when NASDAQ closes.
|
||||
func NASDAQClose() time.Time { return Date.Time(16, 0, 0, 0, Date.Eastern()) }
|
||||
|
||||
// NYSEArcaOpen is when NYSEARCA opens.
|
||||
func NYSEArcaOpen() time.Time { return Date.Time(4, 0, 0, 0, Date.Eastern()) }
|
||||
|
||||
// NYSEArcaClose is when NYSEARCA closes.
|
||||
func NYSEArcaClose() time.Time { return Date.Time(20, 0, 0, 0, Date.Eastern()) }
|
||||
|
||||
// HolidayProvider is a function that returns if a given time falls on a holiday.
|
||||
type HolidayProvider func(time.Time) bool
|
||||
|
||||
// defaultHolidayProvider implements `HolidayProvider` and just returns false.
|
||||
func defaultHolidayProvider(_ time.Time) bool { return false }
|
||||
|
||||
var (
|
||||
// Date contains utility functions that operate on dates.
|
||||
Date = &date{}
|
||||
)
|
||||
// Date contains utility functions that operate on dates.
|
||||
var Date date
|
||||
|
||||
type date struct{}
|
||||
|
||||
// IsNYSEHoliday returns if a date was/is on a nyse holiday day.
|
||||
func (d date) IsNYSEHoliday(t time.Time) bool {
|
||||
te := t.In(d.Eastern())
|
||||
if te.Year() == 2013 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 21
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 18
|
||||
} else if te.Month() == 3 {
|
||||
return te.Day() == 29
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 27
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 2
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 28
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 25
|
||||
}
|
||||
} else if te.Year() == 2014 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 20
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 17
|
||||
} else if te.Month() == 4 {
|
||||
return te.Day() == 18
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 26
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 1
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 27
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 25
|
||||
}
|
||||
} else if te.Year() == 2015 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 19
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 16
|
||||
} else if te.Month() == 4 {
|
||||
return te.Day() == 3
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 25
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 3
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 7
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 26
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 25
|
||||
}
|
||||
} else if te.Year() == 2016 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 18
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 15
|
||||
} else if te.Month() == 3 {
|
||||
return te.Day() == 25
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 30
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 5
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 24 || te.Day() == 25
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 26
|
||||
}
|
||||
} else if te.Year() == 2017 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 16
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 20
|
||||
} else if te.Month() == 4 {
|
||||
return te.Day() == 15
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 29
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 23
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 25
|
||||
}
|
||||
} else if te.Year() == 2018 {
|
||||
if te.Month() == 1 {
|
||||
return te.Day() == 1 || te.Day() == 15
|
||||
} else if te.Month() == 2 {
|
||||
return te.Day() == 19
|
||||
} else if te.Month() == 3 {
|
||||
return te.Day() == 30
|
||||
} else if te.Month() == 5 {
|
||||
return te.Day() == 28
|
||||
} else if te.Month() == 7 {
|
||||
return te.Day() == 4
|
||||
} else if te.Month() == 9 {
|
||||
return te.Day() == 3
|
||||
} else if te.Month() == 11 {
|
||||
return te.Day() == 22
|
||||
} else if te.Month() == 12 {
|
||||
return te.Day() == 25
|
||||
func (d date) MustEastern() *time.Location {
|
||||
if eastern, err := d.Eastern(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return eastern
|
||||
}
|
||||
}
|
||||
|
||||
// Eastern returns the eastern timezone.
|
||||
func (d date) Eastern() (*time.Location, error) {
|
||||
// Try POSIX
|
||||
est, err := time.LoadLocation("America/New_York")
|
||||
if err == nil {
|
||||
// Try Windows
|
||||
est, err = time.LoadLocation("EST")
|
||||
if err == nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return false
|
||||
return est, nil
|
||||
}
|
||||
|
||||
// IsNYSEArcaHoliday returns that returns if a given time falls on a holiday.
|
||||
func (d date) IsNYSEArcaHoliday(t time.Time) bool {
|
||||
return d.IsNYSEHoliday(t)
|
||||
func (d date) MustPacific() *time.Location {
|
||||
if pst, err := d.Pacific(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return pst
|
||||
}
|
||||
}
|
||||
|
||||
// IsNASDAQHoliday returns if a date was a NASDAQ holiday day.
|
||||
func (d date) IsNASDAQHoliday(t time.Time) bool {
|
||||
return d.IsNYSEHoliday(t)
|
||||
// Pacific returns the pacific timezone.
|
||||
func (d date) Pacific() (*time.Location, error) {
|
||||
// Try POSIX
|
||||
pst, err := time.LoadLocation("America/Los_Angeles")
|
||||
if err == nil {
|
||||
// Try Windows
|
||||
pst, err = time.LoadLocation("PST")
|
||||
if err == nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return pst, nil
|
||||
}
|
||||
|
||||
// TimeUTC returns a new time.Time for the given clock components in UTC.
|
||||
// It is meant to be used with the `OnDate` function.
|
||||
func (d date) TimeUTC(hour, min, sec, nsec int) time.Time {
|
||||
return time.Date(0, 0, 0, hour, min, sec, nsec, time.UTC)
|
||||
}
|
||||
|
||||
// Time returns a new time.Time for the given clock components.
|
||||
// It is meant to be used with the `OnDate` function.
|
||||
func (d date) Time(hour, min, sec, nsec int, loc *time.Location) time.Time {
|
||||
return time.Date(0, 0, 0, hour, min, sec, nsec, loc)
|
||||
}
|
||||
|
||||
// DateUTC returns a new time.Time for the given date comonents at (noon) in UTC.
|
||||
func (d date) DateUTC(year, month, day int) time.Time {
|
||||
return time.Date(year, time.Month(month), day, 12, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
// DateUTC returns a new time.Time for the given date comonents at (noon) in a given location.
|
||||
func (d date) Date(year, month, day int, loc *time.Location) time.Time {
|
||||
return time.Date(year, time.Month(month), day, 12, 0, 0, 0, loc)
|
||||
}
|
||||
|
||||
// On returns the clock components of clock (hour,minute,second) on the date components of d.
|
||||
func (d date) On(clock, cd time.Time) time.Time {
|
||||
tzAdjusted := cd.In(clock.Location())
|
||||
// OnDate returns the clock components of clock (hour,minute,second) on the date components of d.
|
||||
func (d date) OnDate(clock, date time.Time) time.Time {
|
||||
tzAdjusted := date.In(clock.Location())
|
||||
return time.Date(tzAdjusted.Year(), tzAdjusted.Month(), tzAdjusted.Day(), clock.Hour(), clock.Minute(), clock.Second(), clock.Nanosecond(), clock.Location())
|
||||
}
|
||||
|
||||
// NoonOn is a shortcut for On(Time(12,0,0), cd) a.k.a. noon on a given date.
|
||||
func (d date) NoonOn(cd time.Time) time.Time {
|
||||
// NoonOnDate is a shortcut for On(Time(12,0,0), cd) a.k.a. noon on a given date.
|
||||
func (d date) NoonOnDate(cd time.Time) time.Time {
|
||||
return time.Date(cd.Year(), cd.Month(), cd.Day(), 12, 0, 0, 0, cd.Location())
|
||||
}
|
||||
|
||||
// Optional returns a pointer reference to a given time.
|
||||
func (d date) Optional(t time.Time) *time.Time {
|
||||
return &t
|
||||
}
|
||||
|
||||
// IsWeekDay returns if the day is a monday->friday.
|
||||
func (d date) IsWeekDay(day time.Weekday) bool {
|
||||
return !d.IsWeekendDay(day)
|
||||
|
|
@ -253,116 +148,11 @@ func (d date) Before(before, reference time.Time) bool {
|
|||
return tzAdjustedBefore.Year() == reference.Year() && tzAdjustedBefore.Month() == reference.Month() && tzAdjustedBefore.Day() < reference.Day()
|
||||
}
|
||||
|
||||
// NextMarketOpen returns the next market open after a given time.
|
||||
func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvider) time.Time {
|
||||
afterLocalized := after.In(openTime.Location())
|
||||
todaysOpen := d.On(openTime, afterLocalized)
|
||||
|
||||
if isHoliday == nil {
|
||||
isHoliday = defaultHolidayProvider
|
||||
}
|
||||
|
||||
todayIsValidTradingDay := d.IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen)
|
||||
|
||||
if (afterLocalized.Equal(todaysOpen) || afterLocalized.Before(todaysOpen)) && todayIsValidTradingDay {
|
||||
return todaysOpen
|
||||
}
|
||||
|
||||
for cursorDay := 1; cursorDay < 7; cursorDay++ {
|
||||
newDay := todaysOpen.AddDate(0, 0, cursorDay)
|
||||
isValidTradingDay := d.IsWeekDay(newDay.Weekday()) && !isHoliday(newDay)
|
||||
if isValidTradingDay {
|
||||
return d.On(openTime, newDay)
|
||||
}
|
||||
}
|
||||
panic("Have exhausted day window looking for next market open.")
|
||||
}
|
||||
|
||||
// NextMarketClose returns the next market close after a given time.
|
||||
func (d date) NextMarketClose(after, closeTime time.Time, isHoliday HolidayProvider) time.Time {
|
||||
afterLocalized := after.In(closeTime.Location())
|
||||
|
||||
if isHoliday == nil {
|
||||
isHoliday = defaultHolidayProvider
|
||||
}
|
||||
|
||||
todaysClose := d.On(closeTime, afterLocalized)
|
||||
if afterLocalized.Before(todaysClose) && d.IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) {
|
||||
return todaysClose
|
||||
}
|
||||
|
||||
if afterLocalized.Equal(todaysClose) { //rare but it might happen.
|
||||
return todaysClose
|
||||
}
|
||||
|
||||
for cursorDay := 1; cursorDay < 6; cursorDay++ {
|
||||
newDay := todaysClose.AddDate(0, 0, cursorDay)
|
||||
if d.IsWeekDay(newDay.Weekday()) && !isHoliday(newDay) {
|
||||
return d.On(closeTime, newDay)
|
||||
}
|
||||
}
|
||||
panic("Have exhausted day window looking for next market close.")
|
||||
}
|
||||
|
||||
// CalculateMarketSecondsBetween calculates the number of seconds the market was open between two dates.
|
||||
func (d date) CalculateMarketSecondsBetween(start, end, marketOpen, marketClose time.Time, isHoliday HolidayProvider) (seconds int64) {
|
||||
startEastern := start.In(d.Eastern())
|
||||
endEastern := end.In(d.Eastern())
|
||||
|
||||
startMarketOpen := d.On(marketOpen, startEastern)
|
||||
startMarketClose := d.On(marketClose, startEastern)
|
||||
|
||||
if !d.IsWeekendDay(startMarketOpen.Weekday()) && !isHoliday(startMarketOpen) {
|
||||
if (startEastern.Equal(startMarketOpen) || startEastern.After(startMarketOpen)) && startEastern.Before(startMarketClose) {
|
||||
if endEastern.Before(startMarketClose) {
|
||||
seconds += int64(endEastern.Sub(startEastern) / time.Second)
|
||||
} else {
|
||||
seconds += int64(startMarketClose.Sub(startEastern) / time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor := d.NextMarketOpen(startMarketClose, marketOpen, isHoliday)
|
||||
for d.Before(cursor, endEastern) {
|
||||
if d.IsWeekDay(cursor.Weekday()) && !isHoliday(cursor) {
|
||||
close := d.NextMarketClose(cursor, marketClose, isHoliday)
|
||||
seconds += int64(close.Sub(cursor) / time.Second)
|
||||
}
|
||||
cursor = cursor.AddDate(0, 0, 1)
|
||||
}
|
||||
|
||||
finalMarketOpen := d.NextMarketOpen(cursor, marketOpen, isHoliday)
|
||||
finalMarketClose := d.NextMarketClose(cursor, marketClose, isHoliday)
|
||||
if endEastern.After(finalMarketOpen) {
|
||||
if endEastern.Before(finalMarketClose) {
|
||||
seconds += int64(endEastern.Sub(finalMarketOpen) / time.Second)
|
||||
} else {
|
||||
seconds += int64(finalMarketClose.Sub(finalMarketOpen) / time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
_secondsPerHour = 60 * 60
|
||||
_secondsPerDay = 60 * 60 * 24
|
||||
)
|
||||
|
||||
func (d date) DiffDays(t1, t2 time.Time) (days int) {
|
||||
t1n := t1.Unix()
|
||||
t2n := t2.Unix()
|
||||
diff := t2n - t1n //yields seconds
|
||||
return int(diff / (_secondsPerDay))
|
||||
}
|
||||
|
||||
func (d date) DiffHours(t1, t2 time.Time) (hours int) {
|
||||
t1n := t1.Unix()
|
||||
t2n := t2.Unix()
|
||||
diff := t2n - t1n //yields seconds
|
||||
return int(diff / (_secondsPerHour))
|
||||
}
|
||||
|
||||
// NextDay returns the timestamp advanced a day.
|
||||
func (d date) NextDay(ts time.Time) time.Time {
|
||||
return ts.AddDate(0, 0, 1)
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
// Eastern returns the eastern timezone.
|
||||
func (d date) Eastern() *time.Location {
|
||||
if _eastern == nil {
|
||||
_easternLock.Lock()
|
||||
defer _easternLock.Unlock()
|
||||
if _eastern == nil {
|
||||
_eastern, _ = time.LoadLocation("America/New_York")
|
||||
}
|
||||
}
|
||||
return _eastern
|
||||
}
|
||||
|
|
@ -33,10 +33,10 @@ func TestDateDate(t *testing.T) {
|
|||
assert.Equal(time.UTC, ts.Location())
|
||||
}
|
||||
|
||||
func TestDateOn(t *testing.T) {
|
||||
func TestDateOnDate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ts := Date.On(Date.Time(5, 4, 3, 2, time.UTC), Date.Date(2016, 6, 7, Date.Eastern()))
|
||||
ts := Date.OnDate(Date.Time(5, 4, 3, 2, time.UTC), Date.Date(2016, 6, 7, Date.MustEastern()))
|
||||
assert.Equal(2016, ts.Year())
|
||||
assert.Equal(6, ts.Month())
|
||||
assert.Equal(7, ts.Day())
|
||||
|
|
@ -47,9 +47,9 @@ func TestDateOn(t *testing.T) {
|
|||
assert.Equal(time.UTC, ts.Location())
|
||||
}
|
||||
|
||||
func TestDateNoonOn(t *testing.T) {
|
||||
func TestDateNoonOnDate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
noon := Date.NoonOn(time.Date(2016, 04, 03, 02, 01, 0, 0, time.UTC))
|
||||
noon := Date.NoonOnDate(time.Date(2016, 04, 03, 02, 01, 0, 0, time.UTC))
|
||||
|
||||
assert.Equal(2016, noon.Year())
|
||||
assert.Equal(4, noon.Month())
|
||||
|
|
@ -77,101 +77,16 @@ func TestDateBeforeHandlesTimezones(t *testing.T) {
|
|||
|
||||
tuesdayUTC := time.Date(2016, 8, 02, 22, 00, 0, 0, time.UTC)
|
||||
mondayUTC := time.Date(2016, 8, 01, 1, 00, 0, 0, time.UTC)
|
||||
sundayEST := time.Date(2016, 7, 31, 22, 00, 0, 0, Date.Eastern())
|
||||
sundayEST := time.Date(2016, 7, 31, 22, 00, 0, 0, Date.MustEastern())
|
||||
|
||||
assert.True(Date.Before(sundayEST, tuesdayUTC))
|
||||
assert.False(Date.Before(sundayEST, mondayUTC))
|
||||
}
|
||||
|
||||
func TestNextMarketOpen(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
beforeOpen := time.Date(2016, 07, 18, 9, 0, 0, 0, Date.Eastern())
|
||||
todayOpen := time.Date(2016, 07, 18, 9, 30, 0, 0, Date.Eastern())
|
||||
|
||||
afterOpen := time.Date(2016, 07, 18, 9, 31, 0, 0, Date.Eastern())
|
||||
tomorrowOpen := time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern())
|
||||
|
||||
afterFriday := time.Date(2016, 07, 22, 9, 31, 0, 0, Date.Eastern())
|
||||
mondayOpen := time.Date(2016, 07, 25, 9, 30, 0, 0, Date.Eastern())
|
||||
|
||||
weekend := time.Date(2016, 07, 23, 9, 31, 0, 0, Date.Eastern())
|
||||
|
||||
assert.True(todayOpen.Equal(Date.NextMarketOpen(beforeOpen, NYSEOpen(), Date.IsNYSEHoliday)))
|
||||
assert.True(tomorrowOpen.Equal(Date.NextMarketOpen(afterOpen, NYSEOpen(), Date.IsNYSEHoliday)))
|
||||
assert.True(mondayOpen.Equal(Date.NextMarketOpen(afterFriday, NYSEOpen(), Date.IsNYSEHoliday)))
|
||||
assert.True(mondayOpen.Equal(Date.NextMarketOpen(weekend, NYSEOpen(), Date.IsNYSEHoliday)))
|
||||
|
||||
assert.Equal(Date.Eastern(), todayOpen.Location())
|
||||
assert.Equal(Date.Eastern(), tomorrowOpen.Location())
|
||||
assert.Equal(Date.Eastern(), mondayOpen.Location())
|
||||
|
||||
testRegression := time.Date(2016, 07, 18, 16, 0, 0, 0, Date.Eastern())
|
||||
shouldbe := time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern())
|
||||
|
||||
assert.True(shouldbe.Equal(Date.NextMarketOpen(testRegression, NYSEOpen(), Date.IsNYSEHoliday)))
|
||||
}
|
||||
|
||||
func TestNextMarketClose(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
beforeClose := time.Date(2016, 07, 18, 15, 0, 0, 0, Date.Eastern())
|
||||
todayClose := time.Date(2016, 07, 18, 16, 00, 0, 0, Date.Eastern())
|
||||
|
||||
afterClose := time.Date(2016, 07, 18, 16, 1, 0, 0, Date.Eastern())
|
||||
tomorrowClose := time.Date(2016, 07, 19, 16, 00, 0, 0, Date.Eastern())
|
||||
|
||||
afterFriday := time.Date(2016, 07, 22, 16, 1, 0, 0, Date.Eastern())
|
||||
mondayClose := time.Date(2016, 07, 25, 16, 0, 0, 0, Date.Eastern())
|
||||
|
||||
weekend := time.Date(2016, 07, 23, 9, 31, 0, 0, Date.Eastern())
|
||||
|
||||
assert.True(todayClose.Equal(Date.NextMarketClose(beforeClose, NYSEClose(), Date.IsNYSEHoliday)))
|
||||
assert.True(tomorrowClose.Equal(Date.NextMarketClose(afterClose, NYSEClose(), Date.IsNYSEHoliday)))
|
||||
assert.True(mondayClose.Equal(Date.NextMarketClose(afterFriday, NYSEClose(), Date.IsNYSEHoliday)))
|
||||
assert.True(mondayClose.Equal(Date.NextMarketClose(weekend, NYSEClose(), Date.IsNYSEHoliday)))
|
||||
|
||||
assert.Equal(Date.Eastern(), todayClose.Location())
|
||||
assert.Equal(Date.Eastern(), tomorrowClose.Location())
|
||||
assert.Equal(Date.Eastern(), mondayClose.Location())
|
||||
}
|
||||
|
||||
func TestCalculateMarketSecondsBetween(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
start := time.Date(2016, 07, 18, 9, 30, 0, 0, Date.Eastern())
|
||||
end := time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern())
|
||||
|
||||
shouldbe := 5 * 6.5 * 60 * 60
|
||||
|
||||
assert.Equal(shouldbe, Date.CalculateMarketSecondsBetween(start, end, NYSEOpen(), NYSEClose(), Date.IsNYSEHoliday))
|
||||
}
|
||||
|
||||
func TestCalculateMarketSecondsBetween1D(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
start := time.Date(2016, 07, 22, 9, 45, 0, 0, Date.Eastern())
|
||||
end := time.Date(2016, 07, 22, 15, 45, 0, 0, Date.Eastern())
|
||||
|
||||
shouldbe := 6 * 60 * 60
|
||||
|
||||
assert.Equal(shouldbe, Date.CalculateMarketSecondsBetween(start, end, NYSEOpen(), NYSEClose(), Date.IsNYSEHoliday))
|
||||
}
|
||||
|
||||
func TestCalculateMarketSecondsBetweenLTM(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
start := time.Date(2015, 07, 01, 9, 30, 0, 0, Date.Eastern())
|
||||
end := time.Date(2016, 07, 01, 9, 30, 0, 0, Date.Eastern())
|
||||
|
||||
shouldbe := 253 * 6.5 * 60 * 60 //253 full market days since this date last year.
|
||||
assert.Equal(shouldbe, Date.CalculateMarketSecondsBetween(start, end, NYSEOpen(), NYSEClose(), Date.IsNYSEHoliday))
|
||||
}
|
||||
|
||||
func TestDateNextHour(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
start := time.Date(2015, 07, 01, 9, 30, 0, 0, Date.Eastern())
|
||||
start := time.Date(2015, 07, 01, 9, 30, 0, 0, Date.MustEastern())
|
||||
next := Date.NextHour(start)
|
||||
assert.Equal(2015, next.Year())
|
||||
assert.Equal(07, next.Month())
|
||||
|
|
@ -221,40 +136,3 @@ func TestDateNextDayOfWeek(t *testing.T) {
|
|||
assert.Equal(time.UTC, nextSunday.Location())
|
||||
assert.Equal(time.UTC, nextMonday.Location())
|
||||
}
|
||||
|
||||
func TestDateIsNYSEHoliday(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cursor := time.Date(2013, 01, 01, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
var holidays int
|
||||
for Date.Before(cursor, end) {
|
||||
if Date.IsNYSEHoliday(cursor) {
|
||||
holidays++
|
||||
}
|
||||
cursor = cursor.AddDate(0, 0, 1)
|
||||
}
|
||||
assert.Equal(holidays, 55)
|
||||
}
|
||||
|
||||
func TestDateDiffDays(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
t1 := time.Date(2017, 02, 27, 12, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2017, 01, 10, 3, 0, 0, 0, time.UTC)
|
||||
t3 := time.Date(2017, 02, 24, 16, 0, 0, 0, time.UTC)
|
||||
|
||||
assert.Equal(48, Date.DiffDays(t2, t1))
|
||||
assert.Equal(2, Date.DiffDays(t3, t1)) // technically we should round down.
|
||||
}
|
||||
|
||||
func TestDateDiffHours(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
t1 := time.Date(2017, 02, 27, 12, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2017, 02, 24, 16, 0, 0, 0, time.UTC)
|
||||
t3 := time.Date(2017, 02, 28, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
assert.Equal(68, Date.DiffHours(t2, t1))
|
||||
assert.Equal(24, Date.DiffHours(t1, t3))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
// Eastern returns the eastern timezone.
|
||||
func (d date) Eastern() *time.Location {
|
||||
if _eastern == nil {
|
||||
_easternLock.Lock()
|
||||
defer _easternLock.Unlock()
|
||||
if _eastern == nil {
|
||||
_eastern, _ = time.LoadLocation("EST")
|
||||
}
|
||||
}
|
||||
return _eastern
|
||||
}
|
||||
74
util/time.go
74
util/time.go
|
|
@ -18,3 +18,77 @@ func (tu timeUtil) ToFloat64(t time.Time) float64 {
|
|||
func (tu timeUtil) FromFloat64(tf float64) time.Time {
|
||||
return time.Unix(0, int64(tf))
|
||||
}
|
||||
|
||||
func (tu timeUtil) DiffDays(t1, t2 time.Time) (days int) {
|
||||
t1n := t1.Unix()
|
||||
t2n := t2.Unix()
|
||||
var diff int64
|
||||
if t1n > t2n {
|
||||
diff = t2n - t1n //yields seconds
|
||||
} else {
|
||||
diff = t1n - t2n //yields seconds
|
||||
}
|
||||
return int(diff / (_secondsPerDay))
|
||||
}
|
||||
|
||||
func (tu timeUtil) DiffHours(t1, t2 time.Time) (hours int) {
|
||||
t1n := t1.Unix()
|
||||
t2n := t2.Unix()
|
||||
var diff int64
|
||||
if t1n > t2n {
|
||||
diff = t1n - t2n
|
||||
} else {
|
||||
diff = t2n - t1n
|
||||
}
|
||||
return int(diff / (_secondsPerHour))
|
||||
}
|
||||
|
||||
// Start returns the earliest (min) time in a list of times.
|
||||
func (tu timeUtil) 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 (tu timeUtil) 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
|
||||
}
|
||||
|
||||
// StartAndEnd returns the start and end of a given set of time in one pass.
|
||||
func (tu timeUtil) StartAndEnd(values ...time.Time) (start time.Time, end time.Time) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
start = values[0]
|
||||
end = values[0]
|
||||
|
||||
for _, v := range values[1:] {
|
||||
if end.Before(v) {
|
||||
end = v
|
||||
}
|
||||
if start.After(v) {
|
||||
start = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
30
util/time_test.go
Normal file
30
util/time_test.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/blend/go-sdk/assert"
|
||||
)
|
||||
|
||||
func TestTimeDiffDays(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
t1 := time.Date(2017, 02, 27, 12, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2017, 01, 10, 3, 0, 0, 0, time.UTC)
|
||||
t3 := time.Date(2017, 02, 24, 16, 0, 0, 0, time.UTC)
|
||||
|
||||
assert.Equal(48, Time.DiffDays(t2, t1))
|
||||
assert.Equal(2, Time.DiffDays(t3, t1)) // technically we should round down.
|
||||
}
|
||||
|
||||
func TestTimeDiffHours(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
t1 := time.Date(2017, 02, 27, 12, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2017, 02, 24, 16, 0, 0, 0, time.UTC)
|
||||
t3 := time.Date(2017, 02, 28, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
assert.Equal(68, Time.DiffHours(t2, t1))
|
||||
assert.Equal(24, Time.DiffHours(t1, t3))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue