17.时间处理

17.时间处理

    日期和时间是日常编程常用的功能之一。如果没有日期和时间,会导致很多功能无法实现,例如日志记录、定时任务、时间延迟等。Go标准库提供了操作日期和时间的方法。

在提到时间,还需要注意不同地区的时间会不一样,所以这里还需要考虑到不同时区不同历法等带来的影响。目前使用较多的两种时间GMT(格林威治时间:Greenwich Mean Time)和UTC(世界协调时间:Universal Time Coodinated)。一般默认时间为当前时区所对应的时间,如果对精度没有太大要求,时间精度到秒即可。

    在Go语言中,日期与时间可以分为三种表示方式时间戳结构体时间字符串时间,这三者之间的数据类型可以相互转换。

时间戳是以GMT时间1970年1月1日00时00分00秒为基准时间(北京时间1970年1月1日80时00分00秒)到当前时间为止,已经经过的秒数。

17.1 时间戳

    Go标准库提供了UnixUnixMicroUnixMilliUnixNano生成的秒级、微秒级、毫秒级和纳秒级时间戳。示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取当前时间
    now := time.Now()
    // 获取秒级时间戳
    t1 := now.Unix()
    // 获取微秒级时间戳
    t2 := now.UnixMicro()
    // 获取毫秒级时间戳
    t3 := now.UnixMilli()
    // 获取纳秒级时间戳
    t4 := now.UnixNano()
    fmt.Printf("时间戳秒级: %v 微秒级:%+v 毫秒级:%+v 纳秒级:%+v\n", t1, t2, t3, t4)
}

    代码运行结果如下所示:

时间戳秒级: 1735398611 微秒级:1735398611946770 毫秒级:1735398611946 纳秒级:1735398611946770100

17.2 结构体时间

    结构体Time是Go语言对时间的具体表现,它将时间以结构体Time表示,并由结构体实例化对象调用结构体方法或结构体成员获取时间信息。示例如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取当前时间
    now := time.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.Weekday())
}

    代码运行结果如下所示:

当前时间-年:2024
当前时间-月:December
当前时间-天:28
当前时间-时:23
当前时间-分:22
当前时间-秒:32
当前时间-星期:Saturday

17.3 字符串时间

    在Go语言中并没有采用%Y%m%d %H:%M:%S这样的格式化字符串。而是采用了这种时间2006-01-02 03:04:05 -0700 pm1月2日下午3点4分5秒06年西7区,转换为北京时间为2006-01-02 15:04:05 +0800

上面的时间字符串比较难记,可以尝试记住这个字符串010203040506pm-07,也可以使用从1数到7来方便记住。

    也可以查看官方文档(https://golang.google.cn/pkg/time/),如下所示:

Year: "2006" "06"
Month: "Jan" "January" "01" "1"
Day of the week: "Mon" "Monday"
Day of the month: "2" "_2" "02"
Day of the year: "__2" "002"
Hour: "15" "3" "03" (PM or AM)
Minute: "4" "04"
Second: "5" "05"
AM/PM mark: "PM"

"-0700"     ±hhmm
"-07:00"    ±hh:mm
"-07"       ±hh
"-070000"   ±hhmmss
"-07:00:00" ±hh:mm:ss


"Z0700"      Z or ±hhmm
"Z07:00"     Z or ±hh:mm
"Z07"        Z or ±hh
"Z070000"    Z or ±hhmmss
"Z07:00:00"  Z or ±hh:mm:ss

    示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取当前时间
    now := time.Now()
    // 带时区
    t1 := now.Format("2006-01-02 15:04:05 +0800")
    fmt.Printf("带时区时间(24小时制): %v\n", t1)
    // 不带时区
    t2 := now.Format("2006-01-02 15:04:05 ")
    fmt.Printf("不 带 时  区  时 间: %v\n", t2)

    // 12小时制
    t3 := now.Format("2006-01-02 03:04:05 pm +0800 ")
    fmt.Printf("带时区时间(12小时制): %v\n", t3)

    //显示格式 为2006/1/2
    t4 := now.Format("2006/01/02 15:04 +0800")
    fmt.Printf("带时区时间(24小时制): %v\n", t4)

    // 显示时/分/秒
    t5 := now.Format("15/04/05 +0800")
    fmt.Printf("带时区时间(24小时制): %v\n", t5)

    // 显示毫秒
    t6 := now.Format("2006-01-02 15:04:05.000 +0800")
    fmt.Printf("带时区时间(24小时制): %v\n", t6)
}

    代码运行结果如下所示:

