@ -0,0 +1,67 @@ | |||||
package common | |||||
import ( | |||||
"fmt" | |||||
"strconv" | |||||
"strings" | |||||
"time" | |||||
"github.com/pkg/errors" | |||||
) | |||||
// ParseDate parses a date string of the format YYYY-MM-DD | |||||
func ParseDate(date string) (t time.Time, err error) { | |||||
//get the time of invoice | |||||
str := strings.Split(date, "-") | |||||
var ymd = []int{} | |||||
for _, i := range str { | |||||
j, err := strconv.Atoi(i) | |||||
if err != nil { | |||||
return t, err | |||||
} | |||||
ymd = append(ymd, j) | |||||
} | |||||
if len(ymd) != 3 { | |||||
return t, fmt.Errorf("Bad date parsing, not 3 segments") //never stack trace | |||||
} | |||||
if ymd[1] < 1 || ymd[1] > 12 { | |||||
return t, fmt.Errorf("Month not between 1 and 12") //never stack trace | |||||
} | |||||
if ymd[2] > 31 { | |||||
return t, fmt.Errorf("Day over 31") //never stack trace | |||||
} | |||||
t = time.Date(ymd[0], time.Month(ymd[1]), ymd[2], 0, 0, 0, 0, time.UTC) | |||||
return t, nil | |||||
} | |||||
// ParseDateRange parses a date range string of the format start:end | |||||
// where the start and end date are of the format YYYY-MM-DD. | |||||
// The parsed dates are *time.Time and will return nil pointers for | |||||
// unbounded dates, ex: | |||||
// unbounded start: :2000-12-31 | |||||
// unbounded end: 2000-12-31: | |||||
func ParseDateRange(dateRange string) (startDate, endDate *time.Time, err error) { | |||||
dates := strings.Split(dateRange, ":") | |||||
if len(dates) != 2 { | |||||
return nil, nil, errors.New("bad date range, must be in format date:date") | |||||
} | |||||
parseDate := func(date string) (*time.Time, error) { | |||||
if len(date) == 0 { | |||||
return nil, nil | |||||
} | |||||
d, err := ParseDate(date) | |||||
return &d, err | |||||
} | |||||
startDate, err = parseDate(dates[0]) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
endDate, err = parseDate(dates[1]) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
return | |||||
} |
@ -0,0 +1,76 @@ | |||||
package common | |||||
import ( | |||||
"testing" | |||||
"time" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
var ( | |||||
date = time.Date(2015, time.Month(12), 31, 0, 0, 0, 0, time.UTC) | |||||
date2 = time.Date(2016, time.Month(12), 31, 0, 0, 0, 0, time.UTC) | |||||
) | |||||
func TestParseDate(t *testing.T) { | |||||
assert := assert.New(t) | |||||
var testDates = []struct { | |||||
dateStr string | |||||
date time.Time | |||||
errNil bool | |||||
}{ | |||||
{"2015-12-31", date, true}, | |||||
{"2015-31-12", date, false}, | |||||
{"12-31-2015", date, false}, | |||||
{"31-12-2015", date, false}, | |||||
} | |||||
for _, test := range testDates { | |||||
parsed, err := ParseDate(test.dateStr) | |||||
switch test.errNil { | |||||
case true: | |||||
assert.Nil(err) | |||||
assert.True(parsed.Equal(test.date), "parsed: %v, want %v", parsed, test.date) | |||||
case false: | |||||
assert.NotNil(err, "parsed %v, expected err %v", parsed, err) | |||||
} | |||||
} | |||||
} | |||||
func TestParseDateRange(t *testing.T) { | |||||
assert := assert.New(t) | |||||
var testDates = []struct { | |||||
dateStr string | |||||
start *time.Time | |||||
end *time.Time | |||||
errNil bool | |||||
}{ | |||||
{"2015-12-31:2016-12-31", &date, &date2, true}, | |||||
{"2015-12-31:", &date, nil, true}, | |||||
{":2016-12-31", nil, &date2, true}, | |||||
{"2016-12-31", nil, nil, false}, | |||||
{"2016-31-12:", nil, nil, false}, | |||||
{":2016-31-12", nil, nil, false}, | |||||
} | |||||
for _, test := range testDates { | |||||
start, end, err := ParseDateRange(test.dateStr) | |||||
switch test.errNil { | |||||
case true: | |||||
assert.Nil(err) | |||||
testPtr := func(want, have *time.Time) { | |||||
if want == nil { | |||||
assert.Nil(have) | |||||
} else { | |||||
assert.True((*have).Equal(*want)) | |||||
} | |||||
} | |||||
testPtr(test.start, start) | |||||
testPtr(test.end, end) | |||||
case false: | |||||
assert.NotNil(err) | |||||
} | |||||
} | |||||
} |