How to Use Golang Time Package

Category:
Tags:
Last Updated: 2022/01/05 09:46:59

Golang time package provides functionalities for handling time.


# Create a time.Time Variable

# time.Time for the Current Time

You can use time.Now to create a time.Time variable of the current Time.

import "time"

// Write redundantly to show the type
var t time.Time
t = time.Now()

fmt.Print(t) // 2022-01-31 13:45:56 +0000 UTC
1
2
3
4
5
6
7

# time.Time for a Specific Time

You can use time.Date to create a time.Time variable for a specific time.
The second argument comes a time.Month type variable, and you can also write as time.Month(1) to cast int into time.Month.


var t time.Time

// 2022-01-31 13:45:56.000000 +00:00
year := 2022
month := time.January // Jatuary - December
// month := time.Month(1) // cast int into time.Month
day := 31
hour := 13
min := 45
sec := 56
nanosec := 0
t = time.Date(year, month, day, hour, min, sec, nanosec, time.UTC)

fmt.Print(t) // 2022-01-31 13:45:56 +0000 UTC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# time.Time for a Specific TimeZone

The default timezone of time.Time type variables is UTC, so if you want to specify another timezone, you can use time.LoadLocation. In the case of Japan, usually Asia/Tokyo is used.


var t time.Time

// 2022-01-31 13:45:56.000000 +00:00
year := 2022
month := time.January // Jatuary - December
day := 31
hour := 13
min := 45
sec := 56
nanosec := 0
loc, _ := time.LoadLocation("Asia/Tokyo")
t = time.Date(year, month, day, hour, min, sec, nanosec, loc)

fmt.Print(t) // 2022-01-31 13:45:56 +0900 JST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Create time.Time with UnixTime

time.Unix can be used to create a time.Time variable with Unix Timestamp. The second argument is nanoseconds (0 - 999,999,999).

t = time.Unix(1640769840, 0)
fmt.Print(t) // 2021-12-29 18:24:00 +0900 JST
1
2

The time zone of this variable is the environment's local timezone. In my case, my PC's timezone is JST because I'm living in Japan.


# get Information from time.Time

# Year, Month, Day, Hours, Minutes, Seconds, Weekday, etc

You can get information like the following:


t := time.Now()

/*
Year(): 2022
Month(): 1 (string: January)
Day(): 3
Hour(): 15
Minute(): 11
Second(): 40
Weekday(): 1 (string: Monday)
*/
fmt.Printf("Year(): %d\n", t.Year() )
fmt.Printf("Month(): %d (.String(): %s)\n", t.Month(), t.Month().String() )
fmt.Printf("Day(): %d\n", t.Day() )
fmt.Printf("Hour(): %d\n", t.Hour() )
fmt.Printf("Minute(): %d\n", t.Minute() )
fmt.Printf("Second(): %d\n", t.Second() )
fmt.Printf("Weekday(): %d (.String(): %s)\n", t.Weekday(), t.Weekday().String() )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Returned values are basically int type , but Month() and Weekday() respectively return time.Month and time.Weekday type. They can be used as if they are int type , however , when they are casted into string or called String() method, they are casted into strings of the month or weekday names like "January" or "Sunday".


# Get Multiple Information

As I got it when I read the source code of AddDate (opens new window), you can get multiple information like this:

year, month, date := t.Date()
hour, min, sec := t.Clock()
nsec := t.Nanosecond()
1
2
3

# get TimeZone Information

You can use Time.Zone() to get the name and offset seconds of the timezone.

t := time.Now()
name, offset := t.Zone()

// name=JST offset=32400
fmt.Printf("name=%s offset=%d", name, offset)
1
2
3
4
5

# Time Culculations

# Add / Subtract Time

You can use Time.Add to add or subtract time. If you want to add or subtract days, months, or years, you can also use Time.AddDate.

t = time.Date(2022, time.Month(1), 3, 12, 23, 34,123, time.UTC)
fmt.Print(t,"\n") // 2022-01-03 12:23:34.000000123 +0000 UTC

// add 20 sec
t = t.Add(time.Duration(20) * time.Second) 
fmt.Print(t,"\n") // 2022-01-03 12:23:54.000000123 +0000 UTC
1
2
3
4
5
6

Time.Add can be also used to subtract time values by using it with negative values. (You may also know Time.Sub method, but Time.Sub is not the opposite of Time.Add. Time.Sub is used for culculating the distance between two time.Time variables.)

// sub 30 hours
t = t.Add(time.Duration(-30) * time.Hour) 
fmt.Print(t,"\n") // 2022-01-02 06:23:54.000000123 +0000 UTC
1
2
3

# Add / Subtract Days

As I wrote above, you can also use Time.AddDate to add or subtract days, months, or years.

t = time.Date(2022, time.Month(1), 3, 0, 0, 0, 0, time.UTC)
t = t.AddDate(1,1,1)
fmt.Print(t,"\n") // 2023-02-04 00:00:00 +0000 UTC
1
2
3

If you want to subtract days, you should use negative values.

t = time.Date(2022, time.Month(1), 3, 0, 0, 0, 0, time.UTC)
t = t.AddDate(-1,-2,-6)
fmt.Print(t,"\n") // 2020-10-28 00:00:00 +0000 UTC
1
2
3

