小 T 導讀:很多新用戶在(zai)配置(zhi)TDengine的時(shi)候,偶爾會因為配置(zhi)了(le)錯誤的時(shi)區(timezone),而導致(zhi)寫入和(he)查(cha)詢的時(shi)間出現錯位。今(jin)天(tian)希(xi)望這(zhe)篇文章,能將日期時(shi)間、時(shi)間戳(chuo)以(yi)及TDengine在(zai)寫入和(he)查(cha)詢時(shi)處(chu)理時(shi)區的行為等(deng)描述清(qing)楚。
TDengine是濤思數(shu)據(ju)專為物聯網、車聯網、工(gong)業互聯網、IT運維(wei)等設計和優化(hua)的(de)大數(shu)據(ju)平臺,核心的(de)時序數(shu)據(ju)庫在處理時序數(shu)據(ju)上有著十(shi)分優異(yi)的(de)性能(neng)。
一般來說(shuo),時(shi)(shi)(shi)序數據(ju)就是帶(dai)有時(shi)(shi)(shi)間(jian)序列屬(shu)性(xing)的(de)(de)數據(ju)。在處(chu)理時(shi)(shi)(shi)序數據(ju)時(shi)(shi)(shi),TDengine有著自己獨特(te)的(de)(de)方式。但是如果你(ni)沒(mei)有正確(que)理解(jie)TDengine在寫(xie)入和查(cha)詢(xun)上的(de)(de)行為,極可能(neng)會因為配置了錯誤的(de)(de)時(shi)(shi)(shi)區(timezone),而導致(zhi)寫(xie)入和查(cha)詢(xun)的(de)(de)時(shi)(shi)(shi)間(jian)出現(xian)錯位。
下面是(shi)一個真實用戶的(de)例子:

從上圖中(zhong)可以看到,用戶(hu)(hu)執行的(de)一條(tiao)SQL寫入了“2021-07-23 07:04:00.000″這個時刻的(de)數(shu)據(ju),可是在(zai)不同的(de)客戶(hu)(hu)端中(zhong),查(cha)詢出的(de)結果卻相(xiang)差了13個小時。
今(jin)天(tian)希望通過這(zhe)篇文章(zhang),將日期時(shi)間、時(shi)間戳以及(ji)TDengine在寫(xie)入和(he)查詢時(shi)處理時(shi)區的行為等(deng)描(miao)述(shu)清(qing)楚,并給出如(ru)何設置timezone參數的意見(jian),供大家參考。
在開始之前,你需要先了解以下這三點
1. TDengine中用時(shi)間戳(chuo)表示(shi)日(ri)期時(shi)間,以標(biao)準的Unix元年(nian)時(shi)間(UTC時(shi)區(qu)1970年(nian)1月1日(ri)0點0分0秒)為原點,支持毫秒、微秒、納秒三(san)種精度(du);
2. 在寫入時(shi)(shi),如果SQL中是本地日(ri)期(qi)時(shi)(shi)間(jian)格式,TDengine的(de)客戶端使用(yong)當前(qian)生效的(de)timezone配置(zhi),將SQL中的(de)日(ri)期(qi)時(shi)(shi)間(jian)轉(zhuan)換為timestamp;同(tong)時(shi)(shi),也(ye)支持(chi)使用(yong)RFC-3339格式的(de)日(ri)期(qi)時(shi)(shi)間(jian)進行寫入;
3. 在shell中(zhong)查(cha)詢時(shi),客戶端使用當前生效的timezone配置,將TDengine中(zhong)存儲的timestamp轉換為日期時(shi)間格式進行顯示。
本文所用相關概念
- 本地日期時間:表示當地的日期時間。12:00是中午吃飯的時間,8:00是早上上班的時間,這是人類習慣的一種表示時間的方式,是不帶時區信息的日期和時間,可以當成一個String。例如:2021-07-21 12:00:00.000,表示2021年7月21日正午,時間精度以毫秒記,這個日期時間的表示方法,不帶任何時區信息。
- 時區:地理概念,按照UTC/格林威治時區,把地球劃分成向東和向西各12個時區,其中東12區和西12區是一個區。時區可以通過’Asia/shanghai’這樣的’地區/城市’的方式表示,也可以用UTC偏移的方式表示。例如:UTC+8,代表東八區,當協調世界時(UTC)時間為凌晨2點的時候,當地的時間為2+8點,即早上10點。
- RFC 3339:一種表示日期時間的標準格式。RFC 3339是帶時區信息的格式,即包含日期時間信息,也有時區信息。例如,以下兩個時間在地球上是同一時刻:2019-10-12T07:20:50+00:00,這個表示2019年10月12日,上午7點20分50秒(UTC+0時區),2019-10-12T15:20:50+08:00,這個表示2019年10月12日,下午3點20分50秒(UTC+8時區)。
- 時間戳:是機器存儲和計算時間的方式。以Unix元年(UTC時區1970年1月1日0點0分0秒)開始經過的秒數計算,不同精度的計時方式,可以有不同的時間戳。例如:0,表示UTC時區1970年1月1日凌晨的時間。
本(ben)地日期(qi)時(shi)間、時(shi)區(qu)信息、時(shi)間戳的關系可以參(can)考下(xia)面(mian)這(zhe)張圖:

