如何在 Linux 上使用 ISO 時間格式表示 UTC 時間 ?
這是一個關於時間, 時區的問題. 並作為第一篇網誌向其網誌名稱出處致敬. (當然, 我不會告訴你 :P)
而如同網誌子標題所暗示的, 這是一系列記錄程式設計上的問題解答.
- 首先, 為了節省大家的時間, “結論” 會放在前面.
- 再者, 為了浪費大家的時間, 中間會加入 “廢話”.
- 然後, 為了避內容農場嫌疑, 附上冗長的 “正文”.
結論:
使用 ls
查詢檔案時,
> TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
使用 date
查詢時間時,
> date -uIsec
ls
查詢檔案時,> TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
date
查詢時間時,> date -uIsec
廢話:
好, 正文之前先說明一下兩個詞彙.
- UTC
- ISO 8601
當然你一定知道這是甚麼, 不然你也不太可能看到這篇文章. 但請容許我為不小心誤入的讀者說明.
UTC 就是所謂的"世界協調時間", 又稱世界標準時間. (只看中文還以為這世界一直在吵架. 不過, 也不算錯, 因為這名稱就是吵完架的結果 :P) 使用格里曆也就是西元紀年, 以英國格林尼治皇家天文台的當地 平太陽時 為準, 午夜零時為一天的起點. 以當地為本初子午線(0度經線), 往東為東經, 往西為西經. 東經 7.5 度到西經 7.5 度為 零時區 , 由零時區開始, 每隔 15 度劃為同一個時區(原則上). 向東每跨 1 個時區加 1 小時, 反之向西每跨 1 時區減 1 小時. 全球共分為 24個時區. 例如台灣就是 UTC+8 . 而鄰近東經或西經 180 度處畫有一條 國際換日線 . 由東經跨越國際換日線到西經, 必須將日期減掉 1 天; 反之, 由西經跨到東經去, 必須在時間上加 24 個小時.
ISO 就是"國際標準化組織", 他們訂的一連串標準, 就是所謂的 ISO 標準. 而 ISO 8601 就是"日期和時間的表示方法"的標準. 日期以 4 位的西元年, 2位月跟日, 中間以-
連接, 時間以 2 位數的時分秒表示, 其中時
須以 24 小時制表示, 中間以:
連接. 日期與時間以T
連接. 如果是 UTC 時間可以大寫字母 Z 結尾, 或+0
表示. 其他時區則以當地時間加時差表示. 例: 2016-06-21T16:42:51+0800.
ISO 在計算周數上有特別的規定. 以大寫字母W
後加上 2 位數表示一年內的第幾周. 以 1 位數表示一周內的第幾天. 第一天為星期一, 星期日為最後一天. 中間以-
連接. 而一年的第一周指的是一年中第一個 星期四 所在的那一周, 表示為 W01. 或說, 若一年的最後一天若不是在一周的前4天中, 下一年的前幾天要計入該年的最後一周. 而下一年的第一周由當年 1 月 4 日所在的那一周開始起算. 也就是說, 若一周中有不同年份, 該周則歸屬於在一周中佔的天數多的那一年.
-
連接, 時間以 2 位數的時分秒表示, 其中時
須以 24 小時制表示, 中間以:
連接. 日期與時間以T
連接. 如果是 UTC 時間可以大寫字母 Z 結尾, 或+0
表示. 其他時區則以當地時間加時差表示. 例: 2016-06-21T16:42:51+0800.W
後加上 2 位數表示一年內的第幾周. 以 1 位數表示一周內的第幾天. 第一天為星期一, 星期日為最後一天. 中間以-
連接. 而一年的第一周指的是一年中第一個 星期四 所在的那一周, 表示為 W01. 或說, 若一年的最後一天若不是在一周的前4天中, 下一年的前幾天要計入該年的最後一周. 而下一年的第一周由當年 1 月 4 日所在的那一周開始起算. 也就是說, 若一周中有不同年份, 該周則歸屬於在一周中佔的天數多的那一年.正文:
在 linux 文字模式中工作時, 每當想知道時間就偶而會令人煩躁.
但如果你總是在視窗環境下作業, 對這個說法應會感到困惑. 畢竟, 滑鼠一點, 就有一堆精美的日曆或時鐘.
而在 linux 中要知道時間, 需要鍵入date
. 例如,
> date
//但這時候, 他會回應出甚麼卻要看你的環境設定了. 他也可能是
//Tue Jun 21 16:42:51 CST 2016
// 或
//Tue Jun 21 08:42:51 UTC 2016
// 或
//二 6月 21 16:42:51 CST 2016
也就是在不同的主機上, 同樣鍵入date
, 很有可能會得到完全不同日期時間格式的字串.
這在要比較時間時, 你必須腦補成一樣的格式. 這或許不是難事, 但很煩. 尤其在寫程式判斷時.
然而, 託 linux 的設計理念是隨心所欲的福. 幸好, 不管 date
或 ls
都允許定義輸出的日期時間格式. 所以, 我們只要這樣做就好了. 但另一個問題是, 要輸出甚麼樣子的格式? 既然要統一格式, 當然要選一個全世界所有人都不會誤解的. 這也不用我們煩惱, ISO 已經幫我們做完這件事了, 就是 ISO 8601.
首先是 date
, 根據說明,
date [OPTION]... [+FORMAT]
只要在其後加上+
號, 再給出格式, 即可. 但
%% 文字的 %
%a 本地化簡短的周名稱 (e.g., Sun)
%A 本地化完整的周名稱 (e.g., Sunday)
%b 本地化簡短的月份名稱 (e.g., Jan)
%B 本地化完整的月份名稱 (e.g., January)
%c 本地化日期和時間 (e.g., Thu Mar 3 23:05:25 2005)
%C 世紀; 像 %Y, 省略最後兩位數 (e.g., 20)
%d 月份中的日 (e.g., 01)
%D 日期; 等效於 %m/%d/%y
%e 月份中的日, 填充空白; 等效於 %_d
%F 完整的日期; 等效於 %Y-%m-%d
%g 以 ISO 周數為準的年份的最後兩位數, (見 %G)
%G 以 ISO 周數為準年份, (見 %V); 通常跟著 %V 使用
%h 等效於 %b
%H 時 (00..23)
%I 時 (01..12)
%j day of year (001..366)
%k 時, 填充空白 ( 0..23); 等效於 %_H
%l 時, 填充空白 ( 1..12); 等效於 %_I
%m 月份 (01..12)
%M 分 (00..59)
%n 換行
%N 奈秒 (000000000..999999999)
%p 等效的本地化 AM 或 PM; 未知則空白
%P 像 %p, 但小寫
%r 本地化 12-小時制 時鐘的時間 (e.g., 11:11:04 PM)
%R 24-小時制的 時 和 分; 等效於 %H:%M
%s 從 1970-01-01 00:00:00 UTC 的秒數
%S 秒 (00..60)
%t 跳格
%T 時間; 等效於 %H:%M:%S
%u 一周中的日數 (1..7); 1 是星期一
%U 一年中中的周數, 以星期天為一周的第一天 (00..53)
%V ISO 的周數, 以星期一為一周的第一天 (01..53)
%w 一周中的日數 (0..6); 0 是星期天
%W 一年中中的周數, 星期一是一周的第一天 (00..53)
%x 本地化的日期表示法 (e.g., 12/31/99)
%X 本地化的時間表示法 (e.g., 23:13:48)
%y 年份的最後兩位數 (00..99)
%Y 年
%z 以 +hhmm 數字化的時區 (e.g., -0400)
%:z 以 +hh:mm 數字化的時區 (e.g., -04:00)
%::z 以 +hh:mm:ss 數字化的時區 (e.g., -04:00:00)
%:::z 以必要的精準度表示的數字化時區 (e.g., -04, +05:30)
%Z 縮寫的文字化時區 (e.g., EDT)
可以使用的格式太多, 簡言之
> date +%Y-%m-%dT%H:%M:%S%z
// 或, 可用
> date -uIsec
-u
是使用 UTC 時間
-I
是使用 ISO 8601 格式, sec
是修飾 -I
指示時間要精準到秒
再來是常用的ls
, 使用 --time-style
修飾 -l
, 而格式可參考 date
.
> ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
但這樣出現的是本地時間. 為了要讓位在世界各地的伺服器表示的時間基準都一樣. 而不用自己腦補時區的加減, 這必須將環境變數 TZ
, 也就是 TIMEZONE 設成 utc
. 當然, 每次下 ls
就要去改一次環境變數也太麻煩了. 合併指令如下,
> TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
// 2016-06-21T16:42:51+8000
// 如果, 不堅持要完全符合 ISO 8601, 則可簡化用
> ls --full-time
// 或, 可用
> ls -l --time-style=full-iso
// 2016-06-21 16:42:51.955353135 +8000
而每次要下 ls
就要打這麼多字, 也是很累. 將他設成指令別名吧.
> vi ~/.profile
// 修改 ~/.profile , 在最下面增加如下一行
alias ll='TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z'
之後, 只要打 ll
即可.
對我而言, 在下 ls
時用標準的 ISO 格式, 會因為日期跟時間連在一起, 反而, 不容易看. 改用
alias ll='TZ=utc ls -l --time-style="+%Y-%m-%d %H:%M:%S %:::z"'
// 2016-06-21 08:42:51 +00
// 因為格式中有空白, 所以 --time-style 的參數值要用 `"` 括起來
但如果你總是在視窗環境下作業, 對這個說法應會感到困惑. 畢竟, 滑鼠一點, 就有一堆精美的日曆或時鐘.
date
. 例如,> date
//但這時候, 他會回應出甚麼卻要看你的環境設定了. 他也可能是
//Tue Jun 21 16:42:51 CST 2016
// 或
//Tue Jun 21 08:42:51 UTC 2016
// 或
//二 6月 21 16:42:51 CST 2016
date
, 很有可能會得到完全不同日期時間格式的字串.date
或 ls
都允許定義輸出的日期時間格式. 所以, 我們只要這樣做就好了. 但另一個問題是, 要輸出甚麼樣子的格式? 既然要統一格式, 當然要選一個全世界所有人都不會誤解的. 這也不用我們煩惱, ISO 已經幫我們做完這件事了, 就是 ISO 8601.date
, 根據說明,date [OPTION]... [+FORMAT]
+
號, 再給出格式, 即可. 但 %% 文字的 %
%a 本地化簡短的周名稱 (e.g., Sun)
%A 本地化完整的周名稱 (e.g., Sunday)
%b 本地化簡短的月份名稱 (e.g., Jan)
%B 本地化完整的月份名稱 (e.g., January)
%c 本地化日期和時間 (e.g., Thu Mar 3 23:05:25 2005)
%C 世紀; 像 %Y, 省略最後兩位數 (e.g., 20)
%d 月份中的日 (e.g., 01)
%D 日期; 等效於 %m/%d/%y
%e 月份中的日, 填充空白; 等效於 %_d
%F 完整的日期; 等效於 %Y-%m-%d
%g 以 ISO 周數為準的年份的最後兩位數, (見 %G)
%G 以 ISO 周數為準年份, (見 %V); 通常跟著 %V 使用
%h 等效於 %b
%H 時 (00..23)
%I 時 (01..12)
%j day of year (001..366)
%k 時, 填充空白 ( 0..23); 等效於 %_H
%l 時, 填充空白 ( 1..12); 等效於 %_I
%m 月份 (01..12)
%M 分 (00..59)
%n 換行
%N 奈秒 (000000000..999999999)
%p 等效的本地化 AM 或 PM; 未知則空白
%P 像 %p, 但小寫
%r 本地化 12-小時制 時鐘的時間 (e.g., 11:11:04 PM)
%R 24-小時制的 時 和 分; 等效於 %H:%M
%s 從 1970-01-01 00:00:00 UTC 的秒數
%S 秒 (00..60)
%t 跳格
%T 時間; 等效於 %H:%M:%S
%u 一周中的日數 (1..7); 1 是星期一
%U 一年中中的周數, 以星期天為一周的第一天 (00..53)
%V ISO 的周數, 以星期一為一周的第一天 (01..53)
%w 一周中的日數 (0..6); 0 是星期天
%W 一年中中的周數, 星期一是一周的第一天 (00..53)
%x 本地化的日期表示法 (e.g., 12/31/99)
%X 本地化的時間表示法 (e.g., 23:13:48)
%y 年份的最後兩位數 (00..99)
%Y 年
%z 以 +hhmm 數字化的時區 (e.g., -0400)
%:z 以 +hh:mm 數字化的時區 (e.g., -04:00)
%::z 以 +hh:mm:ss 數字化的時區 (e.g., -04:00:00)
%:::z 以必要的精準度表示的數字化時區 (e.g., -04, +05:30)
%Z 縮寫的文字化時區 (e.g., EDT)
> date +%Y-%m-%dT%H:%M:%S%z
// 或, 可用
> date -uIsec
-u
是使用 UTC 時間-I
是使用 ISO 8601 格式, sec
是修飾 -I
指示時間要精準到秒ls
, 使用 --time-style
修飾 -l
, 而格式可參考 date
.> ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
TZ
, 也就是 TIMEZONE 設成 utc
. 當然, 每次下 ls
就要去改一次環境變數也太麻煩了. 合併指令如下,> TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z
// 2016-06-21T16:42:51+8000
// 如果, 不堅持要完全符合 ISO 8601, 則可簡化用
> ls --full-time
// 或, 可用
> ls -l --time-style=full-iso
// 2016-06-21 16:42:51.955353135 +8000
ls
就要打這麼多字, 也是很累. 將他設成指令別名吧.> vi ~/.profile
// 修改 ~/.profile , 在最下面增加如下一行
alias ll='TZ=utc ls -l --time-style=+%Y-%m-%dT%H:%M:%S%z'
ll
即可.ls
時用標準的 ISO 格式, 會因為日期跟時間連在一起, 反而, 不容易看. 改用alias ll='TZ=utc ls -l --time-style="+%Y-%m-%d %H:%M:%S %:::z"'
// 2016-06-21 08:42:51 +00
// 因為格式中有空白, 所以 --time-style 的參數值要用 `"` 括起來
沒有留言:
張貼留言