带时区时间(24小时制): 2024-12-28 23:47:46 +0800
不 带 时  区  时 间: 2024-12-28 23:47:46 
带时区时间(12小时制): 2024-12-28 11:47:46 pm +0800 
带时区时间(24小时制): 2024/12/28 23:47 +0800
带时区时间(24小时制): 23/47/46 +0800
带时区时间(24小时制): 2024-12-28 23:47:46.816 +0800

17.4 时间解析

    这里的时间解析是指将字符串的时间按指定格式解析为对应的时间格式,示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 定义字符串格式时间
    timeStr := "2024-12-28 23:55:58 +0800"
    // 时间格式字符串
    formatStr := "2006-01-02 15:04:05 -0700"

    if t, err := time.Parse(formatStr, timeStr); err != nil {
        fmt.Printf("带时区解析时间字符串:%+v出错 %v\n", timeStr, err)
    } else {
        fmt.Printf("带时区解析时间字符串:%+v成功,返回时间: %v\n", timeStr, t)
    }

    // 不带时区
    // 如果没有时区,则表示UTC零时区时间
    // 在没有时区时,这样转换可能会导致时间出现较大误差
    timeStrNoZone := "2024-12-28 23:55:58"
    formatStrNoZone := "2006-01-02 15:04:05"
    if t, err := time.Parse(formatStrNoZone, timeStrNoZone); err != nil {
        fmt.Printf("不带时区解析时间字符串:%+v出错 %v\n", timeStrNoZone, err)
    } else {
        fmt.Printf("不带时区解析时间字符串:%+v成功,返回时间: %v\n", timeStrNoZone, t)
    }

    // 针对以上可能出现的时间误差,我们采取另一种办法
    if tz, err := time.LoadLocation("Asia/Shanghai"); err != nil {
        fmt.Printf("设置时区出错: %v\n", err)
    } else {
        if t, err := time.ParseInLocation(formatStrNoZone, timeStrNoZone, tz); err != nil {
            fmt.Printf("不带时区,使用指定时区解析时间字符串:%+v出错 %v\n", timeStrNoZone, err)
        } else {
            fmt.Printf("不带时区,使用指定时区解析时间字符串:%+v成功,返回时间: %v\n", timeStrNoZone, t)
        }
    }
}

    代码运行结果如下所示:

带时区解析时间字符串:2024-12-28 23:55:58 +0800成功,返回时间: 2024-12-28 23:55:58 +0800 CST
不带时区解析时间字符串:2024-12-28 23:55:58成功,返回时间: 2024-12-28 23:55:58 +0000 UTC
不带时区,使用指定时区解析时间字符串:2024-12-28 23:55:58成功,返回时间: 2024-12-28 23:55:58 +0800 CST