TDengine如何處理日期時間?
寫入
如(ru)果在insert語(yu)句中,用一個String表示日期時間,插入到TDengine,存在著將這(zhe)個String解析(xi)成timestamp的(de)過(guo)程。這(zhe)個String存在不同的(de)格(ge)式,合(he)法的(de)格(ge)式包(bao)括(kuo):
(1)RFC 3339標準的表(biao)示(shi)方(fang)式
(2)yyyy-MM-dd hh:mm:ss
第1種情況——采用RFC 3339標準,那(nei)么這個String是帶(dai)時區信息的,可以明(ming)確地將其轉換成timestamp。例如:
這里,介紹一個小技巧:使用-r參數啟動taos shell時,timestamp類型的數據,將會以時間戳(long值)的形式顯示。# taos -rtaos> drop table test.weather;Query OK, 0 of 0 row(s) in database (0.004202s)taos> create table test.weather(ts timestamp, f1 float) ;Query OK, 0 of 0 row(s) in database (0.012690s)taos> insert into test.weather values('1970-01-01T08:00:00.000+08:00',22.00) ;Query OK, 1 of 1 row(s) in database (0.002363s)taos> select * from test.weather;ts | f1 |========================================0 | 22.00000 |Query OK, 1 row(s) in set (0.001476s)
可以(yi)看到,1970-01-01T08:00:00.000+08:00,代表UTC+8時區1970年1月1日上午8:00,這正好對應UTC時區的凌晨,所(suo)以(yi)在timestamp是(shi)0。
第(di)2種情(qing)況——在insert語句中使用yyyy-MM-dd hh:mm:ss格(ge)式的(de)時間字符串,不含(han)時區(qu)信(xin)息。這(zhe)時,taos客戶端(duan)會采用當前timezone信(xin)息,將字符串轉化(hua)成(cheng)timestamp。例(li)如:
可以(yi)看到,1970-01-01T08:00:00.000+08:00,代表UTC+8時區(qu)1970年(nian)1月1日(ri)上午8:00,這正好對應UTC時區(qu)的凌晨,所以(yi)在timestamp是0。
第2種情況(kuang)——在(zai)insert語(yu)句中使(shi)用yyyy-MM-dd hh:mm:ss格式的時(shi)間字符串,不含時(shi)區信息。這(zhe)時(shi),taos客戶(hu)端(duan)會(hui)采用當(dang)前timezone信息,將字符串轉化成timestamp。例如:
taos> show variables;name | value |============================================================timezone | (CST, +0800) |taos> insert into test.weather(ts, f1) values('1970-01-01 00:00:00.000', 22.00);Query OK, 1 of 1 row(s) in database (0.001290s)taos> select * from test.weather;ts | f1 |========================================-28800000 | 22.00000 |Query OK, 1 row(s) in set (0.002220s)
可以看到,insert語(yu)句使用了配置文件(jian)中的時區信(xin)息(xi),和(he)insert語(yu)句中的日期(qi)時間(jian)信(xin)息(xi),即(ji)“1970-01-01 00:00:00+08:00”,這個值在時間(jian)戳中正(zheng)好代表-28800000。
由此可見,在TDengine中,時(shi)間原點(dian)是國際通用的Unix元年(nian)(UTC時(shi)區1970年(nian)1月1日凌晨)。
查詢
# 在taos.cfg內配置timezone# cat /etc/taos/taos.cfg | grep timezonetimezone UTC+0# 在shell中查詢timezonetaos> show variables;name | value |============================================================timezone | (CST, +0800) |# taos -s "select * from test.weather" -rWelcome to the TDengine shell from Linux, Client Version:2.0.20.11Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.taos> select * from test.weatherts | f1 |========================================-28800000 | 22.00000 |Query OK, 1 row(s) in set (0.002564s)# taos -s "select * from test.weather"Welcome to the TDengine shell from Linux, Client Version:2.0.20.11Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.taos> select * from test.weather;ts | f1 |=================================================1969-12-31 16:00:00.000 | 22.00000 |Query OK, 1 row(s) in set (0.002306s)
可以看(kan)到,select語句(ju)在查詢時,依然(ran)存在著(zhu)從ts轉換(huan)為一(yi)個string串的(de)情況,Tdengine會將ts轉換(huan)成當前(qian)taos client中的(de)時區。
Timezone配置為UTC-8
有些用(yong)戶(hu)不理解,為(wei)什么在(zai)(zai)TDengine中(zhong)timezone會被配置(zhi)為(wei)UTC-8?原因是,在(zai)(zai)POSIX標準中(zhong),表示(shi)(shi)時(shi)區(qu)偏移量的(de)(de)方式和地理的(de)(de)表示(shi)(shi)方式不一致。參考(kao)Wikipedia中(zhong)的(de)(de)定(ding)義(yi),在(zai)(zai)ISO 8601中(zhong),UTC+8為(wei)東八區(qu),該(gai)時(shi)區(qu)是以中(zhong)文為(wei)主的(de)(de)時(shi)區(qu)。那在(zai)(zai)Unix中(zhong),東八區(qu)又應該(gai)如何表示(shi)(shi)?請(qing)參考(kao)下面這個例子:
# date --help用法:date [選項]... [+格式]或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]Display the current time in the given FORMAT, or set the system date.%z +hhmm數字時區(例如,-0400)%:z +hh:mm數字時區(例如,-04:00)%Z按字母表排序的時區縮寫 (例如,EDT)Examples:Show the time on the west coast of the US (use tzselect(1) to find TZ)$ TZ='America/Los_Angeles' date# 使用TZ='UTC-8'查看當前時間# TZ='UTC-8' date +'%Y-%m-%d %H:%M:%S %Z %z'2021-08-01 22:31:29 UTC +0800# 使用TZ='UTC'查看當前時間# TZ='UTC' date +'%Y-%m-%d %H:%M:%S %Z %z'2021-08-01 14:31:51 UTC +0000# 使用TZ='UTC+8'查看當前時間# TZ='UTC+8' date +'%Y-%m-%d %H:%M:%S %Z %z'2021-08-01 06:32:06 UTC -0800
可(ke)見,在POSIX標準中,UTC-8代表東八區,UTC+8代表西八區。這里與地理上表示(shi)時區的習慣(guan)是不一致的。在taos.cfg中,TDengine使用的是POSIX Timezone標準。
在JDBC中設置Timezone
在使用(yong)JDBC Connector連接TDengine時,可以通過3個途徑設置timezone參數,分別為:url、properties和taos.cfg配置文件。
// urlConnection conn = DriverManager.getConnection("jdbc:TAOS://taosdemo.com:6030/test?timezone=UTC-8", "root", "taosdata");// propertiesProperties connProps = new Properties();connProps.setProperty(TSDBDriver.PROPERTY_KEY_USER, "root");connProps.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, "taosdata");connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");// 當url和properties中都沒有制定timezone的情況下,會使用本地配置文件taos.cfg中timezone的配置參數Connection conn = DriverManager.getConnection("jdbc:TAOS://taosdemo.com:6030/test", connProps);
參考(kao)文檔: //tdengine.com/docs/cn/v2.0/connector/java
總結
最后,我們再回顧一下前(qian)文中(zhong)描述的(de)用戶問(wen)題:“為(wei)什么在不(bu)同的(de)客戶端中(zhong),日期時(shi)間會(hui)相差13個小時(shi)?”執行的(de)insert語句SQL為(wei):
INSERT INTO n802344030600001_w21003 USING mnt_factor_item_data TAGS ("N802344030600001", 'w21003') VALUES ('2021-7-23 07:04:00:000',3, 999,'N802344030600002','w21003',19,'COD','mg/L',4,2,'大空港片區',1,'龍翔北路監測控制站',1,'龍翔北路水監測設備',3,'龍翔北路監測終端');
SQL中是以(yi)本地(di)日(ri)期時(shi)間的(de)格(ge)式表示時(shi)間戳的(de),客戶端使用(yong)了本地(di)的(de)timezone,將這個“2021-7-23 07:04:00:000”轉(zhuan)換(huan)為timestamp;在查詢時(shi),Windows上的(de)shell和Linux的(de)shell都會將timestamp,根據當前生效的(de)timezone,轉(zhuan)換(huan)成日(ri)期時(shi)間格(ge)式。
參考文獻:
1.
2.
3.
4.


























