目录

Go 日期时间

Go 中的日期时间使用布局来格式化,通过布局来完成日期时间的格式化和解析。

当前日期时间

标准日期时间格式

布局格式:

LayoutNote
January 2, 2006Date
01/02/06Date
Jan-02-06Date
15:04:05Time
3:04:05 PMTime
Jan _2 15:04:05Timestamp
Jan _2 15:04:05.000000With microseconds
2006-01-02T15:04:05-0700ISO 8601(RFC 3339)
2006-01-02ISO 8601(RFC 3339)
15:04:05ISO 8601(RFC 3339)
02 Jan 06 15:04 MSTRFC 822
02 Jan 06 15:04 -0700With numeric zone
Mon, 02 Jan 2006 15:04:05 MSTRFC 1123
Mon, 02 Jan 2006 15:04:05 -0700With numeric zone

布局选项:

TypeOptions
Year06 2006
Month01 1 Jan January
Day02 2 _2(width two, right justified)
WeekdayMon Monday
Hours03 3 15
Minutes04 4
Seconds05 5
ms us ns.000 .000000 .000000000
ms us ns.999 .999999 .999999999(trailing zeros removed)
am/pmAM am PM pm
TimezoneMST
Offset-0700 -07 -07:00 Z0700 Z07:00

以下是 Go 源码中预定义日期和时间格式常量:

 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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// These are predefined layouts for use in Time.Format and time.Parse.
// The reference time used in the layouts is the specific time:
//	Mon Jan 2 15:04:05 MST 2006
// which is Unix time 1136239445. Since MST is GMT-0700,
// the reference time can be thought of as
//	01/02 03:04:05PM '06 -0700
// To define your own format, write down what the reference time would look
// like formatted your way; see the values of constants like ANSIC,
// StampMicro or Kitchen for examples. The model is to demonstrate what the
// reference time looks like so that the Format and Parse methods can apply
// the same transformation to a general time value.
//
// Some valid layouts are invalid time values for time.Parse, due to formats
// such as _ for space padding and Z for zone information.
//
// Within the format string, an underscore _ represents a space that may be
// replaced by a digit if the following number (a day) has two digits; for
// compatibility with fixed-width Unix time formats.
//
// A decimal point followed by one or more zeros represents a fractional
// second, printed to the given number of decimal places. A decimal point
// followed by one or more nines represents a fractional second, printed to
// the given number of decimal places, with trailing zeros removed.
// When parsing (only), the input may contain a fractional second
// field immediately after the seconds field, even if the layout does not
// signify its presence. In that case a decimal point followed by a maximal
// series of digits is parsed as a fractional second.
//
// Numeric time zone offsets format as follows:
//	-0700  ±hhmm
//	-07:00 ±hh:mm
//	-07    ±hh
// Replacing the sign in the format with a Z triggers
// the ISO 8601 behavior of printing Z instead of an
// offset for the UTC zone. Thus:
//	Z0700  Z or ±hhmm
//	Z07:00 Z or ±hh:mm
//	Z07    Z or ±hh
//
// The recognized day of week formats are "Mon" and "Monday".
// The recognized month formats are "Jan" and "January".
//
// The formats 2, _2, and 02 are unpadded, space-padded, and zero-padded
// day of month. The formats __2 and 002 are space-padded and zero-padded
// three-character day of year; there is no unpadded day of year format.
//
// Text in the format string that is not recognized as part of the reference
// time is echoed verbatim during Format and expected to appear verbatim
// in the input to Parse.
//
// The executable example for Time.Format demonstrates the working
// of the layout string in detail and is a good reference.
//
// Note that the RFC822, RFC850, and RFC1123 formats should be applied
// only to local times. Applying them to UTC times will use "UTC" as the
// time zone abbreviation, while strictly speaking those RFCs require the
// use of "GMT" in that case.
// In general RFC1123Z should be used instead of RFC1123 for servers
// that insist on that format, and RFC3339 should be preferred for new protocols.
// RFC3339, RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting;
// when used with time.Parse they do not accept all the time formats
// permitted by the RFCs and they do accept time formats not formally defined.
// The RFC3339Nano format removes trailing zeros from the seconds field
// and thus may not sort correctly once formatted.
const (
  ANSIC       = "Mon Jan _2 15:04:05 2006"
  UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
  RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
  RFC822      = "02 Jan 06 15:04 MST"
  RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
  RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
  RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
  RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
  RFC3339     = "2006-01-02T15:04:05Z07:00"
  RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
  Kitchen     = "3:04PM"
  // Handy time stamps.
  Stamp      = "Jan _2 15:04:05"
  StampMilli = "Jan _2 15:04:05.000"
  StampMicro = "Jan _2 15:04:05.000000"
  StampNano  = "Jan _2 15:04:05.000000000"
)

