From 304be4ec2fe153fac1a4a240c40f368c4763d760 Mon Sep 17 00:00:00 2001 From: rigel rozanski Date: Tue, 6 Jun 2017 04:00:36 -0400 Subject: [PATCH 1/3] date parse functionality --- common/date.go | 67 +++++++++++++++++++++++++++++++++++++++ common/date_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 common/date.go create mode 100644 common/date_test.go diff --git a/common/date.go b/common/date.go new file mode 100644 index 00000000..05f207f7 --- /dev/null +++ b/common/date.go @@ -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 +} diff --git a/common/date_test.go b/common/date_test.go new file mode 100644 index 00000000..42fd91aa --- /dev/null +++ b/common/date_test.go @@ -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) + } + } +} From f913ed8134448f65f572014b90ca78b24c12c03a Mon Sep 17 00:00:00 2001 From: rigel rozanski Date: Tue, 6 Jun 2017 16:08:57 -0400 Subject: [PATCH 2/3] date simplify --- common/date.go | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/common/date.go b/common/date.go index 05f207f7..7b7b05b2 100644 --- a/common/date.go +++ b/common/date.go @@ -1,8 +1,6 @@ package common import ( - "fmt" - "strconv" "strings" "time" @@ -11,30 +9,8 @@ import ( // 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 + layout := "2006-01-02" //this represents YYYY-MM-DD + return time.Parse(layout, date) } // ParseDateRange parses a date range string of the format start:end From cc364b14e20965a82a59ba2061675eb98dd818c9 Mon Sep 17 00:00:00 2001 From: rigel rozanski Date: Tue, 20 Jun 2017 17:18:55 -0400 Subject: [PATCH 3/3] changelog and PR changes --- CHANGELOG.md | 7 ++++++ common/date.go | 28 +++++++++++------------ common/date_test.go | 56 +++++++++++---------------------------------- 3 files changed, 34 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a97aa128..4261c466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog + +## Develop-Branch changes (unreleased) + +FEATURES: + +- [common] Date range parsing from string (ex. "2015-12-31:2017-12-31") + ## 0.2.1 (June 2, 2017) FEATURES: diff --git a/common/date.go b/common/date.go index 7b7b05b2..e017a4b4 100644 --- a/common/date.go +++ b/common/date.go @@ -7,37 +7,37 @@ import ( "github.com/pkg/errors" ) -// ParseDate parses a date string of the format YYYY-MM-DD -func ParseDate(date string) (t time.Time, err error) { - layout := "2006-01-02" //this represents YYYY-MM-DD - return time.Parse(layout, date) -} +// TimeLayout helps to parse a date string of the format YYYY-MM-DD +// Intended to be used with the following function: +// time.Parse(TimeLayout, date) +var TimeLayout = "2006-01-02" //this represents YYYY-MM-DD // 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 +// The parsed dates are time.Time and will return the zero time for // unbounded dates, ex: // unbounded start: :2000-12-31 // unbounded end: 2000-12-31: -func ParseDateRange(dateRange string) (startDate, endDate *time.Time, err error) { +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") + err = errors.New("bad date range, must be in format date:date") + return } - parseDate := func(date string) (*time.Time, error) { + parseDate := func(date string) (out time.Time, err error) { if len(date) == 0 { - return nil, nil + return } - d, err := ParseDate(date) - return &d, err + out, err = time.Parse(TimeLayout, date) + return } startDate, err = parseDate(dates[0]) if err != nil { - return nil, nil, err + return } endDate, err = parseDate(dates[1]) if err != nil { - return nil, nil, err + return } return } diff --git a/common/date_test.go b/common/date_test.go index 42fd91aa..2c063247 100644 --- a/common/date_test.go +++ b/common/date_test.go @@ -10,66 +10,36 @@ import ( 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) + zero time.Time ) -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 + 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}, + {"2015-12-31:2016-12-31", date, date2, true}, + {"2015-12-31:", date, zero, true}, + {":2016-12-31", zero, date2, true}, + {"2016-12-31", zero, zero, false}, + {"2016-31-12:", zero, zero, false}, + {":2016-31-12", zero, zero, false}, } for _, test := range testDates { start, end, err := ParseDateRange(test.dateStr) - switch test.errNil { - case true: + if test.errNil { assert.Nil(err) - testPtr := func(want, have *time.Time) { - if want == nil { - assert.Nil(have) - } else { - assert.True((*have).Equal(*want)) - } + testPtr := func(want, have time.Time) { + assert.True(have.Equal(want)) } testPtr(test.start, start) testPtr(test.end, end) - case false: + } else { assert.NotNil(err) } }