形成肌肉记忆是一件好事,大脑可以从低级工作中解放出来在潜意识思考高级问题。 锻炼肌肉记忆的一种方法就是将需要训练的内容整理出来,反复练习即可。

Python中的 datetime 模块已经相当熟练,几乎不再需要翻文档。 Golang 写得还不多, 同样整理一遍,多加练习。

如果想不起来,多翻翻文档,大多数情景在标准库里都可以解决。

package main

import (
    "fmt"
    "log"
    "time"
)

func main() {

    var err error

    // 1. 当前时间
    log.Println("===== 1. current time =====")

    // 1.1 获取当前时间 (带时区信息)
    now := time.Now()
    log.Printf("now: %s", now)

    // 1.2 获取当前时间戳 int64 (获取任何 Time 对象的时间戳)
    timestamp := time.Now().Unix()
    log.Printf("timestamp: %d", timestamp)

    // 1.3 获取当前时间戳纳秒 int64 (获取任何 Time 对象的时间戳)
    timestampNano := time.Now().UnixNano()
    log.Printf("timestampNano: %d", timestampNano)

    // 1.4 获取 UTC 时间 (获取任何 Time 对象对应的 UTC 时间)
    utc := time.Now().UTC()
    log.Printf("utc: %s", utc)

    // 2. 格式化
    // Format 的字符串不是 UNIX STRFTIME(3)
    // 而是 golang 自己定的规则,cheatsheet 请参考
    // https://yourbasic.org/golang/format-parse-string-time-date-example/

    // 特别说明:
    // - 24-小时格式时,无法指定不带 0 前缀的小时格式
    // - 表示半夜时间不可使用 24:00 只能带上日期一起使用
    // - 不支持润秒 23:59:60 , golang time 包假设没有润秒的公历

    // 格式化记忆方法,考虑以下表示法,更容易理解
    // 01/02 03:04:05PM '06 -0700
    // 表示 -7 时区 , 2006 年,(上午)3点4分5秒,1月2日
    // 那么想表达24小时制时,应该使用什么呢? 15:04:05

    // 完整格式速查
    // Type         Option
    // -------------------
    // Year         06 2006
    // Month        01 1 Jan January
    // Day          02 2 _2 (width two, right justified)
    // Weekday      Mon Monday
    // Hours        03 3 15
    // Minutes      04 4
    // Seconds      05 5
    // ms us ns     .000 .000000 .000000000
    // ms us ns     .999 .999999 .999999999 (remove trailing zero)
    // am/pm        PM pm
    // Timezone     MST
    // Offset       -0700 -07 -07:00 Z0700 Z07:00

    log.Println("")
    log.Println("")
    log.Println("===== 2. format (Time -> string) =====")

    // 2.1 最常见的格式
    log.Printf("%%Y-%%m-%%d %%H:%%M:%%S: %s",
        now.Format("2006-01-02 15:04:05"))

    // 2.2 RFC3389 格式
    log.Printf("RFC3389: %s", now.Format(time.RFC3339))

    // 2.3 ISO886 周
    _, w := now.ISOWeek()
    log.Printf("ISO8601 Week: %d", w)

    // 3. 从字符串得到对应的时间
    log.Println("")
    log.Println("")
    log.Println("===== 3. parse (string -> Time) =====")

    // 3.1 RFC3389 (带时区)
    var t2 time.Time
    rfc3339 := "2019-01-20T16:45:34+08:00"
    t2, err = time.Parse(time.RFC3339, rfc3339)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("\"%s\" => %s", rfc3339, t2)

    // 3.2 日期 没有时区 (需加上,否则默认得到 UTC 时间)
    date := "2019-07-11"
    input := fmt.Sprintf("%s+0800", date)
    t2, err = time.Parse("2006-01-02-0700", input)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("\"%s\" => %s", date, t2)

    // 3.3 日期时间 没有时区 (需加上,否则默认得到 UTC 时间)
    date2 := "2008-12-31 09:12:47"
    input2 := fmt.Sprintf("%s +0800", date2)
    t2, err = time.Parse("2006-01-02 15:04:05 -0700", input2)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("\"%s\" => %s", date2, t2)

    // 3.4 时间,有小数点  (需加上,否则默认得到 UTC 时间)
    date3 := "15:37:00.965376860"
    t2, err = time.Parse("15:04:05.000000000", date3)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("\"%s\" => %s", date3, t2)

    // 4. 从时间戳得到时间
    // 定义上时间戳本身是 UTC ,转换出来会根据系统配置设计设置时区
    log.Println("")
    log.Println("")
    log.Println("===== 4. parse (int timestamp -> Time) =====")
    i1 := int64(1562922019)
    t3 := time.Unix(i1, 0)
    log.Printf("%d => %v", i1, t3)

    // 5. 时间的比较
    log.Println("")
    log.Println("")
    log.Println("===== 5. compare  =====")

    // 需注意, == 操作会比较小数点以后的
    // Equal 操作则会考虑时区
    nt1 := time.Now()
    nt2 := time.Now()
    log.Printf("%v > %v ?: %t", nt1, nt2, nt1.After(nt2))
    log.Printf("%v == %v ?: %t", nt1, nt2, nt1.Equal(nt2))
    log.Printf("%v < %v ?: %t", nt1, nt2, nt1.Before(nt2))

    // 6. 时间计算
    log.Println("")
    log.Println("")
    log.Println("===== 5. calculate  =====")
    after := time.Now()

    // 6.1 差值 (都利用 Sub 方法) 返回 time.Duration
    d1 := now.Sub(after)
    log.Printf("%v = now - %v", d1, after)
    d2 := after.Sub(now)
    log.Printf("%v = %v - now", d2, after)

    // 6.2 加减操作 () 参数为 time.Duration
    second := 240
    nt3 := now.Add(time.Duration(second) * time.Second)
    log.Printf("now + %ds = %v", second, nt3)

    nt4 := now.Add(-time.Duration(second) * time.Second)
    log.Printf("now - %ds = %v", second, nt4)

    // 7. 时区操作
    // 时区定义请参考 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
    log.Println("")
    log.Println("")
    log.Println("===== 7. timezone  =====")

    var zoneName string
    var location *time.Location

    log.Printf("now location: \"%v\"", now.Location())

    // 7.1 加载地区信息
    zoneName = "US/Alaska"
    location, err = time.LoadLocation(zoneName)
    log.Printf("\"%s\" location: %v", zoneName, location)

    // 7.2 得到不同时区的时间
    log.Printf("now in \"%v\": %v", location, now.In(location))
    zoneName = "Asia/Tokyo"
    location, err = time.LoadLocation(zoneName)
    log.Printf("\"%s\" location: %v", zoneName, location)
    log.Printf("now in \"%v\": %v", location, now.In(location))
}