# Add / Subtract Months with the last days

Sometimes Time.Add may return the different time from expected when it is used with the last days of months.

t = time.Date(2022, time.Month(1), 31, 0, 0, 0, 0, time.UTC)
t = t.AddDate(0,1,0)
fmt.Print(t,"\n") // 2022-03-03 00:00:00 +0000 UTC
1
2
3

Refering the source code of norm (a private method) (opens new window), values ​​outside the expected range seem to be normalized when time.Date instances are created. For example, "1/31 + 1 months" is once culculated to "2/31", and then finally it will be normalized into "3/3".

If you want to add or subtract months with truncating the last day of months, you should create an original function like this:


func main() {
	t := time.Date(2022, time.Month(1), 31, 0, 0, 0, 0, time.UTC)
	t = addMonth(t, 13)
	fmt.Print(t,"\n") // 2023-02-28 00:00:00 +0000 UTC
}

func addMonth(t time.Time, months int) time.Time {
	y, m_, d := t.Date()
	m := int(m_)

	if months > 0 {
		m += months
		if m > 12 {
			y_ := int((m - 1) / 12)

			m -= y_ * 12
			y += y_
		}
	} else if months < 0 {
		m -= months
		if m < 1 {
			y_ := int(-m / 12) + 1
			
			m += y_ * 12
			y -= y_
		}
	}
	last_d := getLastDay(y, m)
	if d > last_d {
		d = last_d
	}
	print(y,"\n",m,"\n", d, "\n")

	h, min, sec := t.Clock()

	return time.Date(y, time.Month(m), d, h, min, sec, t.Nanosecond(), t.Location())
}
// get the last day of the month
func getLastDay(y int, m int) int {
	if m == 4 || m == 6 || m == 9 || m == 11 {
		return 30
	}
	if m == 2 {
		if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) {
			return 29
		} else {
			return 28
		}
	}
	return 31
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# Culculate the Difference Between Two Times

You can use Time.Sub to culculate the difference between two times. It takes two arguments of time.Time variables and returns a time.Duration variable.

t1 := time.Date(2022, time.Month(1), 31, 12, 23, 34, 5678, time.UTC)
t2 := time.Date(2023, time.Month(2), 3, 23, 34, 56, 7890, time.UTC)

var duration time.Duration
duration = t2.Sub(t1)

// int(duration)=31835482000002212 
fmt.Printf("int(duration)=%d\n",int(duration))

// string(duration)=8843h11m22.000002212s
fmt.Printf("string(duration)=%s\n",duration.String())

// methods
/*
days: 368
hours: 8843
minutes: 530591
seconds: 31835482
milliseconds: 31835482111
microseconds: 31835482111111
nanoseconds: 31835482111111102
*/
fmt.Print("days: ", int(duration.Hours() / 24), "\n")
fmt.Print("hours: ", int(duration.Hours()), "\n")
fmt.Print("minutes: ", int(duration.Minutes()), "\n")
fmt.Print("seconds: ", int(duration.Seconds()), "\n")
fmt.Print("milliseconds: ", duration.Milliseconds(), "\n")
fmt.Print("microseconds: ", duration.Microseconds(), "\n")
fmt.Print("nanoseconds: ", duration.Nanoseconds(), "\n")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

When a time.Duration variables are cast into int, it will be nanoseconds, and its String() method returns a formatted string like "8843h11m22.000002212s".

You can extract information by using methods like Hours(), Minutes(), Seconds(), or Nanoseconds(). (Note that time.Duration method names are pluralized like Hours() while time.Time methods are in singular form like Hour())

Nanoseconds(), Microseconds(), and Milliseconds() methods return int64 type variables while Hours(), Minutes(), and Seconds() return float64 type variable, so if you don't need the fractional part, you should convert returned values into int type.

time.Duration doesn't have any method to culculate days, so you have to devide the returned values of Hours() by 24 to culculate it.


# Judgement of leap year

Time package doesn't provide a function to determine whether a year is a leap year or not, so you have to prepare your own function to do so.

You can judge whether a year is a leap year by checking the following conditions:

  • Leap years are always multiples of 4.
  • BUT multiples of 100 are not leap years.
  • BUT multiples of 400 are always leap years.

For example:

  • 2004, 2008, 2012 are leap years because all of them are multiples of 4.
  • 1900, 1800, 1700 are not leap years because they are multiples of 100 and not multiples of 400.
  • 2000, 1600, 1200 are leap years because they are multiples of 400.

The conditional statement of this would be like this:

func isLeapYear(t time.Time) bool {
	year := t.Year()
	return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
1
2
3
4

# Clone a time.Time variable

Like this:

year, month, day := t.Date()
hour, min, sec := t.Clock()
t = time.Date(year, month, day, hour, min, sec, t.Nanosecond(), t.Location())
1
2
3

You can also do it with 1 line code with AddDate(). Maybe it's simpler and easy to understand.

t = t.AddDate(0,0,0)
1

Category:
Tags:
Last Updated: 2022/01/05 09:46:59
Copyright © Web Ninja All Rights Reserved.