获取当前日期时间

 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
package main

import (
  "fmt"
  "time"
)

func main() {
  now := time.Now()
  fmt.Printf("当前日期时间:%v\n", now)
  fmt.Printf("年:%v\n", now.Year())
  fmt.Printf("月:%v\n", now.Month())
  fmt.Printf("日:%v\n", now.Day())
  fmt.Printf("时:%v\n", now.Hour())
  fmt.Printf("分:%v\n", now.Minute())
  fmt.Printf("秒:%v\n", now.Second())
  fmt.Printf("纳秒:%v\n", now.Nanosecond())
  fmt.Printf("\n")
}

/*
当前日期时间2021-07-11 11:12:59.787921 +0800 CST m=+0.000104353
2021
July
11
11
12
59
纳秒787921000
/*

获取当前时间戳

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

import (
  "fmt"
  "time"
)

func main() {
	now := time.Now()
	fmt.Printf("当前时间戳(单位为秒):%v\n", now.Unix())
	fmt.Printf("当前时间戳(单位为纳秒):%v\n", now.UnixNano())
}

/*
当前时间戳(单位为秒):1625973361
当前时间戳(单位为纳秒):1625973361766745000
*/

获取当前时间的字符串格式

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

import (
  "fmt"
  "time"
)

func main() {
  now := time.Now()
  fmt.Printf("当前日期时间:%v\n", now.Format(time.RFC3339))
  fmt.Printf("当前日期时间:%v\n", now.Format("2006-01-02 15:04:05"))
}

/*
当前日期时间:2021-07-11T11:42:19+08:00
当前日期时间:2021-07-11 11:42:19
*/

指定时区获取日期时间

使用 FixedZone 指定

 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
package main

import (
  "testing"
  "time"
)

const TheCSTLayout = "2006-01-02T15:04:05"

var theCSTZone *time.Location

func init() {
  theCSTZone = time.FixedZone("CST", 8*3600)
}

func TestNowDateTime(t *testing.T) {
  dateTime := time.Now().In(theCSTZone).Format(TheCSTLayout)
  t.Logf("The date time: %v", dateTime)
  now, _ := time.ParseInLocation(TheCSTLayout, dateTime, theCSTZone)
  t.Logf("Now date time: %v", now)
}

/*
$ go test -v -timeout 30s -run ^TestNowDateTime$ example.org/hello/v2
=== RUN   TestNowDateTime
    main_test.go:18: The date time: 2021-09-25T09:02:03
    main_test.go:20: Now date time: 2021-09-25 09:02:03 +0800 CST
--- PASS: TestNowDateTime (0.00s)
PASS
ok      example.org/hello/v2    0.234s
*/

使用 LoadLocation 指定

 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
package main

import (
  "log"
  "testing"
  "time"
)

const TheCSTLayout = "2006-01-02T15:04:05"

var theCSTLocation *time.Location

func init() {
  var err error
  theCSTLocation, err = time.LoadLocation("Asia/Shanghai")
  if err != nil {
    log.Fatalf("Load location failed, %v", err)
  }
}

func TestNowDateTime(t *testing.T) {
  dateTime := time.Now().In(theCSTLocation).Format(TheCSTLayout)
  t.Logf("The date time: %v", dateTime)
  now, _ := time.ParseInLocation(TheCSTLayout, dateTime, theCSTLocation)
  t.Logf("Now date time: %v", now)
}

/*
$ go test -v -timeout 30s -run ^TestNowDateTime$ example.org/hello/v2
=== RUN   TestNowDateTime
    main_test.go:23: The date time: 2021-09-25T09:39:02
    main_test.go:25: Now date time: 2021-09-25 09:39:02 +0800 CST
--- PASS: TestNowDateTime (0.00s)
PASS
ok      example.org/hello/v2    1.777s
*/

常用日期时间转换

时间戳转日期时间字符串

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
  "fmt"
  "strconv"
  "time"
)

func main() {
  layout := "2006-01-02 15:04:05"
  now := time.Now().Unix()
  fmt.Printf("转换后的日期时间:%v\n", time.Unix(now, 0).Format(layout))

  a, b := 1626005415, "1626005415"
  c, _ := strconv.ParseInt(b, 10, 64)
  fmt.Printf("转换后的日期时间:%v\n", time.Unix(int64(a), 0).Format(layout))
  fmt.Printf("转换后的日期时间:%v\n", time.Unix(c, 0).Format(layout))
}