17.5 时间类型相互转换

    在Go语言中,类型可以分为时间戳结构体时间字符串时间等,这三者之间可以通过特定方法实现相互转换,示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 定义字符串格式
    timeFormat := "2006-01-02 15:04:05 -0700"
    // 定义时间戳
    var timestamp int64 = 1735561940
    // 将时间戳转换为结构体Time
    tm := time.Unix(timestamp, 0)
    fmt.Printf("时间戳转换为结构体时间:%+v,数据类型为:%[1]T\n", tm)

    // 将时间戳转换为时间字符串格式化
    tmStr := time.Unix(timestamp, 0).Format(timeFormat)
    fmt.Printf("时间戳转换为字符串格式化时间:%+v,数据类型为:%[1]T\n", tmStr)

    // 结构体时间
    curTime := time.Now()
    // 转换为时间戳
    fmt.Printf("结构体时间转换为时间戳:%+v,数据类型为:%[1]T\n", curTime.Unix())
    // 时间戳转换为字符串
    fmt.Printf("结构体时间转换为字符串时间:%+v,数据类型为:%[1]T\n", curTime.Format(timeFormat))

    // 字符串时间
    strTime := "2024-12-30 12:44:12 +0800"
    // 将字符串时间转换为结构体时间
    if timeStruct, err := time.Parse(timeFormat, strTime); err != nil {
        fmt.Printf("字符串时间:%+v转换为结构体时间出错: %v\n", strTime, err)
    } else {
        fmt.Printf("字符串时间:%+v转换为结构体时间成功:%+v,数据类型为%[2]T\n", strTime, timeStruct)
    }

    timeFormat2 := "2006-01-02 15:04:05"
    strTime2 := "2024-12-30 20:54:08"
    if timeStruct2, err := time.ParseInLocation(timeFormat2, strTime2, time.Local); err != nil {
        fmt.Printf("字符串时间:%+v转换为结构体时间出错: %v\n", strTime2, err)
    } else {
        fmt.Printf("字符串时间:%+v转换为结构体时间成功:%+v,数据类型为%[2]T\n", strTime2, timeStruct2)
        // 将字符串时间转换为时间戳
        fmt.Printf("字符串时间%+v转换为时间戳%+v,数据格式为%[2]T", strTime2, timeStruct2.Unix())
    }
}

    代码运行结果如下所示:

时间戳转换为结构体时间:2024-12-30 20:32:20 +0800 CST,数据类型为:time.Time
时间戳转换为字符串格式化时间:2024-12-30 20:32:20 +0800,数据类型为:string
结构体时间转换为时间戳:1735563632,数据类型为:int64
结构体时间转换为字符串时间:2024-12-30 21:00:32 +0800,数据类型为:string
字符串时间:2024-12-30 12:44:12 +0800转换为结构体时间成功:2024-12-30 12:44:12 +0800 CST,数据类型为time.Time
字符串时间:2024-12-30 20:54:08转换为结构体时间成功:2024-12-30 20:54:08 +0800 CST,数据类型为time.Time
字符串时间2024-12-30 20:54:08转换为时间戳1735563248,数据格式为int64

    根据运行结果,可以得出结论时间戳、结构体时间、字符串时间三者相互转换进,需要以结构体时间做为中转,通过结构体时间,可以获取更多的时间属性。

17.6 时间运算

    时间运算是对两个时间或时间增量进行加减运算和对比等操作。如下所示:

  • 计算时间差是两个结构体时间实例对象进行加减计算,可以计算出两个时间相差的时、分、秒等等
  • 时间增量计算是将某个时间增加或减少一段时间,例如增加或减少多少年、月、日、时、分、秒等等
  • 时间信息对比,主要是判断两个时间是否相同或判断两个时间的先后顺序等

    根据以下说明可以总结如下:

时间差/时间增量 = 时间1 - 时间2
时间= 时间 +/- 时间增量

注意事项:两个时间直接相加是错误,没有这个概念

    示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

