Computer scienceProgramming languagesGolangPackages and modulesStandard libraryTime package

Working with time

9 minutes read

Time is not a primitive type of variable. It's a complex value that contains information about various intervals of time: hours, minutes seconds, etc. In this topic, you will learn how the time variable works in Golang, how to compare time, and what to do if you want to change the time.

Time initialization

First of all, you need to know that time is a structured variable. The library time has a struct to represent time. It's called time.Time. This structure keeps time in nanoseconds, and it has a few methods that can help you in your projects. This topic focuses on how basic functions work, and if you'd like additional information, you can look at the official golang documentation.

You can use the similar syntax (like var or :=) to initialize a time.Time variable, as you do with primitive types. However, it has some interesting features: time.Time is a structure with private fields, which means that you can initialize it only with a default value. And the default is the first of January of the first year at 00:00 o'clock by UTC: 0001-01-01 00:00:00 +0000 UTC.

The correct way to create a variable with the time you need is to use the function time.Date(). It takes eight arguments and returns a struct of the time.Time type.

Let's see what arguments time.Date() takes:

  1. year

  2. month

  3. day

  4. hour

  5. minute

  6. second

  7. nanosecond

  8. location (timezone)

If you just want to get the current time, you don't need to write it yourself. Use the time.Now() function. It doesn't require any arguments and returns the time.Time structure as well:

package main

import (
    "fmt"
    "time"
)

func main() {
    //                    YEAR   MONTH    D  H  M  S  nS Location
    starWars := time.Date(1977, time.May, 4, 0, 0, 0, 0, time.UTC)

    fmt.Println(starWars)
    fmt.Println(starWars.Date())
    fmt.Println(time.Now())
}

// Output:
// 1977-05-04 00:00:00 +0000 UTC
// 1977 May 4
// 2022-04-05 15:51:03.410972 +0500 +05 m=+0.000249376

Pay attention to the time.Date() variable initialization. To represent month and location, you should use built-in constants and functions. If you want to get a month's name from its number, the function time.Month() will help you. For example, time.Month(10) will return time.October.

Time comparison

What if you wanted to compare time variables? You could try to use comparison operators like >, <, >=, <=, ==. None of them would work except for ==. The "equal" operator can compare two variables of the same type, but since we are talking about structures, it's not the correct operation.

Naturally, the time library contains features that can help you solve this problem. There are three functions: Before, After, and Equal. All of them are called by source time, take the compared time, and return a boolean value.

  • Before returns true if the original date has happened before the date it's compared with

  • After returns true if the original date has happened after the date it's compared with

  • Equal returns true if the original and compared dates are the same

package main

import (
    "fmt"
    "time"
)

func main() {
    darwin, _ := time.LoadLocation("Australia/Darwin")

    presentMcFly := time.Date(1985, time.October, 26, 1, 20, 0, 0, time.UTC)
    futureMcFly := time.Date(1955, time.November, 12, 22, 4, 0, 0, time.UTC)

    fmt.Println(presentMcFly.Before(futureMcFly))
    fmt.Println(presentMcFly.After(futureMcFly))
    fmt.Println(presentMcFly.Equal(futureMcFly))

    futureMcFlyInDarwin := time.Date(1955, time.November, 13, 7, 34, 0, 0, darwin)

    fmt.Println(futureMcFly.Equal(futureMcFlyInDarwin))
    fmt.Println(futureMcFly == futureMcFlyInDarwin)
}

// Output:
// false
// true
// false
// true
// false

As you see in the code above, the date stored in presentMcFly happened after the futureMcFly date. Hence, the expected output is:

  • Before called on presentMcFly will return false

  • After called on presentMcFly will return true

  • Equal called on presentMcFly will return false

The last two outputs in the code above show the difference between the == operator and the time.Equal() function. The variable futureMcFlyInDarwin sends McFly to Australia, to the timezone of Darwin. If you try to compare futureMcFlyInDarwin with futureMcFly the usual way (using ==), it will return false. On the other hand, the time.Equal() will return true, because the date and the time are the same, just in a different timezone.

Operations with time

Like usual comparison operators that don't work properly with time variables, the usual arithmetic operators (+ - / *) wouldn't work on them either. However, Golang has some methods to modify time variables. There are not a lot of them, though: only addition and subtraction.

Before continuing, we need to say a few words about the difference in time. In Golang, time difference is a specific type called Duration, which stores value in nanoseconds (just like time.Time). However, nanoseconds are not an easy-to-use feature if you need to set another value. Thus, for convenience, Golang has constants equivalent to the most-used time intervals: nanosecond, microsecond, millisecond, second, minute, and hour.

package main

import (
    "fmt"
    "time"
)

func main() {
    twoSecond := time.Millisecond*1000 + time.Second
    sevenDays := time.Hour * 24 * 7
    oneYear := time.Hour * 24 * 365
    threeYearsAndTwoMonths := oneYear*3 + time.Hour*24*30

    fmt.Println(twoSecond)
    fmt.Println(sevenDays)
    fmt.Println(oneYear)
    fmt.Println(threeYearsAndTwoMonths)
}

// Output:
// 2s
// 168h0m0s
// 8760h0m0s
// 27000h0m0s

Pay attention to the output: Duration has a formatted output, but it uses an integer value with all other operations. If you want to get 10 seconds, for example, just take the time.Second constant and multiply it by ten.

Let's return now to the addition and subtraction of time. Corresponding features are called Add and Sub. Both functions are called on the source variable, but:

  • Add returns a new time.Time value that differs from the original value by the given interval;

  • Sub takes the time.Time variable and returns the difference between it and the second provided value as a Duration type.

package main

import (
    "fmt"
    "time"
)

func main() {
    CopernicusBDay := time.Date(1473, time.February, 19, 0, 0, 0, 0, time.UTC)
    NewtonBDay := CopernicusBDay.Add(time.Hour * 1489080)
    EinsteinBDay := time.Date(1879, time.March, 14, 0, 0, 0, 0, time.UTC)

    fmt.Println("Timeline:")
    fmt.Printf("Nicolaus Copernicus: %v\n", CopernicusBDay)
    fmt.Printf(" | %v\n", NewtonBDay.Sub(CopernicusBDay))
    fmt.Printf("Isaac Newton: %v\n", NewtonBDay)
    fmt.Printf(" | %v\n", EinsteinBDay.Sub(NewtonBDay))
    fmt.Printf("Albert Einstein: %v\n", EinsteinBDay)
}

// Output:
// Timeline:
// Nicolaus Copernicus: 1473-02-19 00:00:00 +0000 UTC
//  | 1489080h0m0s
// Isaac Newton: 1643-01-04 00:00:00 +0000 UTC
//  | 2070384h0m0s
// Albert Einstein: 1879-03-14 00:00:00 +0000 UTC

Conclusion

The main thing about time.Time is that it's not a simple variable type. It's a complex structure that contains a lot of information about time. To work with it, you need to know certain special features:

  • to initialize the time variable, use the time.Date or time.Now functions;

  • the structure time.Time has its own comparison operations (Before, After, and Equal);

  • the structure time.Time supports two modifying operations addition and subtraction (Add and Sub, respectively);

  • time.Time has a set of constants for time intervals, names of months, and timezones.

24 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo