Golang — Advanced Date-Time Utility functions

Enhancing Time Package Functionality
Jan 16 2024 · 7 min read

Background

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.


Sponsored

Stop the habit of wishful thinking and start the habit of thoughtful wishes with Justly.


1. Get Start of Month

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.

2. Get End of Month

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.

3. Get Start Day of Every Week

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.

4. Get The End Day of Every Week

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.

5. Get The Start and End Day of Every Week of a Given Month

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.

6. Get The Week Number of a Month From The Date

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.

7. Get Start of Year

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.

8. Get End of 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.

9. Get Start of Quarter

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.

10. Get End of 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.

11. Get The Current Week Range

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.

12. Calculate Duration Between Two Dates

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.

13. Get Dates For The Day of The Week of The Given Month

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.

14. Add Business Days to a Date

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.

15. Format Duration as Human-Readable String

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! 👋


Conclusion

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. 


Similar Useful Articles

 


dharti.r image
Dharti Ramoliya
Web developer at canopas | Eager to Elevate Your Web Experience


dharti.r image
Dharti Ramoliya
Web developer at canopas | Eager to Elevate Your Web Experience

footer
Subscribe Here!
Follow us on
2024 Canopas Software LLP. All rights reserved.