From 7cd93e044ae8ec4ba621e29f0400af2e4079dfdf Mon Sep 17 00:00:00 2001 From: Jan Galek Date: Tue, 4 Feb 2025 22:38:39 +0100 Subject: [PATCH 1/4] [Refactor] some refactoring --- date/date.go | 21 +++- date/range.go | 60 +++++++++-- dateTimeInterface.go | 2 + datetime.go | 33 +++--- range.go | 129 ++++++++++++++++++++---- rangeInterface.go | 38 +++++++ tests/date_test.go | 209 ++++++++++++++++++++++++++++++++++++++ tests/datetime_test.go | 221 ++++++++++++++++++++++++++++++++++++++++- tests/time_test.go | 207 ++++++++++++++++++++++++++++++++++++++ time/range.go | 20 +++- time/time.go | 18 +++- valueInterface.go | 5 + 12 files changed, 906 insertions(+), 57 deletions(-) create mode 100644 rangeInterface.go create mode 100644 valueInterface.go diff --git a/date/date.go b/date/date.go index 1546528..8ee1122 100644 --- a/date/date.go +++ b/date/date.go @@ -24,6 +24,17 @@ type Date struct { DateTime time.Time } +func Now() *Date { + now := time.Now() + + return &Date{ + Year: now.Year(), + Month: int(now.Month()), + Day: now.Day(), + DateTime: now, + } +} + func New(year, month, day int) (datetime.Interface, error) { errs := validator.Validate(year, constraints.GreaterOrEqual{Value: 0}) @@ -95,5 +106,13 @@ func (d *Date) Equal(u datetime.Interface) bool { } func (d *Date) Between(start, end datetime.Interface) bool { - return d.Time().Before(end.Time()) && d.Time().After(start.Time()) + return d.Before(end) && d.After(start) +} + +func (d *Date) Before(u datetime.Interface) bool { + return d.Time().Before(u.Time()) +} + +func (d *Date) After(u datetime.Interface) bool { + return d.Time().After(u.Time()) } diff --git a/date/range.go b/date/range.go index 00e96e3..f15c2a7 100644 --- a/date/range.go +++ b/date/range.go @@ -11,7 +11,8 @@ import ( ) const ( - RangeRegexp = `^([\[\(])(\d{4}-\d{2}-\d{2})?,(\d{4}-\d{2}-\d{2})?([\]\)])$` + RangeDateRegexp = `(` + datetime.YearRegexp + `-` + datetime.MonthRegexp + `-` + datetime.DayRegexp + `)` + RangeRegexp = `^([\[\(])` + RangeDateRegexp + `?\s*,\s*` + RangeDateRegexp + `?([\]\)])$` ) type Range struct { @@ -21,13 +22,46 @@ type Range struct { end datetime.RangeEnd } -func NewDateRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) *Range { +func NewRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) (*Range, error) { + + _, err := FromString(from) + + if from != "" && err != nil { + return nil, err + } + + _, err = FromString(to) + + if to != "" && err != nil { + return nil, err + } + + if to == "" && from == "" { + return nil, errors.New("from and to can not be both empty") + } + return &Range{ from: Value(from), to: Value(to), start: start, end: end, - } + }, nil +} + +func NewRangeOptional(from, to string) (*Range, error) { + return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndOptional) +} + +func NewRangeStrict(from, to string) (*Range, error) { + return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndStrict) +} + +func NewRangeStartStrict(from, to string) (*Range, error) { + return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndOptional) +} + +func NewRangeStartOptional(from, to string) (*Range, error) { + return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndStrict) } func RangeFromString(dateRange string) (*Range, error) { @@ -39,9 +73,9 @@ func RangeFromString(dateRange string) (*Range, error) { re := regexp.MustCompile(RangeRegexp) match := re.FindStringSubmatch(dateRange) - openBracket, date1, date2, closeBracket := match[1], match[2], match[3], match[4] + openBracket, date1, date2, closeBracket := match[1], match[2], match[6], match[10] - return NewDateRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)), nil + return NewRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)) } func (d *Range) Start() datetime.RangeStart { @@ -52,8 +86,16 @@ func (d *Range) End() datetime.RangeEnd { return d.end } +func (d *Range) From() Value { + return d.from +} + +func (d *Range) To() Value { + return d.to +} + func (d *Range) String() string { - return fmt.Sprintf("%s%s,%s%s", d.start, d.from, d.to, d.end) + return fmt.Sprintf("%s%s, %s%s", d.Start(), d.From(), d.To(), d.End()) } func (d *Range) Is(value any) bool { @@ -63,9 +105,9 @@ func (d *Range) Is(value any) bool { return false } - start, _ := FromString(string(d.start)) - end, _ := FromString(string(d.end)) - return date.Between(start, end) + from, _ := FromString(string(d.From())) + to, _ := FromString(string(d.To())) + return date.Between(from, to) } func (d *Range) format(date any) (datetime.Interface, error) { diff --git a/dateTimeInterface.go b/dateTimeInterface.go index 23361bd..e6f5cff 100644 --- a/dateTimeInterface.go +++ b/dateTimeInterface.go @@ -8,5 +8,7 @@ type Interface interface { Time() time.Time Equal(u Interface) bool Between(start, end Interface) bool + Before(u Interface) bool + After(u Interface) bool Compare(u Interface) int } diff --git a/datetime.go b/datetime.go index 0c34c57..d7c44c3 100644 --- a/datetime.go +++ b/datetime.go @@ -12,7 +12,7 @@ import ( ) const ( - Regexp = `^(\d{4})-(\d{2})-(\d{2})( (\d{2}):(\d{2}):(\d{2}))?$` + Regexp = `^((\d+)-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$` ) type DateTime struct { @@ -96,19 +96,14 @@ func FromString(value string) (Interface, error) { re := regexp.MustCompile(Regexp) match := re.FindStringSubmatch(value) - year, _ := strconv.Atoi(match[1]) - month, _ := strconv.Atoi(match[2]) - day, _ := strconv.Atoi(match[3]) - - if match[4] != "" { - hour, _ := strconv.Atoi(match[5]) - minute, _ := strconv.Atoi(match[6]) - second, _ := strconv.Atoi(match[7]) - - return New(year, month, day, hour, minute, second) - } - - return nil, errors.New(fmt.Sprintf("unsupported format of datetime \"%s\". time is missing.", value)) + year, _ := strconv.Atoi(match[2]) + month, _ := strconv.Atoi(match[3]) + day, _ := strconv.Atoi(match[4]) + hour, _ := strconv.Atoi(match[5]) + minute, _ := strconv.Atoi(match[6]) + second, _ := strconv.Atoi(match[7]) + + return New(year, month, day, hour, minute, second) } func (d *DateTime) FromString(value string) (Interface, error) { @@ -143,7 +138,15 @@ func (d *DateTime) Equal(u Interface) bool { } func (d *DateTime) Between(start, end Interface) bool { - return d.Time().Before(end.Time()) && d.Time().After(start.Time()) + return d.Before(end) && d.After(start) +} + +func (d *DateTime) Before(u Interface) bool { + return d.Time().Before(u.Time()) +} + +func (d *DateTime) After(u Interface) bool { + return d.Time().After(u.Time()) } func DaysInMonthList(year int, month int) []int { diff --git a/range.go b/range.go index 4722bf4..a0f5372 100644 --- a/range.go +++ b/range.go @@ -1,43 +1,130 @@ package datetime -type RangeStart string -type RangeEnd string - -var ( - // RangeStartStrict can not be equal - RangeStartStrict RangeStart = "[" - // RangeStartOptional can be equal - RangeStartOptional RangeStart = "(" - // RangeEndStrict can not be equal - RangeEndStrict RangeEnd = "]" - // RangeEndOptional can be equal - RangeEndOptional RangeEnd = ")" +import ( + "errors" + "fmt" + "github.com/gouef/validator" + "github.com/gouef/validator/constraints" + "regexp" + "time" ) type Range struct { + from Value + to Value start RangeStart end RangeEnd } -func NewRange(start RangeStart, end RangeEnd) *Range { +func NewRange(from, to string, start RangeStart, end RangeEnd) (*Range, error) { + + _, err := FromString(from) + + if from != "" && err != nil { + return nil, err + } + + _, err = FromString(to) + + if to != "" && err != nil { + return nil, err + } + return &Range{ + from: Value(from), + to: Value(to), start: start, end: end, + }, nil +} + +func NewRangeOptional(from, to string) (*Range, error) { + return NewRange(from, to, RangeStartOptional, RangeEndOptional) +} + +func NewRangeStrict(from, to string) (*Range, error) { + return NewRange(from, to, RangeStartStrict, RangeEndStrict) +} + +func NewRangeStartStrict(from, to string) (*Range, error) { + return NewRange(from, to, RangeStartStrict, RangeEndOptional) +} + +func NewRangeStartOptional(from, to string) (*Range, error) { + return NewRange(from, to, RangeStartOptional, RangeEndStrict) +} + +func RangeFromString(value string) (*Range, error) { + errs := validator.Validate(value, constraints.RegularExpression{Regexp: RangeRegexp}) + + if len(errs) != 0 { + return nil, errors.New(fmt.Sprintf("unsupported format of datetime range \"%s\"", value)) } + + re := regexp.MustCompile(RangeRegexp) + match := re.FindStringSubmatch(value) + start := match[1] + from := match[2] + to := match[9] + end := match[16] + + return NewRange(from, to, RangeStart(start), RangeEnd(end)) +} + +func (r *Range) Start() RangeStart { + return r.start +} + +func (r *Range) End() RangeEnd { + return r.end } -func NewRangeOptional() *Range { - return NewRange(RangeStartOptional, RangeEndOptional) +func (r *Range) From() ValueInterface { + return r.from } -func NewRangeStrict() *Range { - return NewRange(RangeStartStrict, RangeEndStrict) +func (r *Range) To() ValueInterface { + return r.to } -func NewRangeStartStrict() *Range { - return NewRange(RangeStartStrict, RangeEndOptional) +func (r *Range) String() string { + return fmt.Sprintf("%s%s, %s%s", r.Start(), r.From(), r.To(), r.End()) +} + +func (r *Range) Is(value any) bool { + date, err := r.format(value) + + if err != nil { + return false + } + + from, _ := FromString(string(r.from)) + to, _ := FromString(string(r.to)) + + if from == nil && to == nil { + return false + } + + if from == nil { + return date.Before(to) + } + + if to == nil { + return date.After(from) + } + + return date.Between(from, to) } -func NewRangeStartOptional() *Range { - return NewRange(RangeStartOptional, RangeEndStrict) +func (r *Range) format(date any) (Interface, error) { + switch i := date.(type) { + case time.Time: + return New(i.Year(), int(i.Month()), i.Day(), i.Hour(), i.Minute(), i.Second()) + case *DateTime: + return i, nil + case string: + return FromString(i) + default: + return nil, errors.New("unsupported format of datetime") + } } diff --git a/rangeInterface.go b/rangeInterface.go new file mode 100644 index 0000000..0313080 --- /dev/null +++ b/rangeInterface.go @@ -0,0 +1,38 @@ +package datetime + +// RangeStart start bracket +type RangeStart string + +// RangeEnd end bracket +type RangeEnd string + +var ( + // RangeStartStrict can not be equal + RangeStartStrict RangeStart = "[" + // RangeStartOptional can be equal + RangeStartOptional RangeStart = "(" + // RangeEndStrict can not be equal + RangeEndStrict RangeEnd = "]" + // RangeEndOptional can be equal + RangeEndOptional RangeEnd = ")" +) + +const ( + YearRegexp = `(\d+)` + MonthRegexp = `(0[1-9]|1[0-2])` + DayRegexp = `(0[1-9]|[12][0-9]|3[01])` + HourRegexp = `(0[0-9]|1[0-9]|2[0-3])` + MinuteRegexp = `[0-5][0-9]` + SecondRegexp = `[0-5][0-9]` + + RangeRegexp = `^([\[\(])((` + YearRegexp + `-` + MonthRegexp + `-` + DayRegexp + `) (` + HourRegexp + `:` + MinuteRegexp + `:` + SecondRegexp + `))?\s*,\s*((` + YearRegexp + `-` + MonthRegexp + `-` + DayRegexp + `) (` + HourRegexp + `:` + MinuteRegexp + `:` + SecondRegexp + `))?([\]\)])$` +) + +type RangeInterface interface { + Start() RangeStart + End() RangeEnd + From() ValueInterface + To() ValueInterface + String() string + Is(value any) bool +} diff --git a/tests/date_test.go b/tests/date_test.go index afab41f..30134c6 100644 --- a/tests/date_test.go +++ b/tests/date_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "github.com/gouef/datetime" "github.com/gouef/datetime/date" "github.com/stretchr/testify/assert" @@ -200,3 +201,211 @@ func TestDateToString(t *testing.T) { }) } } + +func TestDateRange(t *testing.T) { + t.Run("New Date Range", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expectedValidErr bool + expected func() any + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", false, false, func() any { + val, err := date.NewRangeStrict( + "2025-01-31", "2026-01-31") + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, false, func() any { + val, err := date.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, false, func() any { + val, err := date.NewRange( + "", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", true, false, func() any { + val, err := date.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), true, false, func() any { + val, err := date.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false, nil}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, true, true, func() any { + val, err := date.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, ]", date.Now(), true, true, func() any { + val, err := date.NewRange( + "", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", date.Now(), true, true, func() any { + val, err := date.NewRange( + "2025-01-31 14:15:16", "2026-02-31 14:15:16", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := date.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := date.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + if tt.expectedValidErr { + assert.False(t, r.Is(tt.testDate)) + } else { + assert.True(t, r.Is(tt.testDate)) + assert.Equal(t, tt.rangeStr, r.String()) + } + } + }) + } + + }) + + t.Run("Range Is", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expected bool + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", false, true}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, true}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, true}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", true, true}, + {"[2025-01-31 14:15:16, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), true, true}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, true, false}, + {"[, ]", date.Now(), true, false}, + {"[2, ]", date.Now(), true, false}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := date.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := date.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected, r.Is(tt.testDate)) + } + }) + } + }) + + t.Run("Range brackets", func(t *testing.T) { + tests := []struct { + expectedErr bool + start string + from string + to string + end string + expected func() any + }{ + {false, "[", "2025-01-31", "2026-01-31", "]", func() any { + val, err := date.NewRangeStrict("2025-01-31", "2026-01-31") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := date.NewRangeStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", "]", func() any { + val, err := date.NewRangeStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", "]", func() any { + val, err := date.NewRangeStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := date.NewRangeStartOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", "]", func() any { + val, err := date.NewRangeStartOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", "]", func() any { + val, err := date.NewRangeStartOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := date.NewRangeOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", ")", func() any { + val, err := date.NewRangeOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", ")", func() any { + val, err := date.NewRangeOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := date.NewRangeStartStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", ")", func() any { + val, err := date.NewRangeStartStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", ")", func() any { + val, err := date.NewRangeStartStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range brackets: "+fmt.Sprintf("From: %s%s, To: %s%s", tt.start, tt.from, tt.to, tt.end), func(t *testing.T) { + r, err := date.NewRange(tt.from, tt.to, datetime.RangeStart(tt.start), datetime.RangeEnd(tt.end)) + + if tt.expectedErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + } + }) + } + }) +} diff --git a/tests/datetime_test.go b/tests/datetime_test.go index 35069df..d7cbb5d 100644 --- a/tests/datetime_test.go +++ b/tests/datetime_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "github.com/gouef/datetime" "github.com/gouef/datetime/date" "github.com/stretchr/testify/assert" @@ -20,10 +21,13 @@ func TestNewDateTime(t *testing.T) { err bool }{ {2024, 12, 25, 0, 0, 0, &datetime.DateTime{Year: 2024, Month: 12, Day: 25, DateTime: time.Date(2024, 12, 25, 0, 0, 0, 0, time.UTC)}, false}, - {2024, 2, 30, 0, 0, 0, nil, true}, // Invalid day for February - {2024, 13, 10, 0, 0, 0, nil, true}, // Invalid month (13) - {2024, 4, 31, 0, 0, 0, nil, true}, // April has only 30 days - {-2024, 12, 25, 0, 0, 0, nil, true}, // April has only 30 days + {2024, 2, 30, 0, 0, 0, nil, true}, + {2024, 13, 10, 0, 0, 0, nil, true}, + {2024, 4, 31, 0, 0, 0, nil, true}, + {-2024, 12, 25, 0, 0, 0, nil, true}, + {2024, 1, 31, 24, 0, 0, nil, true}, + {2024, 1, 31, 23, 60, 0, nil, true}, + {2024, 1, 31, 23, 59, 60, nil, true}, } for _, tt := range tests { @@ -207,6 +211,9 @@ func TestDateTimeFromString(t *testing.T) { }{ {"2025-01-31", true, nil}, {"2025-01-31 23:27:15", false, validDate}, + {"2025-01-31 24:27:15", true, nil}, + {"2025-01-31 23:60:15", true, nil}, + {"2025-01-31 23:27:65", true, nil}, {"2025-02-31", true, nil}, {"2025-13-32", true, nil}, {"-2025-06-31", true, nil}, @@ -230,3 +237,209 @@ func TestDateTimeFromString(t *testing.T) { }) } } + +func TestRange(t *testing.T) { + t.Run("New Range", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expectedValidErr bool + expected func() any + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, func() any { + val, err := datetime.NewRangeStrict( + "2025-01-31", "2026-01-31") + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, nil}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false, nil}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, true, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, ]", datetime.Now(), false, true, func() any { + val, err := datetime.NewRange( + "", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", datetime.Now(), true, true, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-02-31 14:15:16", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + if tt.expectedValidErr { + assert.False(t, r.Is(tt.testDate)) + } else { + assert.True(t, r.Is(tt.testDate)) + assert.Equal(t, tt.rangeStr, r.String()) + } + } + }) + } + + }) + + t.Run("Range Is", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expected bool + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, true}, + {"[2025-01-31 14:15:16, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), false, true}, + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, false}, + {"[, ]", datetime.Now(), false, false}, + {"[2, ]", datetime.Now(), true, false}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected, r.Is(tt.testDate)) + } + }) + } + }) + + t.Run("Range brackets", func(t *testing.T) { + tests := []struct { + expectedErr bool + start string + from string + to string + end string + expected func() any + }{ + {true, "[", "2025-01-31", "2026-01-31", "]", nil}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", "]", func() any { + val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStartOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", "]", func() any { + val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", ")", func() any { + val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeStartStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", ")", func() any { + val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range brackets: "+fmt.Sprintf("From: %s%s, To: %s%s", tt.start, tt.from, tt.to, tt.end), func(t *testing.T) { + r, err := datetime.NewRange(tt.from, tt.to, datetime.RangeStart(tt.start), datetime.RangeEnd(tt.end)) + + if tt.expectedErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + } + }) + } + }) +} diff --git a/tests/time_test.go b/tests/time_test.go index 89fda88..62a789f 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "github.com/gouef/datetime" "github.com/gouef/datetime/time" "github.com/stretchr/testify/assert" @@ -152,3 +153,209 @@ func TestTimeFromString(t *testing.T) { }) } } + +func TestTimeRange(t *testing.T) { + t.Run("New Range", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expectedValidErr bool + expected func() any + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, func() any { + val, err := datetime.NewRangeStrict( + "2025-01-31", "2026-01-31") + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, false, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, nil}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false, nil}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, true, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[, ]", datetime.Now(), false, true, func() any { + val, err := datetime.NewRange( + "", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", datetime.Now(), true, true, func() any { + val, err := datetime.NewRange( + "2025-01-31 14:15:16", "2026-02-31 14:15:16", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + if tt.expectedValidErr { + assert.False(t, r.Is(tt.testDate)) + } else { + assert.True(t, r.Is(tt.testDate)) + assert.Equal(t, tt.rangeStr, r.String()) + } + } + }) + } + + }) + + t.Run("Range Is", func(t *testing.T) { + tests := []struct { + rangeStr string + testDate any + expectedErr bool + expected bool + }{ + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, true}, + {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, true}, + {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, false}, + {"[, ]", datetime.Now(), false, false}, + {"[2, ]", datetime.Now(), true, false}, + } + + for _, tt := range tests { + t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + if tt.expectedErr { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.Error(t, err) + assert.Nil(t, r) + + } else { + r, err := datetime.RangeFromString(tt.rangeStr) + assert.NoError(t, err) + assert.Equal(t, tt.expected, r.Is(tt.testDate)) + } + }) + } + }) + + t.Run("Range brackets", func(t *testing.T) { + tests := []struct { + expectedErr bool + start string + from string + to string + end string + expected func() any + }{ + {true, "[", "2025-01-31", "2026-01-31", "]", nil}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", "]", func() any { + val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", "]", func() any { + val, err := datetime.NewRangeStartOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", "]", func() any { + val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeOptional("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "(", "2025-01-31 14:15:16", "", ")", func() any { + val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "", "2026-01-31 17:18:19", ")", func() any { + val, err := datetime.NewRangeStartStrict("", "2026-01-31 17:18:19") + assert.NoError(t, err) + return val + }}, + {false, "[", "2025-01-31 14:15:16", "", ")", func() any { + val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "") + assert.NoError(t, err) + return val + }}, + } + + for _, tt := range tests { + t.Run("New Range brackets: "+fmt.Sprintf("From: %s%s, To: %s%s", tt.start, tt.from, tt.to, tt.end), func(t *testing.T) { + r, err := datetime.NewRange(tt.from, tt.to, datetime.RangeStart(tt.start), datetime.RangeEnd(tt.end)) + + if tt.expectedErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected(), r) + } + }) + } + }) +} diff --git a/time/range.go b/time/range.go index 1989258..a396742 100644 --- a/time/range.go +++ b/time/range.go @@ -21,7 +21,7 @@ type Range struct { end datetime.RangeEnd } -func NewDateRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) *Range { +func NewRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) *Range { return &Range{ from: Value(from), to: Value(to), @@ -30,6 +30,22 @@ func NewDateRange(from, to string, start datetime.RangeStart, end datetime.Range } } +func NewRangeOptional(from, to string) *Range { + return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndOptional) +} + +func NewRangeStrict(from, to string) *Range { + return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndStrict) +} + +func NewRangeStartStrict(from, to string) *Range { + return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndOptional) +} + +func NewRangeStartOptional(from, to string) *Range { + return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndStrict) +} + func RangeFromString(dateRange string) (*Range, error) { errs := validator.Validate(dateRange, constraints.RegularExpression{Regexp: RangeRegexp}) @@ -41,7 +57,7 @@ func RangeFromString(dateRange string) (*Range, error) { match := re.FindStringSubmatch(dateRange) openBracket, date1, date2, closeBracket := match[1], match[2], match[3], match[4] - return NewDateRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)), nil + return NewRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)), nil } func (d *Range) Start() datetime.RangeStart { diff --git a/time/time.go b/time/time.go index 99f4dac..fd89422 100644 --- a/time/time.go +++ b/time/time.go @@ -13,7 +13,7 @@ import ( const ( Regexp = `^(\d{2}):(\d{2}):(\d{2})?$` - DateTimeRegexp = `^(((\d{4})-(\d{2})-(\d{2}))( ))?((\d{2}):(\d{2}):(\d{2}))$` + DateTimeRegexp = `^((\d{4})-(\d{2})-(\d{2}))?\s*((0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))$` ) type Time struct { @@ -59,9 +59,9 @@ func FromString(value string) (datetime.Interface, error) { re := regexp.MustCompile(DateTimeRegexp) match := re.FindStringSubmatch(value) - hour, _ := strconv.Atoi(match[8]) - minute, _ := strconv.Atoi(match[9]) - second, _ := strconv.Atoi(match[10]) + hour, _ := strconv.Atoi(match[6]) + minute, _ := strconv.Atoi(match[7]) + second, _ := strconv.Atoi(match[8]) return New(hour, minute, second) } @@ -89,5 +89,13 @@ func (t *Time) Equal(u datetime.Interface) bool { } func (t *Time) Between(start, end datetime.Interface) bool { - return t.Time().Before(end.Time()) && t.Time().After(start.Time()) + return t.Before(end) && t.After(start) +} + +func (t *Time) Before(u datetime.Interface) bool { + return t.Time().Before(u.Time()) +} + +func (t *Time) After(u datetime.Interface) bool { + return t.Time().After(u.Time()) } diff --git a/valueInterface.go b/valueInterface.go new file mode 100644 index 0000000..32c4274 --- /dev/null +++ b/valueInterface.go @@ -0,0 +1,5 @@ +package datetime + +type ValueInterface interface { + Date() Interface +} From 0696629c7d36a046377dd83462cbaaeeacaf6088 Mon Sep 17 00:00:00 2001 From: Jan Galek Date: Wed, 5 Feb 2025 18:56:58 +0100 Subject: [PATCH 2/4] [Add] tests for ranges --- date/range.go | 11 ++++++-- tests/date_test.go | 17 ++++++++++-- tests/time_test.go | 66 +++++++++++++++++++++++----------------------- time/range.go | 51 ++++++++++++++++++++++++++--------- time/time.go | 13 ++++++++- 5 files changed, 107 insertions(+), 51 deletions(-) diff --git a/date/range.go b/date/range.go index f15c2a7..a67a5df 100644 --- a/date/range.go +++ b/date/range.go @@ -107,6 +107,15 @@ func (d *Range) Is(value any) bool { from, _ := FromString(string(d.From())) to, _ := FromString(string(d.To())) + + if from == nil { + return date.Before(to) + } + + if to == nil { + return date.After(from) + } + return date.Between(from, to) } @@ -116,8 +125,6 @@ func (d *Range) format(date any) (datetime.Interface, error) { return New(i.Year(), int(i.Month()), i.Day()) case *Date: return i, nil - case Date: - return &i, nil case string: return FromString(i) default: diff --git a/tests/date_test.go b/tests/date_test.go index 30134c6..6b93bd8 100644 --- a/tests/date_test.go +++ b/tests/date_test.go @@ -283,9 +283,19 @@ func TestDateRange(t *testing.T) { }) } + t.Run("New range invalid from and to", func(t *testing.T) { + _, err := date.NewRange( + "invalid", "2026-01-31", datetime.RangeStartStrict, datetime.RangeEndStrict) + assert.Error(t, err) + _, err = date.NewRange( + "2025-01-31", "invalid", datetime.RangeStartStrict, datetime.RangeEndStrict) + assert.Error(t, err) + }) + }) t.Run("Range Is", func(t *testing.T) { + d, _ := date.New(2025, 5, 6) tests := []struct { rangeStr string testDate any @@ -296,7 +306,10 @@ func TestDateRange(t *testing.T) { {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, true}, {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", true, true}, {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", true, true}, - {"[2025-01-31 14:15:16, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), true, true}, + {"[2025-01-31, ]", time.Date(2025, 5, 6, 20, 21, 22, 0, time.UTC), false, true}, + {"[2025-01-31, ]", d, false, true}, + {"[, 2029-01-31]", d, false, true}, + {"[, 2029-01-31]", 30, false, false}, {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, true, false}, {"[, ]", date.Now(), true, false}, @@ -304,7 +317,7 @@ func TestDateRange(t *testing.T) { } for _, tt := range tests { - t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { + t.Run("New Range: "+tt.rangeStr, func(t *testing.T) { if tt.expectedErr { r, err := date.RangeFromString(tt.rangeStr) assert.Error(t, err) diff --git a/tests/time_test.go b/tests/time_test.go index 62a789f..6595f41 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -164,51 +164,51 @@ func TestTimeRange(t *testing.T) { expected func() any }{ {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, func() any { - val, err := datetime.NewRangeStrict( + val, err := time.NewRangeStrict( "2025-01-31", "2026-01-31") assert.NoError(t, err) return val }}, {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { - val, err := datetime.NewRange( - "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + val, err := time.NewRange( + "14:15:16", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { - val, err := datetime.NewRange( - "", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + val, err := time.NewRange( + "", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, false, func() any { - val, err := datetime.NewRange( - "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + val, err := time.NewRange( + "14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, false, func() any { - val, err := datetime.NewRange( - "2025-01-31 14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + val, err := time.NewRange( + "14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, nil}, {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false, nil}, {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, true, func() any { - val, err := datetime.NewRange( + val, err := time.NewRange( "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, - {"[, ]", datetime.Now(), false, true, func() any { - val, err := datetime.NewRange( + {"[, ]", time.Now(), true, true, func() any { + val, err := time.NewRange( "", "", datetime.RangeStart("["), datetime.RangeEnd("]")) - assert.NoError(t, err) + assert.Error(t, err) return val }}, - {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", datetime.Now(), true, true, func() any { - val, err := datetime.NewRange( + {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", time.Now(), true, true, func() any { + val, err := time.NewRange( "2025-01-31 14:15:16", "2026-02-31 14:15:16", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.Error(t, err) return val @@ -218,12 +218,12 @@ func TestTimeRange(t *testing.T) { for _, tt := range tests { t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { if tt.expectedErr { - r, err := datetime.RangeFromString(tt.rangeStr) + r, err := time.RangeFromString(tt.rangeStr) assert.Error(t, err) assert.Nil(t, r) } else { - r, err := datetime.RangeFromString(tt.rangeStr) + r, err := time.RangeFromString(tt.rangeStr) assert.NoError(t, err) assert.Equal(t, tt.expected(), r) if tt.expectedValidErr { @@ -253,19 +253,19 @@ func TestTimeRange(t *testing.T) { {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, false}, - {"[, ]", datetime.Now(), false, false}, + {"[, ]", datetime.Now(), true, false}, {"[2, ]", datetime.Now(), true, false}, } for _, tt := range tests { t.Run("New Range: "+tt.rangeStr+fmt.Sprintf(", valid date: %v", tt.testDate), func(t *testing.T) { if tt.expectedErr { - r, err := datetime.RangeFromString(tt.rangeStr) + r, err := time.RangeFromString(tt.rangeStr) assert.Error(t, err) assert.Nil(t, r) } else { - r, err := datetime.RangeFromString(tt.rangeStr) + r, err := time.RangeFromString(tt.rangeStr) assert.NoError(t, err) assert.Equal(t, tt.expected, r.Is(tt.testDate)) } @@ -284,62 +284,62 @@ func TestTimeRange(t *testing.T) { }{ {true, "[", "2025-01-31", "2026-01-31", "]", nil}, {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { - val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + val, err := time.NewRangeStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "[", "", "2026-01-31 17:18:19", "]", func() any { - val, err := datetime.NewRangeStrict("", "2026-01-31 17:18:19") + val, err := time.NewRangeStrict("", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "[", "2025-01-31 14:15:16", "", "]", func() any { - val, err := datetime.NewRangeStrict("2025-01-31 14:15:16", "") + val, err := time.NewRangeStrict("2025-01-31 14:15:16", "") assert.NoError(t, err) return val }}, {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", "]", func() any { - val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + val, err := time.NewRangeStartOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "(", "", "2026-01-31 17:18:19", "]", func() any { - val, err := datetime.NewRangeStartOptional("", "2026-01-31 17:18:19") + val, err := time.NewRangeStartOptional("", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "(", "2025-01-31 14:15:16", "", "]", func() any { - val, err := datetime.NewRangeStartOptional("2025-01-31 14:15:16", "") + val, err := time.NewRangeStartOptional("2025-01-31 14:15:16", "") assert.NoError(t, err) return val }}, {false, "(", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { - val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") + val, err := time.NewRangeOptional("2025-01-31 14:15:16", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "(", "", "2026-01-31 17:18:19", ")", func() any { - val, err := datetime.NewRangeOptional("", "2026-01-31 17:18:19") + val, err := time.NewRangeOptional("", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "(", "2025-01-31 14:15:16", "", ")", func() any { - val, err := datetime.NewRangeOptional("2025-01-31 14:15:16", "") + val, err := time.NewRangeOptional("2025-01-31 14:15:16", "") assert.NoError(t, err) return val }}, {false, "[", "2025-01-31 14:15:16", "2026-01-31 17:18:19", ")", func() any { - val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") + val, err := time.NewRangeStartStrict("2025-01-31 14:15:16", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "[", "", "2026-01-31 17:18:19", ")", func() any { - val, err := datetime.NewRangeStartStrict("", "2026-01-31 17:18:19") + val, err := time.NewRangeStartStrict("", "2026-01-31 17:18:19") assert.NoError(t, err) return val }}, {false, "[", "2025-01-31 14:15:16", "", ")", func() any { - val, err := datetime.NewRangeStartStrict("2025-01-31 14:15:16", "") + val, err := time.NewRangeStartStrict("2025-01-31 14:15:16", "") assert.NoError(t, err) return val }}, @@ -347,7 +347,7 @@ func TestTimeRange(t *testing.T) { for _, tt := range tests { t.Run("New Range brackets: "+fmt.Sprintf("From: %s%s, To: %s%s", tt.start, tt.from, tt.to, tt.end), func(t *testing.T) { - r, err := datetime.NewRange(tt.from, tt.to, datetime.RangeStart(tt.start), datetime.RangeEnd(tt.end)) + r, err := time.NewRange(tt.from, tt.to, datetime.RangeStart(tt.start), datetime.RangeEnd(tt.end)) if tt.expectedErr { assert.Error(t, err) diff --git a/time/range.go b/time/range.go index a396742..47d3bca 100644 --- a/time/range.go +++ b/time/range.go @@ -21,43 +21,47 @@ type Range struct { end datetime.RangeEnd } -func NewRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) *Range { +func NewRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) (*Range, error) { return &Range{ from: Value(from), to: Value(to), start: start, end: end, - } + }, nil } -func NewRangeOptional(from, to string) *Range { +func NewRangeOptional(from, to string) (*Range, error) { return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndOptional) } -func NewRangeStrict(from, to string) *Range { +func NewRangeStrict(from, to string) (*Range, error) { return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndStrict) } -func NewRangeStartStrict(from, to string) *Range { +func NewRangeStartStrict(from, to string) (*Range, error) { return NewRange(from, to, datetime.RangeStartStrict, datetime.RangeEndOptional) } -func NewRangeStartOptional(from, to string) *Range { +func NewRangeStartOptional(from, to string) (*Range, error) { return NewRange(from, to, datetime.RangeStartOptional, datetime.RangeEndStrict) } func RangeFromString(dateRange string) (*Range, error) { - errs := validator.Validate(dateRange, constraints.RegularExpression{Regexp: RangeRegexp}) + errs := validator.Validate(dateRange, constraints.RegularExpression{Regexp: datetime.RangeRegexp}) if len(errs) != 0 { return nil, errors.New(fmt.Sprintf("unsupported format of date range \"%s\"", dateRange)) } - re := regexp.MustCompile(RangeRegexp) + re := regexp.MustCompile(datetime.RangeRegexp) match := re.FindStringSubmatch(dateRange) - openBracket, date1, date2, closeBracket := match[1], match[2], match[3], match[4] + openBracket, date1, date2, closeBracket := match[1], match[7], match[14], match[16] + + if date1 == "" && date2 == "" { + return nil, errors.New(fmt.Sprintf("time range from and to can not be both empty \"%s\"", dateRange)) + } - return NewRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)), nil + return NewRange(date1, date2, datetime.RangeStart(openBracket), datetime.RangeEnd(closeBracket)) } func (d *Range) Start() datetime.RangeStart { @@ -68,6 +72,14 @@ func (d *Range) End() datetime.RangeEnd { return d.end } +func (d *Range) From() datetime.ValueInterface { + return d.from +} + +func (d *Range) To() datetime.ValueInterface { + return d.to +} + func (d *Range) String() string { return fmt.Sprintf("%s%s,%s%s", d.start, d.from, d.to, d.end) } @@ -79,9 +91,22 @@ func (d *Range) Is(value any) bool { return false } - start, _ := FromString(string(d.start)) - end, _ := FromString(string(d.end)) - return date.Between(start, end) + from, _ := FromString(string(d.from)) + to, _ := FromString(string(d.to)) + + if from == nil && to == nil { + return false + } + + if from == nil { + return date.Before(to) + } + + if to == nil { + return date.After(from) + } + + return date.Between(from, to) } func (d *Range) format(date any) (datetime.Interface, error) { diff --git a/time/time.go b/time/time.go index fd89422..c721814 100644 --- a/time/time.go +++ b/time/time.go @@ -13,7 +13,7 @@ import ( const ( Regexp = `^(\d{2}):(\d{2}):(\d{2})?$` - DateTimeRegexp = `^((\d{4})-(\d{2})-(\d{2}))?\s*((0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))$` + DateTimeRegexp = `^(((\d+)-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))\s)?(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$` ) type Time struct { @@ -23,6 +23,17 @@ type Time struct { DateTime goTime.Time } +func Now() *Time { + now := goTime.Now() + + return &Time{ + Hour: now.Hour(), + Minute: now.Minute(), + Second: now.Second(), + DateTime: now, + } +} + func New(hour, minute, second int) (datetime.Interface, error) { errs := validator.Validate(hour, constraints.Range{Min: 0, Max: 23}) From 25331f1787ae66a31ac779a810cf3845933a8d4e Mon Sep 17 00:00:00 2001 From: Jan Galek Date: Wed, 5 Feb 2025 20:25:06 +0100 Subject: [PATCH 3/4] [Add] test coverage --- range.go | 4 ++-- rangeInterface.go | 18 ++++++++++-------- tests/time_test.go | 26 +++++++++----------------- time/range.go | 45 +++++++++++++++++++++++++++++++++++++++------ time/time.go | 13 ++++++++----- 5 files changed, 68 insertions(+), 38 deletions(-) diff --git a/range.go b/range.go index a0f5372..296899d 100644 --- a/range.go +++ b/range.go @@ -65,8 +65,8 @@ func RangeFromString(value string) (*Range, error) { match := re.FindStringSubmatch(value) start := match[1] from := match[2] - to := match[9] - end := match[16] + to := match[12] + end := match[22] return NewRange(from, to, RangeStart(start), RangeEnd(end)) } diff --git a/rangeInterface.go b/rangeInterface.go index 0313080..4e0e43a 100644 --- a/rangeInterface.go +++ b/rangeInterface.go @@ -18,14 +18,16 @@ var ( ) const ( - YearRegexp = `(\d+)` - MonthRegexp = `(0[1-9]|1[0-2])` - DayRegexp = `(0[1-9]|[12][0-9]|3[01])` - HourRegexp = `(0[0-9]|1[0-9]|2[0-3])` - MinuteRegexp = `[0-5][0-9]` - SecondRegexp = `[0-5][0-9]` - - RangeRegexp = `^([\[\(])((` + YearRegexp + `-` + MonthRegexp + `-` + DayRegexp + `) (` + HourRegexp + `:` + MinuteRegexp + `:` + SecondRegexp + `))?\s*,\s*((` + YearRegexp + `-` + MonthRegexp + `-` + DayRegexp + `) (` + HourRegexp + `:` + MinuteRegexp + `:` + SecondRegexp + `))?([\]\)])$` + YearRegexp = `(\d+)` + MonthRegexp = `(0[1-9]|1[0-2])` + DayRegexp = `(0[1-9]|[12][0-9]|3[01])` + HourRegexp = `(0[0-9]|1[0-9]|2[0-3])` + MinuteRegexp = `[0-5][0-9]` + SecondRegexp = `[0-5][0-9]` + DateRegexp = YearRegexp + `-` + MonthRegexp + `-` + DayRegexp + TimeRegexp = `(` + HourRegexp + `):(` + MinuteRegexp + `):(` + SecondRegexp + `)` + DateTimeRegexp = `((` + DateRegexp + `) (` + TimeRegexp + `))` + RangeRegexp = `^([\[\(])` + DateTimeRegexp + `?\s*,\s*` + DateTimeRegexp + `?([\]\)])$` ) type RangeInterface interface { diff --git a/tests/time_test.go b/tests/time_test.go index 6595f41..a7b7a8a 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -169,35 +169,33 @@ func TestTimeRange(t *testing.T) { assert.NoError(t, err) return val }}, - {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + {"[14:15:16, 17:18:19]", "2025-05-06 15:21:22", false, false, func() any { val, err := time.NewRange( "14:15:16", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, - {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, false, func() any { + {"[, 17:18:19]", "2025-05-06 15:21:22", false, false, func() any { val, err := time.NewRange( "", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, - {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, false, func() any { + {"[14:15:16, ]", "2025-05-06 15:21:22", false, false, func() any { val, err := time.NewRange( "14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, - {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, false, func() any { + {"[14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, false, func() any { val, err := time.NewRange( "14:15:16", "", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, - {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false, nil}, - {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false, nil}, - {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, true, func() any { + {"[14:15:16, 17:18:19]", 2025, false, true, func() any { val, err := time.NewRange( - "2025-01-31 14:15:16", "2026-01-31 17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + "14:15:16", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) assert.NoError(t, err) return val }}, @@ -207,12 +205,6 @@ func TestTimeRange(t *testing.T) { assert.Error(t, err) return val }}, - {"[2025-01-31 14:15:16, 2026-02-31 14:15:16]", time.Now(), true, true, func() any { - val, err := time.NewRange( - "2025-01-31 14:15:16", "2026-02-31 14:15:16", datetime.RangeStart("["), datetime.RangeEnd("]")) - assert.Error(t, err) - return val - }}, } for _, tt := range tests { @@ -246,9 +238,9 @@ func TestTimeRange(t *testing.T) { expected bool }{ {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, - {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, - {"[, 2026-01-31 17:18:19]", "2025-05-06 20:21:22", false, true}, - {"[2025-01-31 14:15:16, ]", "2025-05-06 20:21:22", false, true}, + {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", "2025-05-06 15:21:22", false, true}, + {"[, 2026-01-31 17:18:19]", "2025-05-06 16:21:22", false, true}, + {"[2025-01-31 14:15:16, ]", "2025-05-06 15:21:22", false, true}, {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, true}, {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, diff --git a/time/range.go b/time/range.go index 47d3bca..7848f17 100644 --- a/time/range.go +++ b/time/range.go @@ -11,7 +11,7 @@ import ( ) const ( - RangeRegexp = `^([\[\(])(\d{4}-\d{2}-\d{2})?,(\d{4}-\d{2}-\d{2})?([\]\)])$` + RangeRegexp = `^([\[\(])` + DateTimeRegexp + `?\s*,\s*` + DateTimeRegexp + `?([\]\)])$` ) type Range struct { @@ -22,6 +22,35 @@ type Range struct { } func NewRange(from, to string, start datetime.RangeStart, end datetime.RangeEnd) (*Range, error) { + + if from == "" && to == "" { + return nil, errors.New("from and to (both) can not be empty") + } + + validFrom, err := getTimeFromDateTime(from) + + if err != nil && from != "" { + return nil, errors.New(fmt.Sprintf("from (%s) is not valid", from)) + } + + validTo, err := getTimeFromDateTime(to) + + if err != nil && to != "" { + return nil, errors.New(fmt.Sprintf("to (%s) is not valid", to)) + } + + if validFrom == nil { + from = "" + } else { + from = validFrom.ToString() + } + + if validTo == nil { + to = "" + } else { + to = validTo.ToString() + } + return &Range{ from: Value(from), to: Value(to), @@ -47,15 +76,15 @@ func NewRangeStartOptional(from, to string) (*Range, error) { } func RangeFromString(dateRange string) (*Range, error) { - errs := validator.Validate(dateRange, constraints.RegularExpression{Regexp: datetime.RangeRegexp}) + errs := validator.Validate(dateRange, constraints.RegularExpression{Regexp: RangeRegexp}) if len(errs) != 0 { return nil, errors.New(fmt.Sprintf("unsupported format of date range \"%s\"", dateRange)) } - re := regexp.MustCompile(datetime.RangeRegexp) + re := regexp.MustCompile(RangeRegexp) match := re.FindStringSubmatch(dateRange) - openBracket, date1, date2, closeBracket := match[1], match[7], match[14], match[16] + openBracket, date1, date2, closeBracket := match[1], match[7], match[17], match[22] if date1 == "" && date2 == "" { return nil, errors.New(fmt.Sprintf("time range from and to can not be both empty \"%s\"", dateRange)) @@ -81,7 +110,7 @@ func (d *Range) To() datetime.ValueInterface { } func (d *Range) String() string { - return fmt.Sprintf("%s%s,%s%s", d.start, d.from, d.to, d.end) + return fmt.Sprintf("%s%s, %s%s", d.Start(), d.From(), d.To(), d.End()) } func (d *Range) Is(value any) bool { @@ -109,10 +138,14 @@ func (d *Range) Is(value any) bool { return date.Between(from, to) } +func getTimeFromDateTime(date string) (datetime.Interface, error) { + return FromString(date) +} + func (d *Range) format(date any) (datetime.Interface, error) { switch i := date.(type) { case time.Time: - return New(i.Year(), int(i.Month()), i.Day()) + return New(i.Hour(), i.Minute(), i.Second()) case *Time: return i, nil case Time: diff --git a/time/time.go b/time/time.go index c721814..f325011 100644 --- a/time/time.go +++ b/time/time.go @@ -13,7 +13,10 @@ import ( const ( Regexp = `^(\d{2}):(\d{2}):(\d{2})?$` - DateTimeRegexp = `^(((\d+)-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))\s)?(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$` + DateRegexp = datetime.YearRegexp + `-` + datetime.MonthRegexp + `-` + datetime.DayRegexp + TimeRegexp = `(` + datetime.HourRegexp + `):(` + datetime.MinuteRegexp + `):(` + datetime.SecondRegexp + `)` + DateTimeRegexp = `((` + DateRegexp + `)?\s*(` + TimeRegexp + `))` + //DateTimeRegexp = `^((` + datetime.DateRegexp + `)?\s*((` + datetime.HourRegexp + `):(` + datetime.MinuteRegexp + `):(` + datetime.SecondRegexp + `)))$` ) type Time struct { @@ -65,14 +68,14 @@ func FromString(value string) (datetime.Interface, error) { errs := validator.Validate(value, constraints.RegularExpression{Regexp: DateTimeRegexp}) if len(errs) != 0 { - return nil, errors.New(fmt.Sprintf("unsupported format of date \"%s\"", value)) + return nil, errors.New(fmt.Sprintf("unsupported format of time \"%s\"", value)) } re := regexp.MustCompile(DateTimeRegexp) match := re.FindStringSubmatch(value) - hour, _ := strconv.Atoi(match[6]) - minute, _ := strconv.Atoi(match[7]) - second, _ := strconv.Atoi(match[8]) + hour, _ := strconv.Atoi(match[8]) + minute, _ := strconv.Atoi(match[9]) + second, _ := strconv.Atoi(match[10]) return New(hour, minute, second) } From 800ae34288b98010eba4524932adce49080f09fa Mon Sep 17 00:00:00 2001 From: Jan Galek Date: Wed, 5 Feb 2025 20:35:08 +0100 Subject: [PATCH 4/4] [Add] test coverage --- tests/time_test.go | 29 +++++++++++++++++++++++++++-- time/range.go | 6 ------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/time_test.go b/tests/time_test.go index a7b7a8a..33b3e0d 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -156,6 +156,15 @@ func TestTimeFromString(t *testing.T) { func TestTimeRange(t *testing.T) { t.Run("New Range", func(t *testing.T) { + + val, err := time.NewRange("", "", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + assert.Nil(t, val) + + val, err = time.NewRange("", "invalid", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.Error(t, err) + assert.Nil(t, val) + tests := []struct { rangeStr string testDate any @@ -199,6 +208,18 @@ func TestTimeRange(t *testing.T) { assert.NoError(t, err) return val }}, + {"[invalid, 17:18:19]", 2025, true, true, func() any { + val, err := time.NewRange( + "14:15:16", "17:18:19", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, + {"[14:15:16, invalid]", 2025, true, true, func() any { + val, err := time.NewRange( + "14:15:16", "invalid", datetime.RangeStart("["), datetime.RangeEnd("]")) + assert.NoError(t, err) + return val + }}, {"[, ]", time.Now(), true, true, func() any { val, err := time.NewRange( "", "", datetime.RangeStart("["), datetime.RangeEnd("]")) @@ -231,6 +252,9 @@ func TestTimeRange(t *testing.T) { }) t.Run("Range Is", func(t *testing.T) { + ttTime, err := time.New(20, 21, 22) + assert.Nil(t, err) + tests := []struct { rangeStr string testDate any @@ -242,11 +266,12 @@ func TestTimeRange(t *testing.T) { {"[, 2026-01-31 17:18:19]", "2025-05-06 16:21:22", false, true}, {"[2025-01-31 14:15:16, ]", "2025-05-06 15:21:22", false, true}, {"[2025-01-31 14:15:16, ]", goTime.Date(2025, 5, 6, 20, 21, 22, 0, goTime.UTC), false, true}, + {"[2025-01-31 14:15:16, ]", ttTime, false, true}, {"[2025-01-31, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31]", "2025-05-06", true, false}, {"[2025-01-31 14:15:16, 2026-01-31 17:18:19]", 2025, false, false}, - {"[, ]", datetime.Now(), true, false}, - {"[2, ]", datetime.Now(), true, false}, + {"[, ]", time.Now(), true, false}, + {"[2, ]", time.Now(), true, false}, } for _, tt := range tests { diff --git a/time/range.go b/time/range.go index 7848f17..704c638 100644 --- a/time/range.go +++ b/time/range.go @@ -123,10 +123,6 @@ func (d *Range) Is(value any) bool { from, _ := FromString(string(d.from)) to, _ := FromString(string(d.to)) - if from == nil && to == nil { - return false - } - if from == nil { return date.Before(to) } @@ -148,8 +144,6 @@ func (d *Range) format(date any) (datetime.Interface, error) { return New(i.Hour(), i.Minute(), i.Second()) case *Time: return i, nil - case Time: - return &i, nil case string: return FromString(i) default: