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 类型的更多相关文章

  1. 详解Go语言中的屏蔽现象

    在刚开始学习Go语言的过程中,难免会遇到一些问题,尤其是从其他语言转向Go开发的人员,面对语法及其内部实现的差异,在使用Go开发时也避免不了会踩"坑".本文主要针对Go设计中的屏蔽 ...

  2. 详解go语言的array和slice 【二】

    上一篇已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制sl ...

  3. 详解Python编程中基本的数学计算使用

    详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...

  4. 详解Go语言调度循环源码实现

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/448 本文使用的go的源码15.7 概述 提到"调度&q ...

  5. 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别

    详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html   实例: ...

  6. 详解jquery插件中(function ( $, window, document, undefined )的作用。

    1.(function(window,undefined){})(window); Q:(function(window,undefined){})(window);中为什么要将window和unde ...

  7. zz详解深度学习中的Normalization,BN/LN/WN

    详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...

  8. [转载]详解网络传输中的三张表,MAC地址表、ARP缓存表以及路由表

    [转载]详解网络传输中的三张表,MAC地址表.ARP缓存表以及路由表 虽然学过了计算机网络,但是这部分还是有点乱.正好在网上看到了一篇文章,讲的很透彻,转载过来康康. 本文出自 "邓奇的Bl ...

  9. 详解WebService开发中四个常见问题(2)

    详解WebService开发中四个常见问题(2)   WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...

随机推荐

  1. tomcat生产部署关键參数设置

    JVM设置 个节点.每一个tomcat估计处理500个链接,那么连接池的长连接数最大设为2000. 全节点复制(DeltaManager)模式集群节点数3-6为宜. 主备复制(BackupMnagag ...

  2. (C++)C++多态性中的静态绑定和动态绑定

    静态绑定和动态绑定是C++多态性的一种特性. 1.对象的静态类型和动态类型: 对象的静态类型: 对象在声明是采用的类型,在编译期确定: 对象的动态类型: 当前对象所指的类型,在运行期决定,对象的动态类 ...

  3. Python函数的循环调用

    def foo (): print 'runing foo' bar () def bar (): print 'runing bar' foo () bar() 直接上脚本,上面的脚本如果换成C语言 ...

  4. Linux环境下c语言静态链接库和动态链接库创建和使用

    库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀. 面对比一下两者: 静态链接库:当要使用时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功, ...

  5. 【Android】读取sdcard上的图片

    Android读取sdcard上的图片是很easy的事情,以下用一个样例来说明这个问题. 首先,在sdcard上有一张已经准备好的img25.jpg 以下,须要做的是把这张图片读取到app中显示. 做 ...

  6. Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 )购买链接

      Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 )购买链接: 推荐给你高品质的实战课程 https://laravel-china.org/courses?rf=158 ...

  7. [Swift A] - Welcome to Swift

    Swift is a new object-oriented programming language for iOS and OS X development. Swift is modern, p ...

  8. 一次安装win10 ubuntu16.0经过记录

    步骤摘要 三个U盘: 1.制作WIN8 PE启动盘,使用的软件为“U深度装机版”,可自行百度下载 2.制作WIN10系统安装盘,使用UltraISO,这里使用的win10镜像为: 链接: http:/ ...

  9. 【Linux】centos和ubuntu下php5安装redis2.24扩展

    1.服务器先安装redis-server,这是毋庸置疑的!!! 2.服务器开启redis-server,配置相关参数 3.配置好redis服务器后,再安装php的redis扩展phpredis. 一. ...

  10. Python 函数的 return 是否是必须的?

    —— Python 函数的 return 是否是必须的? —— return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式.不带参数值的return语句返回None. 来看一段关于 r ...