前幾日調整服務器緩存功能代碼時為方便處理時間戳,存儲時將日期時間轉為秒數的整數格式,使用的編程語言是 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.lxweimin.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