详解 Go 语言中的 time.Duration 类型
swardsman
详解 Go 语言中的 time.Duration 类型
swardsman · 2018-03-17 23:10:54 · 5448 次点击 · 预计阅读时间 5 分钟 · 31分钟之前 开始浏览
这是一个创建于 2018-03-17 23:10:54 的文章,其中的信息可能已经有所发展或是发生改变。
长久以来,我一直抓狂于 Go 标准库中的 Time 包,我的抓狂来自于两个功能,一是捕获两个不同时间段之间间隔的毫秒数,二是将一个用毫秒表示的连续时间段与预先定义的时间段进行比较。这听起来很简单,没错,确实如此,但它的确让我抓狂了。
在 Time 包中,定义有一个名为 Duration 的类型和一些辅助的常量:
type Duration int64
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
这些东西我可能已经看了有上千次了,但我的大脑依旧一片迷茫。我只是想比较两个时间段、恢复要持续的时间、比较持续时间的长短并且当预设的时间用完时做一些别的事情,但无论如何这个结构还是无法解决我的困扰。我写下了下面的测试代码,但它没有卵用:
func Test() {
var waitFiveHundredMillisections int64 = 500
startingTime := time.Now().UTC()
time.Sleep(10 * time.Millisecond)
endingTime := time.Now().UTC()
var duration time.Duration = endingTime.Sub(startingTime)
var durationAsInt64 = int64(duration)
if durationAsInt64 >= waitFiveHundredMillisections {
fmt.Printf("Time Elapsed : Wait[%d] Duration[%d]\n", waitFiveHundredMillisections, durationAsInt64)
} else {
fmt.Printf("Time DID NOT Elapsed : Wait[%d] Duration[%d]\n", waitFiveHundredMillisections, durationAsInt64)
}
}
我运行了这段测试代码,然后得到了下面的输出,从输出内容来看,我定义的 500 毫秒的时间已经用完了,但怎么可能。
Time Elapsed : Wait[500] Duration[10724798]
那么问题出在哪里?我又一次将眼光投向了 Duration 类型的定义:
type Duration int64
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
从代码上看, Duration 类型中时间的基本单位是 Nanosecond ,所以当我将一个表示 10 毫秒的 Duration 类型对象转换为 int64 类型时,我实际上得到的是 10,000,000。
所以直接转换是不行的,我需要一个不同的策略使用和转换 Duration 类型。
我知道最好使用 Duration 类型中定义的数据,这将最大可能地减少问题的发生。基于 Duration 中定义的常量,我能够像下面这样创建一个 Duration 变量:
func Test() {
var duration_Milliseconds time.Duration = 500 * time.Millisecond
var duration_Seconds time.Duration = (1250 * 10) * time.Millisecond
var duration_Minute time.Duration = 2 * time.Minute
fmt.Printf("Milli [%v]\nSeconds [%v]\nMinute [%v]\n", duration_Milliseconds, duration_Seconds, duration_Minute)
}
在上面的代码中,我创建了 3 个 Duration 类型的变量,通过使用时间常数,我能够创建正确的持续时间的值。然后我使用标准库函数 Printf 和 %v 操作符,得到了下面的输出结果:
Milli [500ms]
Seconds [12.5s]
Minute [2m0s]
很酷,有木有? Printf 函数知道如何本地化显示一个 Duration 类型,它基于 Duration 类型中的每一个值,选择合适的格式进行时间的显示,当然,我也得到了期待中的结果。
实际上, Duration 类型拥有一些便捷的类型转换函数,它们能将 Duration 类型转化为 Go 语言的内建类型 int64 或 float64 ,像下面这样:
func Test() {
var duration_Seconds time.Duration = (1250 * 10) * time.Millisecond
var duration_Minute time.Duration = 2 * time.Minute
var float64_Seconds float64 = duration_Seconds.Seconds()
var float64_Minutes float64 = duration_Minute.Minutes()
fmt.Printf("Seconds [%.3f]\nMinutes [%.2f]\n", float64_Seconds, float64_Minutes)
}
我也迅速注意到了在时间转换函数中,并没有转换毫秒值的函数,使用 Seconds 和 Minutes 函数,我得到了如下输出:
Seconds [12.500]
Minutes [2.00]
但我需要转换毫秒值,为什么包里面单单没有提供毫秒值的转换呢?因为 Go 语言的设计者希望我有更多的选择,而不只是将毫秒值转换成某种单独的内建类型。下面的代码中,我将毫秒值转化为了 int64 类型和 float64 类型:
func Test() {
var duration_Milliseconds time.Duration = 500 * time.Millisecond
var castToInt64 int64 = duration_Milliseconds.Nanoseconds() / 1e6
var castToFloat64 float64 = duration_Milliseconds.Seconds() * 1e3
fmt.Printf("Duration [%v]\ncastToInt64 [%d]\ncastToFloat64 [%.0f]\n", duration_Milliseconds, castToInt64, castToFloat64)
}
我将纳秒值除以 1^6 得到了 int64 类型表示的毫秒值,将秒值乘以 1^3 ,我得到了 float64 类型表示的毫秒值,上面代码的输出如下:
Duration [500ms]
castToInt64 [500]
castToFloat64 [500]
现在,我知道了 Duration 类型是什么和怎么用,下面是我最终写的使用毫秒值的测试代码示例:
func Test() {
var waitFiveHundredMillisections time.Duration = 500 * time.Millisecond
startingTime := time.Now().UTC()
time.Sleep(600 * time.Millisecond)
endingTime := time.Now().UTC()
var duration time.Duration = endingTime.Sub(startingTime)
if duration >= waitFiveHundredMillisections {
fmt.Printf("Wait %v\nNative [%v]\nMilliseconds [%d]\nSeconds [%.3f]\n", waitFiveHundredMillisections, duration, duration.Nanoseconds()/1e6, duration.Seconds())
}
}
得到的输出如下:
Wait 500ms
Native [601.091066ms]
Milliseconds [601]
Seconds [0.601]
不再通过比较本地类型来确定时间是否已经用完,而是比较两个 Duration 类型变量,这样更加清晰明了。虽然花了一些时间,但最终我理解了 Duration 类型,我也希望这篇文章能帮助其他人在使用 Go 语言的过程中解决 Duration 类型的疑惑。
via: https://www.ardanlabs.com/blog/2013/06/gos-duration-type-unravelled.html
作者:William Kennedy 译者:swardsman 校对:polaris1119
详解 Go 语言中的 time.Duration 类型的更多相关文章
- 详解Go语言中的屏蔽现象
在刚开始学习Go语言的过程中,难免会遇到一些问题,尤其是从其他语言转向Go开发的人员,面对语法及其内部实现的差异,在使用Go开发时也避免不了会踩"坑".本文主要针对Go设计中的屏蔽 ...
- 详解go语言的array和slice 【二】
上一篇已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制sl ...
- 详解Python编程中基本的数学计算使用
详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...
- 详解Go语言调度循环源码实现
转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/448 本文使用的go的源码15.7 概述 提到"调度&q ...
- 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别
详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html 实例: ...
- 详解jquery插件中(function ( $, window, document, undefined )的作用。
1.(function(window,undefined){})(window); Q:(function(window,undefined){})(window);中为什么要将window和unde ...
- zz详解深度学习中的Normalization,BN/LN/WN
详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...
- [转载]详解网络传输中的三张表,MAC地址表、ARP缓存表以及路由表
[转载]详解网络传输中的三张表,MAC地址表.ARP缓存表以及路由表 虽然学过了计算机网络,但是这部分还是有点乱.正好在网上看到了一篇文章,讲的很透彻,转载过来康康. 本文出自 "邓奇的Bl ...
- 详解WebService开发中四个常见问题(2)
详解WebService开发中四个常见问题(2) WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...
随机推荐
- span中内容随着数字长度的添加而增大
场景:导航条中数据,当数据量不大时.仅仅会显示几页,数字仅仅有1,2.3,4..,数字写在span标签中, 则span不须要多宽.设置固定宽度就能够,但当数据量很大的.比如:日志管理--有增 删 改就 ...
- vue - 减少打包后的体积
打包命令: npm:npm run build yarn:yarn run build 路径:/config/index.js 是否产生map文件,置为false.
- 算法笔记_153:算法提高 判断名次(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 某场比赛过后,你想要知道A~E五个人的排名是什么,于是要求他们每个人说了一句话.(经典的开头……-_-!)得了第1名的人23,说了假话:得 ...
- Native App、Web App 还是Hybrid App
Native App.Web App 还是Hybrid App? 技术 标点符 1年前 (2014-05-09) 3036℃ 0评论 一.什么是Native App? Native App即原生应用, ...
- Android 打造完美的侧滑菜单/侧滑View控件
概述 Android 打造完美的侧滑菜单/侧滑View控件,完全自定义实现,支持左右两个方向弹出,代码高度简洁流畅,兼容性高,控件实用方便. 详细 代码下载:http://www.demodashi. ...
- Linux命令-目录处理命令:mv
注意:在linux下面,剪切文件和改名是同一个命令mv,而不是两个独立的命令. mv /tmp/beijing/chaoyangqu /root 移动chaoyangqu目录到root目录下面 mv ...
- SEH, SAFESEH相关
SEH, SAFESEH相关 1,触发seh异常让目标程序Read/Write无效地址,如果和栈底相邻的内存只读,尝试覆盖超出栈底 2,如何找到(显示)要覆盖的SEHod语法:dd fs:[0]sof ...
- opensips编译安装时可能遇到的问题
错误一: ERROR: could not load the script in /usr/local//lib64/opensips/opensipsctl/opensipsdbctl.pgsql ...
- C# 文件与路径操作
OpenFileDialog private void btnOpenFileDialog_Click(object sender, EventArgs e) { OpenFileDialog ope ...
- 5V系统和3.3V系统电平转换
在设计一个带MCU或者ARM系统电路时候,经常遇见MCU的VCC是3.3V,但是外围电路需要5V.有时候是反过来.虽然现在MCU的IO都声称支持TTL电平,但是我们谁也不想将MCU的IO口直接接上5V ...