'How do you deal with wall-clock times without date in Go?

I'd like to know if there is any existing package to deal with datetimes without date in Golang.

The problem is as follows. Imagine I want to store information about shifts in a company, including when this shifts start and end, I would create something like:

import "time"

type Shift struct {
    StartTime  time.Time
    FinishTime time.Time
    // More fields
}

The problem is, the field time.Time also stores information about the day, month and year, and that could lead to several problems at the time of comparing shifts.

Is there any alternative like civil.Date (https://pkg.go.dev/cloud.google.com/go/civil) but for just times instead of just dates?



Solution 1:[1]

In order to have just time and omit the other information about the day, month and year you can make use of Format() method. I have created a sample program for the same as follows:

package main

import (
    "fmt"
    "time"
)

func main() {

    const (
        layoutTime = "15:04:05"
    )

    fmt.Println("Date", time.Now())
    fmt.Println("Time", time.Now().Format(layoutTime))

}

Output:

Date 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
Time 23:00:00

Solution 2:[2]

It's kind of hard to answer this question, as you haven't really described what the type would need to do, other than this:

Yeah, but the point is, that time.Time will be prone to bugs in a less-than / more-than comparison if you allow the user to give a date. The goal should be to have a comparable struct and for the user not to give a date in order not to mess it up.

So going off that, you could use something like this:

package wall
import "errors"

type Clock struct { hour, minute, second int }

func NewClock(h, m, s int) (*Clock, error) {
   if h > 23 || m > 59 || s > 59 {
      return nil, errors.New("invalid input")
   }
   return &Clock{h, m, s}, nil
}

func (c Clock) Seconds() int {
   return c.hour * 60 * 60 + c.minute * 60 + c.second
}

func (c Clock) LessThan(d *Clock) bool {
   return c.Seconds() < d.Seconds()
}

Example:

package main
import "wall"

func main() {
   c, err := wall.NewClock(9, 59, 59)
   if err != nil {
      panic(err)
   }
   d, err := wall.NewClock(10, 59, 59)
   if err != nil {
      panic(err)
   }
   println(c.LessThan(d))
}

Solution 3:[3]

Trust me, You still need the Date, not just the Clock.

For example : what if the StartTime = 23:00:00 and FinishTime = 06:00:00, if you didn't capture the date, the information inside of the Shift struct is FinishTime < StartTime, and that goes against the words of start and finish itself.

That's where the date works, so if the FinishTime is more than the StartTime, the StartTime needs to be reduced by 1 day OR FinishTime plus 1 day.

Here's the example solution code that I make to check if the currentTime is between 2 time(clock)

First of all since I just need the clock information (HH:mm:ss), I prefer to use a string type for the StartTime and FinishTime.

type Shift struct {
    StartTime  string
    FinishTime string
}

and here's what it goes.


type Shift struct {
    StartTime  string
    FinishTime string
}

func main() {
    myShift := Shift{
        StartTime:  "23:00",
        FinishTime: "06:00",
    }
    actualStartTime, actualEndTIme := convertShiftClockToShiftTime(myShift.StartTime, myShift.FinishTime)
    fmt.Println(actualStartTime, actualEndTIme)
}

func convertShiftClockToShiftTime(StartTime string, FinishTime string) (startTime time.Time, endTime time.Time) {

    // I need to convert the time to a string so I can easily add the clock
    timeLayout := "2006-01-02T15:04"
    dateOnlyLayout := "2006-01-02"

    currentTime := time.Now()
    currentDate := currentTime.Format(dateOnlyLayout)
    // currentDate = Y-m-d

    startDateStr := currentDate + "T" + StartTime
    startTime, _ = time.Parse(timeLayout, startDateStr)

    endDateStr := currentDate + "T" + FinishTime
    endTime, _ = time.Parse(timeLayout, endDateStr)

    if startTime.After(endTime) {
        // plus 1 day to the endTime if the endTime < startTime
        endTime = endTime.AddDate(0, 0, 1)
    }

    return startTime, endTime
}

I created a function called convertShiftClockToShiftTime to change the clock into actual time as I mentioned before, what if the StartTime = 23:00 and EndTime = 06:00.

Basically the program should know if the StartTime is more than FinishTime, FinishTime should be tomorrow.

Hope this helps, I hope my explanation can explain to you what time itself means.

Solution 4:[4]

time.Duration can be used for wall-clock time without date. Some examples:

type Shift struct {
    StartTime  time.Duration
    FinishTime time.Duration
}

func main() {
    d1, _ := time.ParseDuration("11h")  // 11:00
    d2, _ := time.ParseDuration("20h")  // 20:00
    shift := Shift{
        StartTime:  d1,
        FinishTime: d2,
    }

    // If you want to put them into specific day
    today0 := time.Now().UTC().Truncate(24 * time.Hour)

    start := today0.Add(shift.StartTime)
    finish := today0.Add(shift.FinishTime)

    fmt.Printf("Start at: %v\nFinish at: %v\n", start, finish)
}

For times that span two days:

d1, _ := time.ParseDuration("-30m")   // 23:30 of previous day
d2, _ := time.ParseDuration("6h30m")  // 6:30 of next day

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gopher
Solution 2 Zoe stands with Ukraine
Solution 3 Rizlan Nawfal Tamima
Solution 4 DDKK