小 T 導(dǎo)讀:很多新用戶在配置TDengine的時候,偶爾會因為配置了錯誤的時區(qū)(timezone),而導(dǎo)致寫入和查詢的時間出現(xiàn)錯位。今天希望這篇文章,能將日期時間、時間戳以及TDengine在寫入和查詢時處理時區(qū)的行為等描述清楚。
TDengine是濤思數(shù)據(jù)專為物聯(lián)網(wǎng)、車聯(lián)網(wǎng)、工業(yè)互聯(lián)網(wǎng)、IT運維等設(shè)計和優(yōu)化的大數(shù)據(jù)平臺,核心的時序數(shù)據(jù)庫在處理時序數(shù)據(jù)上有著十分優(yōu)異的性能。
一般來說,時序數(shù)據(jù)就是帶有時間序列屬性的數(shù)據(jù)。在處理時序數(shù)據(jù)時,TDengine有著自己獨特的方式。但是如果你沒有正確理解TDengine在寫入和查詢上的行為,極可能會因為配置了錯誤的時區(qū)(timezone),而導(dǎo)致寫入和查詢的時間出現(xiàn)錯位。
下面是一個真實用戶的例子:

從上圖中可以看到,用戶執(zhí)行的一條SQL寫入了“2021-07-23 07:04:00.000″這個時刻的數(shù)據(jù),可是在不同的客戶端中,查詢出的結(jié)果卻相差了13個小時。
今天希望通過這篇文章,將日期時間、時間戳以及TDengine在寫入和查詢時處理時區(qū)的行為等描述清楚,并給出如何設(shè)置timezone參數(shù)的意見,供大家參考。
在開始之前,你需要先了解以下這三點
1. TDengine中用時間戳表示日期時間,以標準的Unix元年時間(UTC時區(qū)1970年1月1日0點0分0秒)為原點,支持毫秒、微秒、納秒三種精度;
2. 在寫入時,如果SQL中是本地日期時間格式,TDengine的客戶端使用當前生效的timezone配置,將SQL中的日期時間轉(zhuǎn)換為timestamp;同時,也支持使用RFC-3339格式的日期時間進行寫入;
3. 在shell中查詢時,客戶端使用當前生效的timezone配置,將TDengine中存儲的timestamp轉(zhuǎn)換為日期時間格式進行顯示。
本文所用相關(guān)概念
- 本地日期時間:表示當?shù)氐娜掌跁r間。12:00是中午吃飯的時間,8:00是早上上班的時間,這是人類習(xí)慣的一種表示時間的方式,是不帶時區(qū)信息的日期和時間,可以當成一個String。例如:2021-07-21 12:00:00.000,表示2021年7月21日正午,時間精度以毫秒記,這個日期時間的表示方法,不帶任何時區(qū)信息。
- 時區(qū):地理概念,按照UTC/格林威治時區(qū),把地球劃分成向東和向西各12個時區(qū),其中東12區(qū)和西12區(qū)是一個區(qū)。時區(qū)可以通過’Asia/shanghai’這樣的’地區(qū)/城市’的方式表示,也可以用UTC偏移的方式表示。例如:UTC+8,代表東八區(qū),當協(xié)調(diào)世界時(UTC)時間為凌晨2點的時候,當?shù)氐臅r間為2+8點,即早上10點。
- RFC 3339:一種表示日期時間的標準格式。RFC 3339是帶時區(qū)信息的格式,即包含日期時間信息,也有時區(qū)信息。例如,以下兩個時間在地球上是同一時刻:2019-10-12T07:20:50+00:00,這個表示2019年10月12日,上午7點20分50秒(UTC+0時區(qū)),2019-10-12T15:20:50+08:00,這個表示2019年10月12日,下午3點20分50秒(UTC+8時區(qū))。
- 時間戳:是機器存儲和計算時間的方式。以Unix元年(UTC時區(qū)1970年1月1日0點0分0秒)開始經(jīng)過的秒數(shù)計算,不同精度的計時方式,可以有不同的時間戳。例如:0,表示UTC時區(qū)1970年1月1日凌晨的時間。
本地日期時間、時區(qū)信息、時間戳的關(guān)系可以參考下面這張圖:

