Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
bobgroup-go-utils
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Bob Public Utils
bobgroup-go-utils
Merge requests
!51
Refactor TradingHours struct and validation
Code
Review changes
Check out branch
Download
Patches
Plain diff
Expand sidebar
Merged
Refactor TradingHours struct and validation
trading_hours
into
main
Overview
4
Commits
4
Pipelines
0
Changes
1
Merged
Daniel Naude
requested to merge
trading_hours
into
main
10 months ago
Overview
4
Commits
4
Pipelines
0
Changes
1
0
0
Merge request reports
Compare
main
version 3
b1fbcc32
10 months ago
version 2
fe363a19
10 months ago
version 1
daab1fd2
10 months ago
main (base)
and
version 1
latest version
5465be18
4 commits,
10 months ago
version 3
b1fbcc32
3 commits,
10 months ago
version 2
fe363a19
2 commits,
10 months ago
version 1
daab1fd2
1 commit,
10 months ago
1 file
+
126
−
114
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
date_utils/date_utils.go
+
125
−
116
View file @ 5465be18
Edit in single-file editor
Open in Web IDE
Show full file
@@ -2,15 +2,15 @@ package date_utils
import
(
"fmt"
"github.com/jinzhu/now"
"reflect"
"strconv"
"strings"
"time"
"github.com/jinzhu/now"
"github.com/araddon/dateparse"
"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/utils"
)
const
TimeZoneString
=
"Africa/Johannesburg"
@@ -325,146 +325,155 @@ func formatTimestampsWithTimeZoneInSlice(fieldValue reflect.Value, location *tim
return
nil
}
// TradingHours represents an array of (StartTime,EndTime) pairs, one for each day of the week.
// The array is 0 indexed, with 0 being Sunday and 6 being Saturday and 7 being public holidays.
type
TradingHours
[]
struct
{
StartTime
string
`json:"start_time"`
EndTime
string
`json:"end_time"`
type
TradingHours
struct
{
Monday
TradingHoursDay
`json:"monday"`
Tuesday
TradingHoursDay
`json:"tuesday"`
Wednesday
TradingHoursDay
`json:"wednesday"`
Thursday
TradingHoursDay
`json:"thursday"`
Friday
TradingHoursDay
`json:"friday"`
Saturday
TradingHoursDay
`json:"saturday"`
Sunday
TradingHoursDay
`json:"sunday"`
Holidays
TradingHoursDay
`json:"holidays"`
}
func
(
t
TradingHours
)
Validate
()
error
{
if
len
(
t
)
!=
8
{
return
errors
.
Error
(
"Trading hours must have 8 days, 7 for every day of the week and 1 for public holidays"
)
if
err
:=
t
.
Monday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Monday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Tuesday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Tuesday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Wednesday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Wednesday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Thursday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Thursday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Friday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Friday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Saturday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Saturday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Sunday
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Sunday failed validation: %v"
,
err
.
Error
())
}
if
err
:=
t
.
Holidays
.
Validate
();
err
!=
nil
{
return
errors
.
Wrapf
(
err
,
"Holidays failed validation: %v"
,
err
.
Error
())
}
for
_
,
day
:=
range
t
{
if
day
.
StartTime
==
""
||
day
.
EndTime
==
""
{
// Allow empty trading hours for a day to represent closed
continue
return
nil
}
if
!
TimeBefore
(
day
.
StartTime
,
day
.
EndTime
)
{
return
errors
.
Error
(
"Start time must be before end time"
)
func
(
day
TradingHoursDay
)
Validate
()
error
{
if
day
.
StartTime
==
""
||
day
.
EndTime
==
""
{
// Allow empty trading hours for a day to represent closed
return
nil
}
if
len
(
day
.
StartTime
)
!=
5
||
len
(
day
.
EndTime
)
!=
5
{
return
errors
.
Error
(
"
T
ime must be in the format HH:MM"
)
return
errors
.
Error
(
"
t
ime must be in the format HH:MM"
)
}
startHourMinSlice
:=
strings
.
Split
(
day
.
StartTime
,
":"
)
if
len
(
startHourMinSlice
)
!=
2
{
return
errors
.
Error
(
"
T
ime must be in the format HH:MM"
)
return
errors
.
Error
(
"
t
ime must be in the format HH:MM"
)
}
startHour
,
startMin
:=
startHourMinSlice
[
0
],
startHourMinSlice
[
1
]
startHourInt
,
err
:=
strconv
.
Atoi
(
startHour
)
if
err
!=
nil
||
startHourInt
<
0
||
startHourInt
>
23
{
return
errors
.
Error
(
"
S
tart hour must be between 0 and 23"
)
return
errors
.
Error
(
"
s
tart hour must be between 0 and 23"
)
}
startMinInt
,
err
:=
strconv
.
Atoi
(
startMin
)
if
err
!=
nil
||
!
(
startMinInt
==
0
||
startMinInt
==
30
)
{
return
errors
.
Error
(
"
S
tart minute must be 0 or 30"
)
return
errors
.
Error
(
"
s
tart minute must be 0 or 30"
)
}
endHourMinSlice
:=
strings
.
Split
(
day
.
EndTime
,
":"
)
if
len
(
endHourMinSlice
)
!=
2
{
return
errors
.
Error
(
"
T
ime must be in the format HH:MM"
)
return
errors
.
Error
(
"
t
ime must be in the format HH:MM"
)
}
endHour
,
endMin
:=
endHourMinSlice
[
0
],
endHourMinSlice
[
1
]
endHourInt
,
err
:=
strconv
.
Atoi
(
endHour
)
if
err
!=
nil
||
endHourInt
<
0
||
endHourInt
>
23
{
return
errors
.
Error
(
"
E
nd hour must be between 0 and 23"
)
return
errors
.
Error
(
"
e
nd hour must be between 0 and 23"
)
}
endMinInt
,
err
:=
strconv
.
Atoi
(
endMin
)
if
err
!=
nil
||
!
(
endMinInt
==
0
||
endMinInt
==
30
||
endMinInt
==
59
)
{
return
errors
.
Error
(
"
E
nd minute must be 0, 30 or 59"
)
return
errors
.
Error
(
"
e
nd minute must be 0, 30 or 59"
)
}
if
!
TimeBefore
(
day
.
StartTime
,
day
.
EndTime
)
{
return
errors
.
Error
(
"start time must be before end time"
)
}
return
nil
}
func
(
t
TradingHours
)
String
()
string
{
var
result
strings
.
Builder
const
numberOfDaysInWeek
=
7
copyOfT
:=
utils
.
DeepCopy
(
t
)
.
(
TradingHours
)
weekdays
,
publicHolidays
:=
copyOfT
[
:
numberOfDaysInWeek
],
copyOfT
[
numberOfDaysInWeek
]
weekdays
=
append
(
weekdays
,
weekdays
[
0
])
// Add the first day (Sunday) to the end because we want Monday to be first in the string
rangeStartIndex
:=
1
for
i
:=
1
;
i
<
len
(
weekdays
);
i
++
{
currentDay
:=
weekdays
[
i
]
nextDay
:=
currentDay
if
i
+
1
<
len
(
weekdays
)
{
nextDay
=
weekdays
[
i
+
1
]
}
// Determine times
var
times
string
if
currentDay
.
StartTime
!=
""
&&
currentDay
.
EndTime
!=
""
{
startTime
,
err
:=
time
.
Parse
(
"15:04"
,
currentDay
.
StartTime
)
if
err
!=
nil
{
return
""
}
var
(
result
strings
.
Builder
weekdays
=
[]
TradingHoursDay
{
t
.
Monday
,
t
.
Tuesday
,
t
.
Wednesday
,
t
.
Thursday
,
t
.
Friday
,
t
.
Saturday
,
t
.
Sunday
}
rangeStartWeekday
=
time
.
Monday
)
endTime
,
err
:=
time
.
Parse
(
"15:04"
,
currentDay
.
EndTime
)
if
err
!=
nil
{
return
""
}
for
i
:=
range
weekdays
{
currentTradingHoursDay
:=
weekdays
[
i
]
times
=
startTime
.
Format
(
"3:04pm"
)
+
" – "
+
endTime
.
Format
(
"3:04pm"
)
if
currentDay
.
StartTime
==
"00:00"
&&
currentDay
.
EndTime
==
"23:59"
{
times
=
"All day"
}
// Here the index is wrapped so we will never go out of bounds. The next day after Sunday is Monday at index 0 because (6+1)%7 = 0
nextTradingHoursDay
:=
weekdays
[(
i
+
1
)
%
len
(
weekdays
)]
// Here we use the same value, as above, but for a different purpose of being compatible with the time package
// This is because the time package uses 0 for Sunday and 1 for Monday. This means when we get to index 6 (Sunday),
// to get the time.Weekday value for Sunday we need to use 0, and as before (6+1)%7 = 0
currentWeekday
:=
time
.
Weekday
((
i
+
1
)
%
len
(
weekdays
))
nextWeekday
:=
time
.
Weekday
((
i
+
2
)
%
len
(
weekdays
))
// Determine range description
var
rangeDescription
string
if
currentWeekday
==
rangeStartWeekday
{
rangeDescription
=
currentWeekday
.
String
()[
:
3
]
}
else
{
times
=
"Closed"
rangeDescription
=
fmt
.
Sprintf
(
"%s – %s"
,
rangeStartWeekday
.
String
()[
:
3
],
currentWeekday
.
String
()[
:
3
])
}
// If we're at the last element or the next day doesn't have the same times, we end the current range
if
i
==
len
(
weekdays
)
-
1
||
currentDay
.
StartTime
!=
nextDay
.
StartTime
||
currentDay
.
EndTime
!=
nextDay
.
EndTime
{
if
rangeStartIndex
==
i
{
day
:=
time
.
Weekday
(
rangeStartIndex
)
.
String
()[
:
3
]
if
rangeStartIndex
==
numberOfDaysInWeek
{
day
=
time
.
Sunday
.
String
()[
:
3
]
}
result
.
WriteString
(
fmt
.
Sprintf
(
"%s: %s"
,
day
,
times
))
}
else
{
rangeStartDay
:=
time
.
Weekday
(
rangeStartIndex
)
.
String
()[
:
3
]
rangeEndDay
:=
time
.
Weekday
(
i
)
.
String
()[
:
3
]
if
i
==
numberOfDaysInWeek
{
rangeEndDay
=
time
.
Sunday
.
String
()[
:
3
]
// If the next day has the same times and we're not at the last element, we continue the current range
if
nextTradingHoursDay
.
StartTime
==
currentTradingHoursDay
.
StartTime
&&
nextTradingHoursDay
.
EndTime
==
currentTradingHoursDay
.
EndTime
&&
i
<
len
(
weekdays
)
-
1
{
continue
}
result
.
WriteString
(
fmt
.
Sprintf
(
"%s – %s: %s"
,
rangeStartDay
,
rangeEndDay
,
times
))
result
.
WriteString
(
fmt
.
Sprintf
(
"%s: %s, "
,
rangeDescription
,
currentTradingHoursDay
.
String
()))
rangeStartWeekday
=
nextWeekday
}
if
i
<
len
(
weekdays
)
-
1
{
result
.
WriteString
(
", "
)
// Public holidays
result
.
WriteString
(
fmt
.
Sprintf
(
"Public holidays: %s"
,
t
.
Holidays
.
String
()))
return
result
.
String
()
}
rangeStartIndex
=
i
+
1
type
TradingHoursDay
struct
{
StartTime
string
`json:"start_time"`
EndTime
string
`json:"end_time"`
}
func
(
day
TradingHoursDay
)
String
()
string
{
if
day
.
StartTime
==
""
||
day
.
EndTime
==
""
{
return
"Closed"
}
// Public holidays
var
times
string
if
publicHolidays
.
StartTime
!=
""
&&
publicHolidays
.
EndTime
!=
""
{
startTime
,
err
:=
time
.
Parse
(
"15:04"
,
publicHolidays
.
StartTime
)
startTime
,
err
:=
time
.
Parse
(
"15:04"
,
day
.
StartTime
)
if
err
!=
nil
{
return
""
}
endTime
,
err
:=
time
.
Parse
(
"15:04"
,
publicHoli
day
s
.
EndTime
)
endTime
,
err
:=
time
.
Parse
(
"15:04"
,
day
.
EndTime
)
if
err
!=
nil
{
return
""
}
times
=
startTime
.
Format
(
"3:04pm"
)
+
" – "
+
endTime
.
Format
(
"3:04pm"
)
if
publicHolidays
.
StartTime
==
"00:00"
&&
publicHolidays
.
EndTime
==
"23:59"
{
times
=
"All day"
if
day
.
StartTime
==
"00:00"
&&
day
.
EndTime
==
"23:59"
{
return
"All day"
}
}
else
{
times
=
"Closed"
}
result
.
WriteString
(
fmt
.
Sprintf
(
", Public holidays: %s"
,
times
))
return
result
.
String
(
)
return
startTime
.
Format
(
"3:04pm"
)
+
" – "
+
endTime
.
Format
(
"3:04pm"
)
}
Loading