前几日调整服务器缓存功能代码时为方便处理时间戳,存储时将日期时间转为秒数的整数格式,使用的编程语言是 Ruby 当时并不知晓这种存储方式是 Unix 世界早已通用的时间格式。
事后数据运维同事查看缓存说明文档时,反问时间戳的整数格式是否从格林尼治时间算起?鬼知道 Time.now.to_i
是怎么算的,走心的疑惑还有其他计算方式吗?无知的反问语里竟透露着焉敢质疑的咄咄逼人,久久不知如何解释。
Time#to_i
Returns the value of time as an integer number of seconds since the Epoch.
API 解释为从 Epoch 算起的总秒数,看着陌生的 Epoch 误以为是个专有名称,但英文再差还是有份自信它怎么也不会音译为格林尼治。
Epoch 时期; 纪元; 世; 新时代;
Epoch Time 指一个特定的时间:1970-01-01 00:00:00 UTC。
UTC 又是什么?
协调世界时(英:Coordinated Universal Time ,法:Temps Universel Coordonné),又称世界统一时间,世界标准时间,国际协调时间。英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
UTC 时间是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统,是新的标准时间。
新的标准时间?那旧标准时间的有那些?原来就是格林尼治时间。
格林尼治标准时间(Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,本初子午线被定义为通过那里的经线。
由于地球每天的自转有些不规则,而且正在缓慢减速,因此,格林尼治时间已不再被作为标准时间使用。新的标准时间,是由原子钟报时的协调世界时(UTC)。
明白了 GMT/UTC 的标准时间方案更替的承接关系,那 1970 年发生了什么,以至于 Unix 系统以它作 “纪元”。
原来 Unix 就是在那个时代产生的,1969 年发布的雏形,最早是基于硬件 60Hz 的时间计数。
1971年底出版的《Unix Programmer's Manual》里定义的 Unix Time 是以 1971年1月1日00:00:00 作为起始时间,每秒增长 60。考虑到 32 位整数的范围,如果每秒 60 个数字,则两年半就会循环一轮,于是改成以秒为计数单位。循环周期有136年之长,就不在乎起始时间是 1970 还是 1971 年,遂改成人工记忆、计算比较方便的1970年。
看起来很乱的概念此时清晰很多,Unix 的世界开启了 “纪元”,Unix 时间戳也就成为了一个专有名称。
Unix 时间戳是一种时间表示方式,定义为从格林尼治时间 1970年01月01日 00时00分00秒 起至现在的总秒数,不考虑闰秒。
Unix 时间戳的英文称呼列表:
- POSIX time
- Epoch time
- Unix epoch
- Unix time
- Unix timestamp
GMT/UTC 是两套时间标准;1970 年是 Unix 系统诞生的年代里易于人工记忆的年份;1970 年作为 Unix 系统历史长河中里程碑式的一年,正式开启了 Unix 世界的 “纪元”,番邦称 Epoch。
UTC 本质强调的是比 GMT 更为精确的世界时间标准,不过对于开发者而言,GMT 与 UTC 的功能与精确度是没有差别的。
HTTP 响应头中的日期就是使用旧款的 GMT 标准时间:
curl -I jianshu.com
HTTP/1.1 301 Moved Permanently
Server: Tengine
Date: Mon, 12 Dec 2016 14:29:05 GMT
Content-Type: text/html
Content-Length: 278
Connection: keep-alive
Location: http://www.jianshu.com/
MySQL
MySQL 不止是稳定好用存储数据的库,更是一套超强的工具集,生成与反解 Unix 时间戳的操作也只是一条命令的执行。
mysql> select unix_timestamp();
+------------------+
| unix_timestamp() |
+------------------+
| 1481545686 |
+------------------+
1 row in set (0.01 sec)
mysql> select from_unixtime(1481545686);
+---------------------------+
| from_unixtime(1481545686) |
+---------------------------+
| 2016-12-12 20:28:06 |
+---------------------------+
1 row in set (0.01 sec)
感兴趣的可以查询一下这两个函数,用法相当灵活,以便需要时脑中多一套解决思路,莫要有事没事就写脚本。
生长在天朝,配置数据库时除了中文编码外就是时间的设置,那么如何查看当前数据库是否按配置的时区在运行呢?
$ cat /ect/my.cnf
[mysqld]
default_time_zone='+08:00'
mysql> select timediff(now(), utc_timestamp) as date_area;
+-----------+
| date_area |
+-----------+
| 08:00:00 |
+-----------+
1 row in set (0.00 sec)
mysql> select utc_timestamp();
+---------------------+
| utc_timestamp() |
+---------------------+
| 2016-12-12 13:51:15 |
+---------------------+
1 row in set (0.00 sec)
mysql> select current_timestamp();
+---------------------+
| current_timestamp() |
+---------------------+
| 2016-12-12 21:50:38 |
+---------------------+
1 row in set (0.00 sec)
mysql> select now();
+---------------------+
| now() |
+---------------------+
| 2016-12-12 21:50:49 |
+---------------------+
1 row in set (0.00 sec)
MySQL# UTC_TIMESTAMP
Returns the current UTC date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function is used in a string or numeric context.
UTC 标准时间按指定的格式显示,那么还有其他的显示格式?查询 API 果然无规矩不成方圆,发现真有个函数可以获取不同标准时间的显示格式,自己过于肤浅,这才意示到优秀的工具中随便显示的时间格式都是各有出处。
mysql> select get_format(datetime,'iso') as iso;
+-------------------+
| iso |
+-------------------+
| %Y-%m-%d %H:%i:%s |
+-------------------+
1 row in set (0.01 sec)
显示格式的标准不多,贴一下权当备忘:
Function | Call Result |
---|---|
GET_FORMAT(DATE,'USA') | '%m.%d.%Y' |
GET_FORMAT(DATE,'JIS') | '%Y-%m-%d' |
GET_FORMAT(DATE,'ISO') | '%Y-%m-%d' |
GET_FORMAT(DATE,'EUR') | '%d.%m.%Y' |
GET_FORMAT(DATE,'INTERNAL') | '%Y%m%d' |
GET_FORMAT(DATETIME,'USA') | '%Y-%m-%d %H.%i.%s' |
GET_FORMAT(DATETIME,'JIS') | '%Y-%m-%d %H:%i:%s' |
GET_FORMAT(DATETIME,'ISO') | '%Y-%m-%d %H:%i:%s' |
GET_FORMAT(DATETIME,'EUR') | '%Y-%m-%d %H.%i.%s' |
GET_FORMAT(DATETIME,'INTERNAL') | '%Y%m%d%H%i%s' |
GET_FORMAT(TIME,'USA') | '%h:%i:%s %p' |
GET_FORMAT(TIME,'JIS') | '%H:%i:%s' |
GET_FORMAT(TIME,'ISO') | '%H:%i:%s' |
GET_FORMAT(TIME,'EUR') | '%H.%i.%s' |
GET_FORMAT(TIME,'INTERNAL') | '%H%i%s' |
Terminal
若在数据库中操作,使用上述函数已经可以随手生成、解析 Unix 时间戳,若未在数据库中操作,难不成为查看 Unix 时间戳还要再登录数据库?打开命令终端,也是一个命令分秒间释然的事情。
$ uname -s
Darwin
$ date +%s
1481546766
$ date -r 1481546766
2016年12月12日 星期一 20时46分06秒 CST
$ cat /etc/redhat-release
CentOS release 6.5 (Final)
$ date +%s
1481546890
$ date -d @1481546890
2016年 12月 12日 星期一 20:48:10 CST
$ irb
irb(main):001:0> t = Time.now.to_i
=> 1481547158
irb(main):002:0> Time.at(t)
=> 2016-12-12 20:52:38 +0800
> window.userAgent
< "mozilla/5.0 (macintosh; intel mac os x 10_10_5) applewebkit/601.7.7 (khtml, like gecko) version/9.1.2 safari/601.7.7"
> var t = Math.round(new Date().getTime() / 1000)
< undefined
> t
< 1481601053
> new Date(t * 1000)
< Tue Dec 13 2016 11:50:53 GMT+0800 (CST)
CST 又是什么时间标准?CST 可视为美国、澳大利亚、古巴或中国的标准时间,可为如下 4 个不同时区的缩写:
- 古巴标准时间:Cuba Standard Time UT-4:00
- 中国标准时间:China Standard Time UT+8:00
- 美国中部时间:Central Standard Time (USA) UT-6:00
- 澳大利亚中部时间:Central Standard Time (Australia) UT+9:30
简单查找资料未找到 CST 存在的意义,仅仅是上述几行文字的解释。TODO: CST 产生的原因
参考列表中 百科:Unix时间戳 值得简单阅读,内有彩蛋。
其他标准时间
CET(Central European Time,CET)欧洲中部时间是比世界标准时间(UTC)早一个小时的时区名称之一。
它被大部分欧洲国家和部分北非国家采用。
冬季时间为 UTC + 1,夏季欧洲夏令时为 UTC + 2。
DST (Daylight Saving Time)夏日节约时间,指在夏天太阳升起的比较早时,将时钟拨快一小时,以提早日光的使用,在英国则称为夏令时间(Summer Time)。
这个构想于 1784年由美国班杰明·富兰克林提出来,1915 年德国成为第一个正式实施夏令日光节约时间的国家,以削减灯光照明和耗电开支。自此以后,全球以欧洲和北美为主的约 70 个国家都引用了这个套方案。
欧洲手机上也有很多 GSM 系统的基地台,除了会传送当地时间外也包括夏令日光节约时间,做为手机的时间标准,使用者可以自行决定开启或关闭。
值得注意的是,某些国家有实施「夏日节约时间」的制度,出国时别忘了跟随当地习惯在表上调整一下,这可是机械表没有的功能设计!
各标准时间的计算
- UTC = GMT
- CET = UTC/GMT + 1
- CST = UTC/GMT + 8
- CST = CET + 9
参考
- 百科:Unix时间戳
- 知乎:Unix 时间戳为什么是自 1970 年 1 月 1 日起的绝对时间?
- 知乎:使用CST时间的意义是什么?
- CSDN:Unix时间戳(Unix timestamp)及其他时间标准
- CSDN:CST,CET,UTC,GMT,DST,Unix时间戳几种常见时间概述与关系
- :mysql UNIX时间戳与日期的相互转换
- Linux命令date日期时间和Unix时间戳互转