TDengine如何處理日期時間?
寫入
如果在insert語句中,用一個String表示日期時間,插入到TDengine,存在著將這個String解析成timestamp的過程。這個String存在不同的格式,合法的格式包括:
(1)RFC 3339標準的表示方式
(2)yyyy-MM-dd hh:mm:ss
第1種情況——采用RFC 3339標準,那么這個String是帶時區(qū)信息的,可以明確地將其轉(zhuǎn)換成timestamp。例如:
這里,介紹一個小技巧:使用-r參數(shù)啟動taos shell時,timestamp類型的數(shù)據(jù),將會以時間戳(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)
可以看到,1970-01-01T08:00:00.000+08:00,代表UTC+8時區(qū)1970年1月1日上午8:00,這正好對應(yīng)UTC時區(qū)的凌晨,所以在timestamp是0。
第2種情況——在insert語句中使用yyyy-MM-dd hh:mm:ss格式的時間字符串,不含時區(qū)信息。這時,taos客戶端會采用當前timezone信息,將字符串轉(zhuǎn)化成timestamp。例如:
可以看到,1970-01-01T08:00:00.000+08:00,代表UTC+8時區(qū)1970年1月1日上午8:00,這正好對應(yīng)UTC時區(qū)的凌晨,所以在timestamp是0。
第2種情況——在insert語句中使用yyyy-MM-dd hh:mm:ss格式的時間字符串,不含時區(qū)信息。這時,taos客戶端會采用當前timezone信息,將字符串轉(zhuǎn)化成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語句使用了配置文件中的時區(qū)信息,和insert語句中的日期時間信息,即“1970-01-01 00:00:00+08:00”,這個值在時間戳中正好代表-28800000。
由此可見,在TDengine中,時間原點是國際通用的Unix元年(UTC時區(qū)1970年1月1日凌晨)。
查詢
# 在taos.cfg內(nèi)配置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)
可以看到,select語句在查詢時,依然存在著從ts轉(zhuǎn)換為一個string串的情況,Tdengine會將ts轉(zhuǎn)換成當前taos client中的時區(qū)。
Timezone配置為UTC-8
有些用戶不理解,為什么在TDengine中timezone會被配置為UTC-8?原因是,在POSIX標準中,表示時區(qū)偏移量的方式和地理的表示方式不一致。參考Wikipedia中的定義,在ISO 8601中,UTC+8為東八區(qū),該時區(qū)是以中文為主的時區(qū)。那在Unix中,東八區(qū)又應(yīng)該如何表示?請參考下面這個例子:
# 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數(shù)字時區(qū)(例如,-0400)%:z +hh:mm數(shù)字時區(qū)(例如,-04:00)%Z按字母表排序的時區(qū)縮寫 (例如,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
可見,在POSIX標準中,UTC-8代表東八區(qū),UTC+8代表西八區(qū)。這里與地理上表示時區(qū)的習(xí)慣是不一致的。在taos.cfg中,TDengine使用的是POSIX Timezone標準。
在JDBC中設(shè)置Timezone
在使用JDBC Connector連接TDengine時,可以通過3個途徑設(shè)置timezone參數(shù),分別為: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的配置參數(shù)Connection conn = DriverManager.getConnection("jdbc:TAOS://taosdemo.com:6030/test", connProps);
參考文檔: https://tdengine.com/docs/cn/v2.0/connector/java
總結(jié)
最后,我們再回顧一下前文中描述的用戶問題:“為什么在不同的客戶端中,日期時間會相差13個小時?”執(zhí)行的insert語句SQL為:
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,'大空港片區(qū)',1,'龍翔北路監(jiān)測控制站',1,'龍翔北路水監(jiān)測設(shè)備',3,'龍翔北路監(jiān)測終端');
SQL中是以本地日期時間的格式表示時間戳的,客戶端使用了本地的timezone,將這個“2021-7-23 07:04:00:000”轉(zhuǎn)換為timestamp;在查詢時,Windows上的shell和Linux的shell都會將timestamp,根據(jù)當前生效的timezone,轉(zhuǎn)換成日期時間格式。
參考文獻:
2. 理解RFC 3339標準
3. RFC 3339標準



互聯(lián)網(wǎng).png)



-1.png)










伙伴.png)
伙伴.png)
伙伴.png)



