基础数据类型转换
在 Go 语言中,数据类型转换需要显式操作,主要分为以下几种情况:
数值类型互转
var i int = 42
f := float64(i)
u := uint(i)
f2 := 3.14 i2 := int(f2)
|
字符串与基本类型互转
使用 strconv
包:
import "strconv"
s := strconv.Itoa(65) s = strconv.FormatInt(123, 10)
num, err := strconv.Atoi("65") num2, err := strconv.ParseInt("FF", 16, 64)
s := strconv.FormatFloat(3.1415, 'f', 2, 64)
f, err := strconv.ParseFloat("3.14", 64)
s := strconv.FormatBool(true)
b, err := strconv.ParseBool("true")
|
字节切片([]byte)与字符串
data := []byte("Hello")
data1 := []byte("你好")
str := string(data)
|
符文切片([]rune)与字符串
runes := []rune("你好") runes1 := []rune("Hello")
str := string(runes)
|
接口类型断言
var val interface{} = "hello"
s, ok := val.(string)
s2 := val.(string)
|
自定义类型转换
type Celsius float64 type Fahrenheit float64
c := Celsius(100) f := Fahrenheit(c*9/5 + 32)
var f2 float64 = float64(c)
|
JSON 转换
import "encoding/json"
type Person struct { Name string `json:"name"` Age int `json:"age"` }
p := Person{"Alice", 30} jsonData, _ := json.Marshal(p)
var p2 Person json.Unmarshal(jsonData, &p2)
|
重要注意事项
精度损失:大类型转小类型时可能丢失数据
var big int64 = 1<<33 + 1 small := int32(big)
|
非法转换:编译器会阻止不兼容转换
类型断言要求:接口转换必须实际存储目标类型
字符串转换性能:
str := "hello" unsafeBytes := *(*[]byte)(unsafe.Pointer(&str))
|
建议:
- 数值转换注意范围检查
- 优先使用
strconv
而非 fmt.Sprintf
- 大切片转换考虑
unsafe
(需谨慎)
- 使用
, ok
格式避免断言 panic
通过显式类型转换和标准库函数,Go 在保持类型安全的同时提供了灵活的类型转换机制。
字符串与日期时间的互相转换
在 Go 语言中,时间处理主要依赖于标准库中的 time
包。下面我将详细解释字符串与时间对象之间的转换方法,并提供一个完整的可视化示例。
核心转换方法
时间对象转字符串
使用 time.Format(layout string)
方法,其中 layout
必须是 Go 的参考时间(2006-01-02 15:04:05)
t := time.Now() str := t.Format("2006-01-02 15:04:05")
|
字符串转时间对象
使用 time.Parse(layout, value string)
或 time.ParseInLocation()
layout := "2006-01-02" str := "2023-07-15" t, err := time.Parse(layout, str)
|
完整示例代码
package main
import ( "fmt" "time" )
func main() { now := time.Now() fmt.Println("=== 时间转字符串 ===") fmt.Println("默认格式:", now.String()) fmt.Println("RFC3339格式:", now.Format(time.RFC3339)) fmt.Println("自定义格式:", now.Format("2006年01月02日 15:04:05")) fmt.Println("日期部分:", now.Format("2006-01-02")) fmt.Println("时间部分:", now.Format("15:04:05")) fmt.Println("Unix时间戳:", now.Unix()) fmt.Println("\n=== 字符串转时间 ===") rfcStr := "2023-07-15T14:30:45+08:00" t1, _ := time.Parse(time.RFC3339, rfcStr) fmt.Println("解析 RFC3339:", t1) customStr := "2023-07-15 14:30:45" t2, _ := time.Parse("2006-01-02 15:04:05", customStr) fmt.Println("解析自定义格式:", t2) dateStr := "2023-07-15" t3, _ := time.Parse("2006-01-02", dateStr) fmt.Println("解析日期:", t3.Format("2006-01-02")) localStr := "2023-07-15 14:30:45" loc, _ := time.LoadLocation("Asia/Shanghai") t4, _ := time.ParseInLocation("2006-01-02 15:04:05", localStr, loc) fmt.Println("带时区解析:", t4) fmt.Println("\n=== 时间计算 ===") tomorrow := now.Add(24 * time.Hour) fmt.Println("明天:", tomorrow.Format("2006-01-02")) duration := tomorrow.Sub(now) fmt.Println("时间差:", duration.Round(time.Hour)) fmt.Println("\n=== 时间戳转换 ===") ts := now.Unix() fmt.Println("当前时间戳:", ts) tsTime := time.Unix(ts, 0) fmt.Println("时间戳转时间:", tsTime.Format(time.RFC3339)) fmt.Println("\n=== 时区处理 ===") utcTime := now.UTC() fmt.Println("UTC时间:", utcTime.Format(time.RFC3339)) nyLoc, _ := time.LoadLocation("America/New_York") nyTime := now.In(nyLoc) fmt.Println("纽约时间:", nyTime.Format("2006-01-02 15:04:05 MST")) }
|
常用时间格式布局
布局字符串 |
示例输出 |
说明 |
time.RFC3339 |
“2006-01-02T15:04:05Z07:00” |
ISO8601/RFC3339 格式 |
time.RFC3339Nano |
“2006-01-02T15:04:05.999999999Z07:00” |
带纳秒的 RFC3339 |
time.RFC1123 |
“Mon, 02 Jan 2006 15:04:05 MST” |
HTTP 头常用格式 |
“2006-01-02” |
“2023-07-15” |
年-月-日 |
“15:04:05” |
“14:30:45” |
时:分:秒 |
“2006/01/02 15:04” |
“2023/07/15 14:30” |
自定义格式 |
“Jan _2, 2006” |
“Jul 15, 2023” |
月名 日, 年 |
“20060102” |
“20230715” |
紧凑日期格式 |
重要注意事项
布局字符串必须使用参考时间:
Go 使用 2006-01-02 15:04:05
作为布局模板,必须精确使用这些数字
时区处理:
time.Parse()
默认使用 UTC 时区
- 对于本地时间,使用
time.ParseInLocation()
- 显示指定时区:
"2006-01-02 15:04:05 MST"
错误处理:
总是检查 time.Parse()
返回的错误:
t, err := time.Parse(layout, value) if err != nil { }
|
性能考虑:
- 重复使用
time.Parse()
时,预编译布局可以提高性能
- 对于高吞吐场景,考虑使用时间戳代替字符串
时间计算:
- 使用
time.Add()
进行时间加减
- 使用
time.Sub()
计算时间差
- 使用
time.Truncate()
和 time.Round()
进行时间取整
时间戳与日期时间的转换、时间计算和比较
在 Go 语言中,处理时间戳和日期时间的转换、时间计算以及时间点比较是常见的任务。下面我将详细解释这些操作,并提供实用的代码示例。
时间戳与日期时间的互相转换
时间戳转日期时间
package main
import ( "fmt" "time" )
func main() { now := time.Now() unixSec := now.Unix() unixMilli := now.UnixMilli() unixMicro := now.UnixMicro() unixNano := now.UnixNano()
fmt.Printf("当前时间: %s\n", now.Format("2006-01-02 15:04:05.999999")) fmt.Printf("秒级时间戳: %d\n", unixSec) fmt.Printf("毫秒级时间戳: %d\n", unixMilli) fmt.Printf("微秒级时间戳: %d\n", unixMicro) fmt.Printf("纳秒级时间戳: %d\n", unixNano)
t1 := time.Unix(unixSec, 0) fmt.Printf("秒级时间戳转时间: %s\n", t1.Format("2006-01-02 15:04:05"))
t2 := time.UnixMilli(unixMilli) fmt.Printf("毫秒级时间戳转时间: %s\n", t2.Format("2006-01-02 15:04:05.000"))
t3 := time.UnixMicro(unixMicro) fmt.Printf("微秒级时间戳转时间: %s\n", t3.Format("2006-01-02 15:04:05.000000"))
t4 := time.Unix(0, unixNano) fmt.Printf("纳秒级时间戳转时间: %s\n", t4.Format("2006-01-02 15:04:05.99999999"))
loc, _ := time.LoadLocation("America/New_York") t5 := time.Unix(unixSec, 0).In(loc) fmt.Printf("纽约时间: %s\n", t5.Format("2006-01-02 15:04:05 MST")) }
|
日期时间转时间戳
func datetimeToTimestamp() { layout := "2006-01-02 15:04:05" str := "2023-07-15 14:30:45" t, _ := time.Parse(layout, str) sec := t.Unix() milli := t.UnixMilli() micro := t.UnixMicro() nano := t.UnixNano() fmt.Printf("\n时间: %s\n", t) fmt.Printf("秒级时间戳: %d\n", sec) fmt.Printf("毫秒级时间戳: %d\n", milli) fmt.Printf("微秒级时间戳: %d\n", micro) fmt.Printf("纳秒级时间戳: %d\n", nano) loc, _ := time.LoadLocation("Asia/Tokyo") tokyoTime, _ := time.ParseInLocation(layout, "2023-07-15 14:30:45", loc) fmt.Printf("东京时间戳: %d (UTC时间: %s)\n", tokyoTime.Unix(), tokyoTime.UTC()) }
|
时间加减操作
基本时间加减
func timeArithmetic() { now := time.Now() fmt.Printf("\n当前时间: %s\n", now.Format("2006-01-02 15:04:05")) oneHourLater := now.Add(time.Hour) fmt.Printf("1小时后: %s\n", oneHourLater.Format("15:04:05")) twoDaysAgo := now.Add(-48 * time.Hour) fmt.Printf("2天前: %s\n", twoDaysAgo.Format("2006-01-02")) nextMonth := now.AddDate(0, 1, 0) fmt.Printf("1个月后: %s\n", nextMonth.Format("2006-01-02")) nextYear := now.AddDate(1, 0, 0) fmt.Printf("1年后: %s\n", nextYear.Format("2006-01-02")) tenDaysLater := now.AddDate(0, 0, 10) fmt.Printf("10天后: %s\n", tenDaysLater.Format("2006-01-02")) complexTime := now.Add(2*time.Hour + 30*time.Minute + 15*time.Second) fmt.Printf("2小时30分15秒后: %s\n", complexTime.Format("15:04:05")) }
|
计算时间差
func timeDifference() { start := time.Date(2023, 7, 1, 9, 0, 0, 0, time.UTC) end := time.Date(2023, 7, 15, 14, 30, 45, 0, time.UTC) duration := end.Sub(start) fmt.Printf("\n时间差计算:\n") fmt.Printf("开始时间: %s\n", start.Format("2006-01-02 15:04:05")) fmt.Printf("结束时间: %s\n", end.Format("2006-01-02 15:04:05")) fmt.Printf("总时间差: %v\n", duration) fmt.Printf("天数: %.1f\n", duration.Hours()/24) fmt.Printf("小时: %.1f\n", duration.Hours()) fmt.Printf("分钟: %.1f\n", duration.Minutes()) fmt.Printf("秒数: %.1f\n", duration.Seconds()) deadline := time.Date(2023, 12, 31, 23, 59, 59, 0, time.UTC) timeLeft := time.Until(deadline) fmt.Printf("\n距离2023年底还有: %d天 %d小时\n", int(timeLeft.Hours()/24), int(timeLeft.Hours())%24) }
|
时间点前后判断
基本时间比较
func timeComparison() { now := time.Now() past := now.Add(-1 * time.Hour) future := now.Add(1 * time.Hour) fmt.Printf("\n时间比较:\n") fmt.Printf("当前时间: %s\n", now.Format("15:04:05")) fmt.Printf("过去时间: %s\n", past.Format("15:04:05")) fmt.Printf("未来时间: %s\n", future.Format("15:04:05")) fmt.Printf("\n过去时间在当前时间之前? %t\n", past.Before(now)) fmt.Printf("未来时间在当前时间之后? %t\n", future.After(now)) fmt.Printf("当前时间等于现在? %t\n", now.Equal(time.Now())) fmt.Printf("\n使用比较运算符:\n") fmt.Printf("past < now? %t\n", past.Before(now)) fmt.Printf("now < future? %t\n", now.Before(future)) fmt.Printf("past < future? %t\n", past.Before(future)) }
|
时间范围判断
func timeRangeCheck() { now := time.Now() startWork := time.Date(now.Year(), now.Month(), now.Day(), 9, 0, 0, 0, now.Location()) endWork := time.Date(now.Year(), now.Month(), now.Day(), 18, 0, 0, 0, now.Location()) fmt.Printf("\n工作时间范围判断:\n") fmt.Printf("当前时间: %s\n", now.Format("15:04:05")) fmt.Printf("工作时间: %s - %s\n", startWork.Format("15:04"), endWork.Format("15:04")) if now.After(startWork) && now.Before(endWork) { fmt.Println("现在是工作时间") } else { fmt.Println("现在是非工作时间") } if now.Weekday() == time.Saturday || now.Weekday() == time.Sunday { fmt.Println("今天是周末") } else { fmt.Println("今天是工作日") } }
|
定时器与超时判断
func timerAndTimeout() { fmt.Println("\n定时器与超时示例:") timeout := 2 * time.Second start := time.Now() timer := time.NewTimer(timeout) fmt.Printf("开始时间: %s\n", start.Format("15:04:05")) <-timer.C fmt.Printf("定时器触发: %s (等待了 %v)\n", time.Now().Format("15:04:05"), time.Since(start)) result := make(chan string) go func() { time.Sleep(3 * time.Second) result <- "操作完成" }() select { case res := <-result: fmt.Println(res) case <-time.After(2 * time.Second): fmt.Println("操作超时!") } }
|
完整示例代码
package main
import ( "fmt" "time" )
func main() { timestampConversion() datetimeToTimestamp() timeArithmetic() timeDifference() timeComparison() timeRangeCheck() timerAndTimeout() }
|
关键点总结
时间戳转换:
- 使用
time.Unix()
将秒级时间戳转为时间
- 使用
time.UnixMilli()
(Go 1.17+) 将毫秒级时间戳转为时间
- 使用
time.Unix(0, nanos)
将纳秒级时间戳转为时间
- 使用
.Unix()
, .UnixMilli()
, .UnixMicro()
, .UnixNano()
获取各种精度的时间戳
时间加减:
- 使用
.Add(duration)
添加时间间隔
- 使用
.AddDate(years, months, days)
添加年/月/日
- 使用
.Sub()
计算两个时间点之间的差值
时间比较:
- 使用
.Before()
检查一个时间是否在另一个之前
- 使用
.After()
检查一个时间是否在另一个之后
- 使用
.Equal()
检查两个时间是否相等
- 使用
time.Until()
计算到未来时间点的剩余时间
- 使用
time.Since()
计算从过去时间点到现在的时间
最佳实践:
- 始终使用时区感知的时间操作
- 对于高精度需求,使用纳秒级时间戳
- 在分布式系统中使用 UTC 时间
- 使用
time.Timer
和 time.Ticker
处理定时任务
- 使用
context.WithTimeout
处理超时控制
定时器的创建与管理
在 Go 语言中,定时器主要通过 time
包提供的功能来实现。下面我将详细介绍如何创建定时器、设置频率以及启动和停止定时器。
定时器类型与创建方法
一次性定时器 (Timer)
package main
import ( "fmt" "time" )
func main() { timer := time.NewTimer(3 * time.Second) fmt.Println("定时器已创建,等待触发...") <-timer.C fmt.Println("定时器触发!") if !timer.Stop() { fmt.Println("定时器已触发,无法停止") } }
|
周期性定时器 (Ticker)
func tickerExample() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() done := make(chan bool) go func() { for i := 1; i <= 5; i++ { <-ticker.C fmt.Printf("第 %d 次触发\n", i) } done <- true }() <-done fmt.Println("周期性定时任务完成") }
|
基本使用
ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { fmt.Println(time.Now().Format("2006-01-02 15:04:05")) }
fmt.Println("周期性定时任务完成")
|
运行结果如下:
2025-08-19 11:35:57 2025-08-19 11:35:58 2025-08-19 11:35:59 2025-08-19 11:36:00 2025-08-19 11:36:01 2025-08-19 11:36:02 2025-08-19 11:36:03 2025-08-19 11:36:04
|
使用 time.After 的简化定时器
func afterExample() { fmt.Println("等待 2 秒...") <-time.After(2 * time.Second) fmt.Println("2 秒后触发!") }
|
定时器的高级管理
动态改变定时器频率
func dynamicTicker() { interval := 1 * time.Second ticker := time.NewTicker(interval) defer ticker.Stop() go func() { count := 0 for { select { case t := <-ticker.C: count++ fmt.Printf("触发 #%d: %s\n", count, t.Format("15:04:05.000")) if count%3 == 0 { ticker.Stop() interval += 500 * time.Millisecond ticker = time.NewTicker(interval) fmt.Printf("频率调整为: %.1f秒\n", interval.Seconds()) } } } }() time.Sleep(10 * time.Second) fmt.Println("动态定时器结束") }
|
多个定时器管理
func multipleTimers() { timer1 := time.NewTimer(2 * time.Second) timer2 := time.NewTimer(4 * time.Second) timer3 := time.NewTimer(6 * time.Second) for i := 0; i < 3; i++ { select { case <-timer1.C: fmt.Println("定时器1触发") case <-timer2.C: fmt.Println("定时器2触发") case <-timer3.C: fmt.Println("定时器3触发") } } }
|
带取消功能的定时器
func cancellableTimer() { timer := time.NewTimer(5 * time.Second) cancelChan := make(chan struct{}) go func() { select { case <-timer.C: fmt.Println("定时任务完成") case <-cancelChan: fmt.Println("定时任务已取消") if !timer.Stop() { <-timer.C } } }() go func() { time.Sleep(2 * time.Second) fmt.Println("用户请求取消") close(cancelChan) }() time.Sleep(6 * time.Second) }
|
定时器使用的最佳实践
正确停止定时器
func stopTimerSafely() { timer := time.NewTimer(3 * time.Second) go func() { time.Sleep(1 * time.Second) if timer.Stop() { fmt.Println("定时器已成功停止") } else { fmt.Println("定时器已触发,无法停止") <-timer.C } }() select { case <-timer.C: fmt.Println("定时器触发") default: } time.Sleep(2 * time.Second) }
|
定时器与上下文 (Context) 结合
func timerWithContext() { ctx, cancel := context.WithCancel(context.Background()) timeoutCtx, _ := context.WithTimeout(ctx, 3*time.Second) go func() { select { case <-timeoutCtx.Done(): fmt.Println("操作超时:", timeoutCtx.Err()) } }() go func() { time.Sleep(5 * time.Second) fmt.Println("长时间操作完成") }() time.Sleep(4 * time.Second) cancel() }
|
高性能定时任务
func highPerformanceScheduling() { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() taskQueue := make(chan func(), 100) for i := 0; i < 5; i++ { go func(id int) { for task := range taskQueue { start := time.Now() task() duration := time.Since(start) fmt.Printf("Worker %d 完成任务,耗时: %.2fms\n", id, float64(duration.Microseconds())/1000) } }(i) } go func() { for range ticker.C { taskQueue <- func() { time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) } } }() time.Sleep(5 * time.Second) fmt.Println("高性能定时任务结束") }
|
完整示例程序
package main
import ( "context" "fmt" "math/rand" "time" )
func main() { fmt.Println("===== 一次性定时器示例 =====") timer := time.NewTimer(3 * time.Second) <-timer.C fmt.Println("一次性定时器触发\n") fmt.Println("===== 周期性定时器示例 =====") tickerExample() fmt.Println("\n===== 简化定时器示例 =====") afterExample() fmt.Println("\n===== 动态频率定时器 =====") dynamicTicker() time.Sleep(1 * time.Second) fmt.Println("\n===== 多定时器管理 =====") multipleTimers() fmt.Println("\n===== 可取消定时器 =====") cancellableTimer() time.Sleep(1 * time.Second) fmt.Println("\n===== 安全停止定时器 =====") stopTimerSafely() fmt.Println("\n===== 上下文超时控制 =====") timerWithContext() time.Sleep(1 * time.Second) fmt.Println("\n===== 高性能定时任务 =====") highPerformanceScheduling() }
|
关键注意事项
资源泄漏预防:
- 总是使用
defer ticker.Stop()
确保定时器被正确清理
- 对于停止的 Timer,如果已触发,需要清空通道:
if !timer.Stop() { <-timer.C }
精度问题:
- Go 定时器精度通常为毫秒级
- 对于高精度需求(>100Hz),考虑专用计时方案
并发安全:
- Timer 和 Ticker 都是并发安全的
- 但重置操作(
Reset()
)需要谨慎处理
性能考虑:
- 避免创建大量短期定时器
- 对于高频任务,使用单个 Ticker 分发任务
上下文集成:
- 使用
context.WithTimeout
管理操作超时
- 在服务关闭时取消所有定时任务