func main() {
    tz := time.Local
    timeFormat := "2006-01-02 15:04:05"

    st1 := "2024-12-30 20:55:12"
    st2 := "2024-12-30 21:25:42"

    t1, _ := time.ParseInLocation(timeFormat, st1, tz)
    t2, _ := time.ParseInLocation(timeFormat, st2, tz)

    fmt.Printf("解析后的时间分别为:t1 -  t2: %+v - %+v\n", t1, t2)

    // 计算时间差
    diff := t2.Sub(t1)
    fmt.Printf("%+v 和 %+v的时间差为: %v,数据类型为:%[3]T,相差分钟数为%+v,秒为:%+v\n", t2, t2, diff, diff.Minutes(), diff.Seconds())

    // 计算时间增量
    // 默认为 3 纳秒
    // time.Duration(3) 相当于强制类型转换,即将 3 强制转换为 Duration,而Duration 为int64
    /*
        type Duration int64
        const (
            Nanosecond  Duration = 1
            Microsecond          = 1000 * Nanosecond
            Millisecond          = 1000 * Microsecond
            Second               = 1000 * Millisecond
            Minute               = 60 * Second
            Hour                 = 60 * Minute
        )
    */
    ns3 := time.Duration(3)
    s3 := time.Duration(3 * time.Second)
    h3 := time.Duration(3 * time.Hour)
    fmt.Printf("ns3: %v s3: %+v h3: %+v\n", ns3, s3, h3)

    // 按时间增量
    t3 := t1.Add(s3)
    t4 := t1.Add(-s3)
    fmt.Printf("t1: %+v t3: %+v t4: %+v\n", t1, t3, t4)

    // 按日期增量
    t5 := t1.AddDate(1, 1, 1)
    fmt.Printf("t1: %v按日期增量之后的结果t5: %v\n", t1, t5)

    // 对比时间
    fmt.Printf("t2 %v 在时间 %v 之后吗?%v\n", t2, t1, t2.After(t1))
    fmt.Printf("t2 %v 在时间 %v 之前吗?%v\n", t2, t1, t2.Before(t1))
    fmt.Printf("t2 %v 和 %v 相等吗?%v\n", t2, t1, t2.Equal(t1))
}

    代码运行结果如下所示:

解析后的时间分别为:t1 -  t2: 2024-12-30 20:55:12 +0800 CST - 2024-12-30 21:25:42 +0800 CST
2024-12-30 21:25:42 +0800 CST 和 2024-12-30 21:25:42 +0800 CST的时间差为: 30m30s,数据类型为:time.Duration,相差分钟数为30.5,秒为:1830
ns3: 3ns s3: 3s h3: 3h0m0s
t1: 2024-12-30 20:55:12 +0800 CST t3: 2024-12-30 20:55:15 +0800 CST t4: 2024-12-30 20:55:09 +0800 CST
t1: 2024-12-30 20:55:12 +0800 CST按日期增量之后的结果t5: 2026-01-31 20:55:12 +0800 CST
t2 2024-12-30 21:25:42 +0800 CST 在时间 2024-12-30 20:55:12 +0800 CST 之后吗?true
t2 2024-12-30 21:25:42 +0800 CST 在时间 2024-12-30 20:55:12 +0800 CST 之前吗?false
t2 2024-12-30 21:25:42 +0800 CST 和 2024-12-30 20:55:12 +0800 CST 相等吗?false

17.7 时间延迟

    时间延迟是程序在执行过程中进入睡眠状态暂停执行,在睡眠状态结束之后,再继续执行。在Go使用time.Sleep方法实现,可以实现级别的延时功能。示例代码如下所示:

package main

import (
    "fmt"
    "time"
)

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

    // 延迟时间
    sleepTime := time.Duration(1 * time.Second)
    time.Sleep(sleepTime)
    fmt.Printf("当前时间: %v\n", time.Now().Format(timeFormat))

    sleepTime = time.Duration(1 * time.Minute)
    time.Sleep(sleepTime)
    fmt.Printf("当前时间: %v\n", time.Now().Format(timeFormat))
}

    代码运行结果如下所示:

当前时间: 2024-12-30 22:41:14 +0800
当前时间: 2024-12-30 22:41:15 +0800
当前时间: 2024-12-30 22:42:15 +0800
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 文章图片上传不正常,如需文档,可联系微信:1017429387 目录 1 安装... 4 1.1 配置探针... ...
    Mrhappy_a7eb阅读 11,652评论 0 5
  • 在Python中,日期和时间的应用非常普遍。在实际应用中,大部分数据的记录和日志的处理都需要使用时间。这里将介绍P...
    泷汰泱阅读 4,549评论 0 0
  • 1、time模块 time模块中时间的表现格式有三种: timestamp:时间戳,表示的是从1970年1月1日0...
    老张_Jason阅读 4,082评论 0 0
  • 在工程化的Go语言开发项目中,Go语言的源码复用是建立在包(package)基础之上的。本文介绍了Go语言中如何定...
    雪上霜阅读 1,748评论 0 0
  • 为了让您了解实际情况,我使用Apache Beam代码片段,并结合延时图来提供可视化的表示。Apache Beam...
    瑞_xlows阅读 4,328评论 0 0