In the world of programming, working with dates and times is a common task that often requires precision and flexibility.
While the Go programming language’s standard library provides the time
package for handling time-related operations, there are instances where developers need additional utilities to simplify time-dependent tasks.
In this blog post, we will explore a set of utility functions that serve as a wrapper around the time
package to facilitate operations. If you are unaware of the time
package, consider referring to it before going deep with advanced utilities.
Refer to the Time utility functions you will always need for basic ones.
🎯 So, let’s delve deep into implementation.
Stop the habit of wishful thinking and start the habit of thoughtful wishes with Justly.
The start of a month is a fundamental reference point for many date-related calculations. Let’s create a function that takes a date as input and returns the first day of the respective month as per the system’s timezone:
func StartOfMonth(date time.Time) time.Time {
return time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, date.Location())
}
// function called
StartOfMonth(time.Now())
// output
2024-01-01 00:00:00 +0530 IST
This function sets the day of the month to 1, leaving the other components unchanged.
Conversely, getting the end of a month is equally important. The following function returns the last second of the last day of a given month:
func EndOfMonth(date time.Time) time.Time {
firstDayOfNextMonth := StartOfMonth(date).AddDate(0, 1, 0)
return firstDayOfNextMonth.Add(-time.Second)
}
// function called
EndOfMonth(time.Now())
// output
2024-01-31 23:59:59 +0530 IST
This function utilizes the previously defined StartOfMonth
function to find the first day of the next month and subtracts one second to obtain the end of the current month.
For tasks like creating a calendar or displaying weekly data, it’s helpful to know the starting day of each week. The following function provides the first day of the week for a given date:
func StartOfDayOfWeek(date time.Time) time.Time {
daysSinceSunday := int(date.Weekday())
return date.AddDate(0, 0, -daysSinceSunday)
}
// function called
StartOfDayOfWeek(time.Now()) // time.Now() = 2024-01-16 16:00:12.901778919 +0530 IST
// output
2024-01-14 11:31:37.344224696 +0530 IST
This function calculates the number of days since Sunday and subtracts this value from the given date.
Similarly, getting the last day of the week is often necessary. Here’s a function that accomplishes this:
func EndOfDayOfWeek(date time.Time) time.Time {
daysUntilSaturday := 6 - int(date.Weekday())
return date.AddDate(0, 0, daysUntilSaturday)
}
// function called
EndOfDayOfWeek(time.Now()) // time.Now() = 2024-01-16 16:00:12.901778919 +0530 IST
// output
2024-01-20 11:31:37.344227536 +0530 IST
This function calculates the number of days until Saturday and adds this value to the given date.
To obtain a comprehensive view of each week in a month, we can combine the previous two functions. The following function returns a slice containing the start and end day of each week in a given month:
func StartAndEndOfWeeksOfMonth(year, month int) []struct{ Start, End time.Time } {
startOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
weeks := make([]struct{ Start, End time.Time }, 0)
for current := startOfMonth; current.Month() == time.Month(month); current = current.AddDate(0, 0, 7) {
startOfWeek := StartOfDayOfWeek(current)
endOfWeek := EndOfDayOfWeek(current)
if endOfWeek.Month() != time.Month(month) {
endOfWeek = EndOfMonth(current)
}
weeks = append(weeks, struct{ Start, End time.Time }{startOfWeek, endOfWeek})
}
return weeks
}
// function called
StartAndEndOfWeeksOfMonth(2024, 1)
// output
[{2023-12-31 00:00:00 +0530 IST 2024-01-06 00:00:00 +0530 IST}
{2024-01-07 00:00:00 +0530 IST 2024-01-13 00:00:00 +0530 IST}
{2024-01-14 00:00:00 +0530 IST 2024-01-20 00:00:00 +0530 IST}
{2024-01-21 00:00:00 +0530 IST 2024-01-27 00:00:00 +0530 IST}
{2024-01-28 00:00:00 +0530 IST 2024-01-31 23:59:59 +0530 IST}]
This function iterates through each week of the given month, determining each week's start and end days.
Lastly, determining the week number of a month from a specific date can be achieved with the following function:
func WeekNumberInMonth(date time.Time) int {
startOfMonth := StartOfMonth(date)
_, week := date.ISOWeek()
_, startWeek := startOfMonth.ISOWeek()
return week - startWeek + 1
}
// function called
WeekNumberInMonth(time.Now()) // time.Now() = 2024-01-16 16:00:12.901778919 +0530 IST
// output
3
This function calculates the ISO week number of the given date and subtracts the ISO week number of the first day of the month, adding 1 to get the relative week number within the month.
To retrieve the first moment of the year for a given date, we can create a function as follows:
func StartOfYear(date time.Time) time.Time {
return time.Date(date.Year(), time.January, 1, 0, 0, 0, 0, date.Location())
}
// function called
StartOfYear(time.Now())
// output
2024-01-01 00:00:00 +0530 IST
This function sets the month to January and the day to 1, providing the start of the year.
Similarly, we can define a function to obtain the last second of the last day of the year:
func EndOfYear(date time.Time) time.Time {
startOfNextYear := StartOfYear(date).AddDate(1, 0, 0)
return startOfNextYear.Add(-time.Second)
}
// function called
EndOfYear(time.Now())
// output
2024-12-31 23:59:59 +0530 IST
This function leverages the StartOfYear
function to find the first day of the next year and subtracts one second to get the end of the current year.
For tasks requiring quarterly data, we can create a function to obtain the start of the quarter for a given date:
func StartOfQuarter(date time.Time) time.Time {
// you can directly use 0, 1, 2, 3 quarter
quarter := (int(date.Month()) - 1) / 3
startMonth := time.Month(quarter * 3 + 1)
return time.Date(date.Year(), startMonth, 1, 0, 0, 0, 0, date.Location())
}
// function called
StartOfQuarter(time.Now())
// output
2024-01-01 00:00:00 +0530 IST
This function calculates the quarter of the given date and sets the month to the first month of that quarter.
To complement the previous function, we can create one to find the end of the quarter for a given date:
func EndOfQuarter(date time.Time) time.Time {
startOfNextQuarter := StartOfQuarter(date).AddDate(0, 3, 0)
return startOfNextQuarter.Add(-time.Second)
}
// function called
EndOfQuarter(time.Now())
// output
2024-03-31 23:59:59 +0530 IST
This function utilizes the StartOfQuarter
function to determine the first day of the next quarter and subtract one second to get the end of the current quarter.
Obtaining the start and end of the current week in a specific timezone can be useful. Here’s a function that does just that:
func CurrentWeekRange(timeZone string) (startOfWeek, endOfWeek time.Time) {
loc, _ = time.LoadLocation(timeZone)
now := time.Now().In(loc)
startOfWeek = StartOfDayOfWeek(now)
endOfWeek = EndOfDayOfWeek(now)
return startOfWeek, endOfWeek
}
// function called
start, end := CurrentWeekRange("America/New_York")
// output
2024-01-14 05:47:38.093990643 -0500 EST, 2024-01-20 05:47:38.093990643 -0500 EST
This function uses the previously defined StartOfDayOfWeek
and EndOfDayOfWeek
functions to get the range for the current week in the specified timezone. It considers the system’s timezone unless specified.
Calculating the duration between two dates is a common task. This function returns the duration between two time.Time
instances
func DurationBetween(start, end time.Time) time.Duration {
return end.Sub(start)
}
// function called
DurationBetween(time.Now(), time.Now().AddDate(0, 0, 7)) // time.Now() = 2024-01-16 16:00:12.901778919 +0530 IST
// output
168h0m0.000000056s
This simple utility function can be helpful when you need to measure the time elapsed between two events.
This function takes the year, month, and target day of the week as arguments and then returns a slice of time.Time
values representing all occurrences of the specified day in the given month.
func GetDatesForDayOfWeek(year, month int, day time.Weekday) []time.Time {
var dates []time.Time
firstDayOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
diff := int(day) - int(firstDayOfMonth.Weekday())
if diff < 0 {
diff += 7
}
firstDay := firstDayOfMonth.AddDate(0, 0, diff)
for current := firstDay; current.Month() == time.Month(month); current = current.AddDate(0, 0, 7) {
dates = append(dates, current)
}
return dates
}
// function called
GetDatesForDayOfWeek(2024, 1, time.Sunday)
// output
[2024-01-07 00:00:00 +0000 UTC 2024-01-14 00:00:00 +0000 UTC 2024-01-21 00:00:00 +0000 UTC 2024-01-28 00:00:00 +0000 UTC]
This function can be customized for different months and days of the week, providing a versatile solution for obtaining specific date occurrences in a month.
If your application deals with business days, this function can be handy for adding a certain number of business days to a given date:
func AddBusinessDays(startDate time.Time, daysToAdd int) time.Time {
currentDate := startDate
for i := 0; i < daysToAdd; {
currentDate = currentDate.AddDate(0, 0, 1)
if currentDate.Weekday() != time.Saturday && currentDate.Weekday() != time.Sunday {
i++
}
}
return currentDate
}
// function called
AddBusinessDays(time.Now(), 50) // time.Now() = 2024-01-16 16:00:12.901778919 +0530 IST
// output
2024-03-26 12:21:55.727849491 +0530 IST
This function iterates through days, skipping weekends until the desired number of business days is reached.
When presenting durations to users, formatting them in a human-readable way can enhance the user experience. This function converts a duration to a string in a user-friendly format:
func FormatDuration(duration time.Duration) string {
days := int(duration.Hours() / 24)
hours := int(duration.Hours()) % 24
minutes := int(duration.Minutes()) % 60
seconds := int(duration.Seconds()) % 60
return fmt.Sprintf("%dd %02dh %02dm %02ds", days, hours, minutes, seconds)
}
// function called
FormatDuration(time.Hour * 24 * 3 + time.Hour * 4 + time.Minute * 15 + time.Second * 30)
// output
3d 04h 15m 30s
This function breaks down the duration into days, hours, minutes, and seconds for a more user-friendly display.
Find the complete source code at — Golang: Date Time Utilities
That’s it for today. Keep Coding! 👋
By implementing these fifteen additional advanced utility functions, we have expanded the capabilities of our date-time package wrapper, which provides developers with a comprehensive set of tools for handling various time-related operations.
Whether you're building a scheduling application, generating reports, or working on any project involving temporal data or working with daily, weekly, monthly, or quarterly data, these functions can be seamlessly integrated into your codebase to simplify complex date and time manipulations.
Let's Work Together
Not sure where to start? We also offer code and architecture reviews, strategic planning, and more.