/*
转换后的日期时间:2021-07-11 20:35:31
转换后的日期时间:2021-07-11 20:10:15
转换后的日期时间:2021-07-11 20:10:15
*/

日期时间字符串转时间戳

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

import (
  "fmt"
  "time"
)

const (
  ChinaFormat = "2006-01-02 15:04:05"
)

func main() {
  a, b := "2021-07-11 20:27:33", "Sun, 11 Jul 2021 15:04:05 UTC"
  at1, _ := time.Parse(ChinaFormat, a)
  at2, _ := time.Parse(time.RFC1123, b)
  fmt.Printf("转换后的时间戳:%v\n", at1.Unix())
  fmt.Printf("转换后的时间戳:%v\n", at2.Unix())
}

/*
转换后的时间戳:1626035253
转换后的时间戳:1626015845
*/

日期时间字符串转时间对象

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

import (
  "fmt"
  "time"
)

func main() {
  str := "2008-08-08 08:08:08"

  // Parse the date string into Go's time object
  at, _ := time.Parse("2006-01-02 15:04:05", str)
  fmt.Printf("转换后的日期时间:%v\n", at)
  fmt.Println(at.Date())
}

/*
转换后的日期时间:2008-08-08 08:08:08 +0000 UTC
2008 August 8
*/

常用日期时间计算

计算两个给定日期之前相差的天数

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

import (
  "fmt"
  "time"
)

func main() {
  t1, t2 := Date(2020, 1, 1), Date(2021, 1, 1)
  days := t2.Sub(t1).Hours() / 24
  fmt.Printf("两个给定日期之前相差的天数为:%v\n", days)
}

func Date(year, month, day int) time.Time {
  return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}

/*
两个给定日期之前相差的天数为:366
*/

统计代码运行时间

 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
package main

import (
  "fmt"
  "time"
)

func main() {
  fn := TimeSpent(execSlowFunc)
  fmt.Printf("Exec result: %v\n", fn(9999))
}

func execSlowFunc(x int) int {
  time.Sleep(3 * time.Second)
  return x + 1
}

func TimeSpent(fn func(x int) int) func(n int) int {
  return func(x int) int {
    start := time.Now()
    ret := fn(x)
    fmt.Println("Time spent:", time.Since(start).Seconds())
    return ret
  }
}

/*
Time spent: 3.004356442
Exec result: 10000
*/
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
  "log"
  "time"
)

func main() {
  defer TimeSpent(execSlowFunc("Hello World!"))
}

func execSlowFunc(s string) (string, time.Time) {
  time.Sleep(1 * time.Second)
  return s + " 🌏🌈💫🔥🎉🚀🙏", time.Now()
}

func TimeSpent(s string, start time.Time) {
  log.Printf("%v: %v\n", s, time.Since(start))
}

/*
2021/07/14 08:22:38 Hello World! 🌏🌈💫🔥🎉🚀🙏: 4.582µs
*/

日期时间比较

 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
package test

import (
	"testing"
	"time"
)

func TestCompareTime(t *testing.T) {
	at := time.Now()
	t1, t2 := "2023-01-31", "2099-01-31"
	v1, _ := time.ParseInLocation("2006-01-02", t1, time.Local)
	v2, _ := time.ParseInLocation("2006-01-02", t2, time.Local)

	if at.Before(v1) && at.After(v2) {
		t.Errorf("%s is between %s and %s", at, v1, v2)
	}

	t3, t4 := "2099-01-01 12:00:00", "2099-01-31 12:00:00"
	v3, _ := time.ParseInLocation("2006-01-02 15:04:05", t3, time.Local)
	v4, _ := time.ParseInLocation("2006-01-02 15:04:05", t4, time.Local)
	if at.Before(v4) && at.After(v3) {
		t.Errorf("%s is between %s and %s", at, v3, v4)
	}

	t5, t6 := "2099-01-01 12:00:00", "2099-01-01 12:00:00"
	v5, _ := time.ParseInLocation("2006-01-02 15:04:05", t5, time.Local)
	v6, _ := time.ParseInLocation("2006-01-02 15:04:05", t6, time.Local)
	if !v5.Equal(v6) {
		t.Errorf("%s is equal to %s", v5, v6)
	}
}
1
2
3
4
5
❯ go test -timeout 30s -run '^TestCompareTime$' example.com/m/test -v
=== RUN   TestCompareTime
--- PASS: TestCompareTime (0.00s)
PASS
ok      example.com/m/test      0.010s