此檔已淘汰,且可能不會更新。 不再支援此內容中提及的產品、服務或技術。 請參閱
日期時間模式
。
Date
和
Timestamp
資料類型在 Databricks Runtime 7.0 中大幅變更。 本文章說明:
類型和
Date
相關聯的行事曆。
類型
Timestamp
及其與時區的關聯性。 它也會說明時區位移解析的詳細資料,以及 Databricks Runtime 7.0 所使用之 JAVA 8 中新時間 API 的細微行為變更。
用來建構日期和時間值的 API。
在 Apache Spark 驅動程式上收集日期和時間物件的常見陷阱和最佳做法。
日期和行事曆
是
Date
年、月和日欄位的組合,例如 (year=2012、month=12、day=31) 。 不過,年、月和日欄位的值有條件約束,以確保日期值是真實世界中的有效日期。 例如,月的值必須介於 1 到 12 之間,日期的值必須介於 1 到 28、29、30 或 31 之間,或 31 (,視年份和月份) 而定,依此類故。 類型
Date
不會考慮時區。
欄位的條件
Date
約束是由許多可能的行事曆之一所定義。 某些像
月曆
一樣,只會在特定區域中使用。 有些像是
Julian 行事曆
,只會在歷程記錄中使用。 事實上,國際標準是
西曆
,幾乎用於世界各地的西曆,以供公民之用。 它已在 1582 中引進,並延伸至 1582 之前的支援日期。 此擴充行事曆稱為
Proceptic 西曆
。
Databricks Runtime 7.0 使用 Proceptic 西曆,此行事曆已供其他資料系統使用,例如 pandas、R 和 Apache Arrow。 Databricks Runtime 6.x 和以下使用 Julian 和西曆的組合:針對 1582 之前的日期,會使用 Julian 行事曆,用於 1582 之後的日期。 這是繼承自舊版
java.sql.Date
API,此 API 由 取代在 JAVA 8 中
java.time.LocalDate
,它會使用 Proceptic 西曆。
時間戳記和時區
此
Timestamp
類型會
Date
使用新的欄位來擴充類型:小時、分鐘、秒 (,其小數部分) ,以及全域 (會話範圍) 時區。 它會定義具象的時間立即。 例如, (year=2012、month=12、day=31、hour=23、minute=59、second=59.123456) 與會話時區 UTC+01:00。 將時間戳記值寫出到 Parquet 之類的非文字資料來源時,這些值只會立即 (,例如 UTC) 中沒有時區資訊的時間戳記。 如果您使用不同的會話時區來寫入和讀取時間戳記值,您可能會看到小時、分鐘和秒欄位的不同值,但它們是相同的具象時間立即。
小時、分鐘和秒欄位有標準範圍:0-23,小時和 0-59 則為分鐘和秒。 Spark 支援最多小數秒的精確度。 分數的有效範圍是從 0 到 999,999 微秒。
在任何具體立即,視時區而定,您可以觀察許多不同的時鐘值:
相反地,時鐘值可以代表許多不同的時間瞬間。
時區位移
可讓您明確將本機時間戳系結至時間立即。 時區位移通常定義為從 Greenwich Mean Time (GMT) 或
UTC+0
(
國際標準時間
) 的時差。 這個時區資訊的標記法會消除模棱兩可,但它不方便。 大部分的人都偏好指出 或
Europe/Paris
之類的
America/Los_Angeles
位置。 這個來自區域位移的額外抽象層級讓生活更容易,但帶來複雜度。 例如,您現在必須維護特殊的時區資料庫,以將時區名稱對應至位移。 由於 Spark 在 JVM 上執行,它會委派對應至 JAVA 標準程式庫,它會從
網際網路指派號碼授權單位時區資料庫
載入資料, (IANA TZDB) 。 此外,JAVA 標準程式庫中的對應機制有一些影響 Spark 行為的細微差異。
由於 JAVA 8,JDK 會針對日期時間操作和時區位移解析公開不同的 API,而 Databricks Runtime 7.0 會使用此 API。 雖然時區名稱與位移的對應具有相同的來源,但 IANA TZDB 在 JAVA 8 和更新版本中會以不同的方式實作,相較于 JAVA 7。
例如,查看時區中 1883
America/Los_Angeles
年之前的時間戳記:
1883-11-10 00:00:00
。 今年因 1883 年 11 月 18 日,所有北美洲機場都切換為新的標準時間系統,因此從其他人中脫穎而出。 使用 JAVA 7 時間 API,您可以在本機時間戳取得時區位移,如下所示
-08:00
:
java.time.ZoneId.systemDefault
res0:java.time.ZoneId = America/Los_Angeles
java.sql.Timestamp.valueOf("1883-11-10 00:00:00").getTimezoneOffset / 60.0
res1: Double = 8.0
對等的 JAVA 8 API 會傳回不同的結果:
java.time.ZoneId.of("America/Los_Angeles").getRules.getOffset(java.time.LocalDateTime.parse("1883-11-10T00:00:00"))
res2: java.time.ZoneOffset = -07:52:58
在 1883 年 11 月 18 日之前,北美洲中的一天時間是一個當地問題,而大部分城市和城市都使用了某種形式的當地太陽能時間,由知名的時鐘 (維護,例如,或在) 的視窗。 這就是您看到這種奇怪時區位移的原因。
此範例示範 JAVA 8 函式更精確,並考慮 IANA TZDB 的歷史資料。 切換至 JAVA 8 時間 API 之後,Databricks Runtime 7.0 會自動受益于改進,並更精確地解析時區位移。
Databricks Runtime 7.0 也切換至類型的 Proceptic 西曆 Timestamp 。 ISO SQL:2016標準宣告時間戳記的有效範圍是從 0001-01-01 00:00:00 到 9999-12-31 23:59:59.999999 。 Databricks Runtime 7.0 完全符合標準,並支援此範圍中的所有時間戳記。 相較于 Databricks Runtime 6.x 和以下版本,請注意下列子範圍:
0001-01-01 00:00:00..1582-10-03 23:59:59.999999. Databricks Runtime 6.x 和以下版本使用 Julian 行事曆,且不符合標準。 Databricks Runtime 7.0 會修正問題,並在時間戳記的內部作業中套用 Proceptic 西曆,例如取得年、月、日等。由於不同的行事曆,Databricks Runtime 6.x 和以下部分日期不存在於 Databricks Runtime 7.0 中。 例如,1000-02-29 不是有效的日期,因為 1000 不是西曆中的閏年。 此外,Databricks Runtime 6.x 和更新版本會將時區名稱解析為此時間戳記範圍的時區位移不正確。
1582-10-04 00:00:00..1582-10-14 23:59:59.999999. 這是 Databricks Runtime 7.0 中本機時間戳的有效範圍,與 Databricks Runtime 6.x 和以下的這類時間戳記不存在。
1582-10-15 00:00:00..1899-12-31 23:59:59.999999. Databricks Runtime 7.0 會使用 IANA TZDB 的歷史資料正確解析時區位移。 相較于 Databricks Runtime 7.0,Databricks Runtime 6.x 和以下可能會在某些情況下,解析時區名稱的區域位移不正確,如上述範例所示。
1900-01-01 00:00:00..2036-12-31 23:59:59.999999. Databricks Runtime 7.0 和 Databricks Runtime 6.x 和以下都符合 ANSI SQL 標準,並在日期時間作業中使用西曆,例如取得月份的日期日。
2037-01-01 00:00:00..9999-12-31 23:59:59.999999. Databricks Runtime 6.x 和更新版本可以解析時區位移和日光節約時間位移不正確。 Databricks Runtime 7.0 不會。
將時區名稱對應至位移的一個層面是因日光節約時間 (DST) 或切換至另一個標準時區位移而可能發生的本機時間戳重迭。 例如,在 2019 年 11 月 3 日,02:00:00,美國的大部分州將時鐘往後 1 小時轉回 01:00:00。 本機時間戳 2019-11-03 01:30:00 America/Los_Angeles 可以對應至 2019-11-03 01:30:00 UTC-08:00 或 2019-11-03 01:30:00 UTC-07:00 。 如果您未指定位移,而且只要設定時區名稱 (,例如 2019-11-03 01:30:00 America/Los_Angeles ,) ,Databricks Runtime 7.0 會採用先前的位移,通常對應至「summer」。 行為會從 Databricks Runtime 6.x 和以下進行差異,這會採用「冷」位移。 在間距的情況下,時鐘向前跳躍,沒有有效的位移。 針對典型的一小時日光節約時間變更,Spark 會將這類時間戳記移至對應至「日光」時間的下一個有效時間戳記。
如先前範例所示,時區名稱與位移的對應模棱兩可,而且不是一對一。 在可能的情況下,建構時間戳記時,我們建議指定確切的時區位移,例如 2019-11-03 01:30:00 UTC-07:00 。
ANSI SQL 和 Spark SQL 時間戳記
ANSI SQL 標準會定義兩種類型的時間戳記:
TIMESTAMP WITHOUT TIME ZONE 或 TIMESTAMP :本機時間戳, (YEAR 、 MONTH 、 DAY 、 HOUR 、、) MINUTESECOND 。 這些時間戳記不會系結至任何時區,而且是時鐘時間戳記。
TIMESTAMP WITH TIME ZONE:區域時間戳記, (、、、、、、、 TIMEZONE_HOUR) TIMEZONE_MINUTE 。 SECONDHOURMINUTEDAYMONTHYEAR 這些時間戳記代表 UTC 時區 + 時區位移 (以小時和分鐘為單位的立即,) 與每個值相關聯。
的 TIMESTAMP WITH TIME ZONE 時區位移不會影響時間戳記所代表的實體時間點,因為該時間點完全由其他時間戳記元件所提供的 UTC 時間立即表示。 相反地,時區位移只會影響顯示時間戳記值的預設行為、日期/時間元件擷取 (例如, EXTRACT) ,以及其他需要知道時區的作業,例如將月份新增至時間戳記。
Spark SQL 會將時間戳記類型 TIMESTAMP WITH SESSION TIME ZONE 定義為 ,這是欄位 (YEAR 、 MONTH 、 SECONDHOURDAYMINUTE 、 SESSION TZ) 的組合,其中 YEAR ,透過 SECOND 欄位識別 UTC 時區的時間立即,以及從 SQL config spark.sql.session.timeZone 取得 SESSION TZ。 會話時區可以設定為:
區域位移 (+|-)HH:mm 。 此表單可讓您明確定義實體時間點。
時區名稱,格式為區域識別碼 area/city ,例如 America/Los_Angeles 。 這種形式的時區資訊會受到先前描述的一些問題所影響,例如重迭的本機時間戳。 不過,每個 UTC 時間立即明確與任何區域識別碼的一個時區位移相關聯,因此,每個具有區域識別碼型時區的時間戳記可以明確轉換成具有區域位移的時間戳記。 根據預設,會話時區會設定為 JAVA 虛擬機器的預設時區。
Spark TIMESTAMP WITH SESSION TIME ZONE 與下列不同:
TIMESTAMP WITHOUT TIME ZONE,因為這個類型的值可以對應到多個實體時間實例,但 的任何值 TIMESTAMP WITH SESSION TIME ZONE 都是具象的實體時間立即。 SQL 類型可以使用所有會話的一個固定時區位移來模擬,例如 UTC+0。 在此情況下,您可以將 UTC 的時間戳記視為本機時間戳。
TIMESTAMP WITH TIME ZONE,因為根據類型的 SQL 標準資料行值可能會有不同的時區位移。 Spark SQL 不支援。
您應該注意到,與全域 (會話範圍) 時區相關聯的時間戳記不是 Spark SQL 新發明的時間戳記。 Oracle 之類的 RDBMS 會提供類似時間戳記的類型: TIMESTAMP WITH LOCAL TIME ZONE 。
建構日期和時間戳記
Spark SQL 提供一些建構日期和時間值的方法:
不含參數的預設建構函式: CURRENT_TIMESTAMP() 和 CURRENT_DATE() 。
從其他基本 Spark SQL 類型,例如 INT 、 LONG 和 STRING
從 Python 日期時間或 JAVA 類別 java.time.LocalDate/Instant 等外部類型。
從 CSV、JSON、Avro、Parquet、ORC 等資料來源還原序列化。
Databricks Runtime 7.0 中引進的函 MAKE_DATE 式會採用三個 DATE 參數: YEAR 、 MONTH 和 DAY ,並建構值。 所有輸入參數都會盡可能隱含地轉換成 INT 類型。 函式會檢查產生的日期是否為 Proceptic 西曆中的有效日期,否則會傳 NULL 回 。 例如:
spark.createDataFrame([(2020, 6, 26), (1000, 2, 29), (-44, 1, 1)],['Y', 'M', 'D']).createTempView('YMD')
df = sql('select make_date(Y, M, D) as date from YMD')
df.printSchema()
|-- date: date (nullable = true)
若要列印 DataFrame 內容,請呼叫 show() 動作,此動作會將日期轉換成執行程式上的字串,並將字串傳送至驅動程式,以在主控台上輸出這些字串:
df.show()
+-----------+
| date|
+-----------+
| 2020-06-26|
| null|
|-0044-01-01|
+-----------+
同樣地,您可以使用 函 MAKE_TIMESTAMP 式來建構時間戳記值。 就像 一樣 MAKE_DATE ,它會針對日期欄位執行相同的驗證,並額外接受時間欄位 HOUR (0-23) 、MINUTE (0-59) 和 SECOND (0-60) 。 SECOND 的類型為 Decimal (precision = 8,小數位數 = 6) ,因為秒數部分最多可以微秒有效位數傳遞。 例如:
df = spark.createDataFrame([(2020, 6, 28, 10, 31, 30.123456), \
(1582, 10, 10, 0, 1, 2.0001), (2019, 2, 29, 9, 29, 1.0)],['YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND'])
df.show()
+----+-----+---+----+------+---------+
|YEAR|MONTH|DAY|HOUR|MINUTE| SECOND|
+----+-----+---+----+------+---------+
|2020| 6| 28| 10| 31|30.123456|
|1582| 10| 10| 0| 1| 2.0001|
|2019| 2| 29| 9| 29| 1.0|
+----+-----+---+----+------+---------+
df.selectExpr("make_timestamp(YEAR, MONTH, DAY, HOUR, MINUTE, SECOND) as MAKE_TIMESTAMP")
ts.printSchema()
|-- MAKE_TIMESTAMP: timestamp (nullable = true)
至於日期,請使用 show () 巨集指令來列印 ts DataFrame 的內容。 以類似的方式,將時間戳記轉換成字串, show() 但現在會將 SQL 設定 spark.sql.session.timeZone 所定義的會話時區納入考慮。
ts.show(truncate=False)
+--------------------------+
|MAKE_TIMESTAMP |
+--------------------------+
|2020-06-28 10:31:30.123456|
|1582-10-10 00:01:02.0001 |
|null |
+--------------------------+
Spark 無法建立最後一個時間戳記,因為此日期無效:2019 不是閏年。
您可能會注意到上述範例中沒有時區資訊。 在此情況下,Spark 會從 SQL spark.sql.session.timeZone 設定取得時區,並將其套用至函式調用。 您也可以將它傳遞為 的最後一個參數 MAKE_TIMESTAMP ,以挑選不同的時區。 範例如下:
df = spark.createDataFrame([(2020, 6, 28, 10, 31, 30, 'UTC'),(1582, 10, 10, 0, 1, 2, 'America/Los_Angeles'), \
(2019, 2, 28, 9, 29, 1, 'Europe/Moscow')], ['YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'TZ'])
df = df.selectExpr('make_timestamp(YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, TZ) as MAKE_TIMESTAMP')
df = df.selectExpr("date_format(MAKE_TIMESTAMP, 'yyyy-MM-dd HH:mm:ss VV') AS TIMESTAMP_STRING")
df.show(truncate=False)
+---------------------------------+
|TIMESTAMP_STRING |
+---------------------------------+
|2020-06-28 13:31:00 Europe/Moscow|
|1582-10-10 10:24:00 Europe/Moscow|
|2019-02-28 09:29:00 Europe/Moscow|
+---------------------------------+
如範例所示,Spark 會將指定的時區納入考慮,但會將所有本機時間戳調整為會話時區。 傳遞至 MAKE_TIMESTAMP 函式的原始時區會遺失, TIMESTAMP WITH SESSION TIME ZONE 因為類型會假設所有值都屬於一個時區,而且甚至不會為每個值儲存時區。 根據 的定義 TIMESTAMP WITH SESSION TIME ZONE ,Spark 會將本機時間戳儲存在 UTC 時區,並在擷取日期時間欄位或將時間戳記轉換為字串時使用會話時區。
此外,您可以使用轉換從 LONG 類型建構時間戳記。 如果 LONG 資料行包含自 epoch 1970-01-01 00:00:00Z 以來的秒數,它可以轉換成 Spark SQL TIMESTAMP :
select CAST(-123456789 AS TIMESTAMP);
1966-02-02 05:26:51
可惜的是,這種方法不允許您指定秒數的小數部分。
另一種方式是從型別的值 STRING 建構日期和時間。 您可以使用特殊關鍵字建立常值:
select timestamp '2020-06-28 22:17:33.123456 Europe/Amsterdam', date '2020-07-01';
2020-06-28 23:17:33.123456 2020-07-01
或者,您可以使用轉換來套用至資料行中的所有值:
select cast('2020-06-28 22:17:33.123456 Europe/Amsterdam' as timestamp), cast('2020-07-01' as date);
2020-06-28 23:17:33.123456 2020-07-01
如果輸入字串中省略時區,輸入時間戳記字串會解譯為指定時區或會話時區中的本機時間戳。 具有異常模式的字串可以使用 函式轉換成時間戳記 to_timestamp() 。 支援的模式會在 格式化和剖析的日期時間模式中說明:
select to_timestamp('28/6/2020 22.17.33', 'dd/M/yyyy HH.mm.ss');
2020-06-28 22:17:33
如果您未指定模式,函式的行為類似于 CAST 。
為了方便使用,Spark SQL 會在接受字串並傳回時間戳記或日期的所有方法中辨識特殊字元串值:
epoch 是日期 1970-01-01 或時間戳記 1970-01-01 00:00:00Z 的別名。
now 是會話時區的目前時間戳記或日期。 在單一查詢中,它一律會產生相同的結果。
today 是型別的目前日期 TIMESTAMP 開頭,或只是型別的 DATE 目前日期。
tomorrow 是時間戳記的下一天開始,或只是類型的下一天 DATE 。
yesterday 是目前類型之一或其開頭之前的 TIMESTAMP 日期。
select timestamp 'yesterday', timestamp 'today', timestamp 'now', timestamp 'tomorrow';
2020-06-27 00:00:00 2020-06-28 00:00:00 2020-06-28 23:07:07.18 2020-06-29 00:00:00
select date 'yesterday', date 'today', date 'now', date 'tomorrow';
2020-06-27 2020-06-28 2020-06-28 2020-06-29
Spark 可讓您從驅動程式端的現有外部物件集合建立 Datasets ,並建立對應類型的資料行。 Spark 會將外部類型的實例轉換成語意相等的內部表示。 例如,若要從 Python 集合建立具有 DATE 和 TIMESTAMP 資料行的 Dataset ,您可以使用:
import datetime
df = spark.createDataFrame([(datetime.datetime(2020, 7, 1, 0, 0, 0), datetime.date(2020, 7, 1))], ['timestamp', 'date'])
df.show()
+-------------------+----------+
| timestamp| date|
+-------------------+----------+
|2020-07-01 00:00:00|2020-07-01|
+-------------------+----------+
PySpark 會使用系統時區,將 Python 的日期時間物件轉換為驅動程式端的內部 Spark SQL 標記法,這與 Spark 的會話時區設定 spark.sql.session.timeZone 不同。 內部值不包含原始時區的相關資訊。 根據類型定義,平行化日期和時間值的未來作業只會考慮 Spark SQL 會話時區 TIMESTAMP WITH SESSION TIME ZONE 。
以類似的方式,Spark 會將下列類型辨識為 JAVA 和 Scala API 中的外部日期時間類型:
java.sql.Date 和 java.time.LocalDate 作為型別 DATE 的外部類型
java.sql.Timestamp型別的 TIMESTAMP 和 java.time.Instant 。
和 java.time.* 類型之間 java.sql.* 有差異。 java.time.LocalDate 和 java.time.Instant 已新增至 JAVA 8,且類型是以 Proceptic 西曆為基礎,也就是 Databricks Runtime 7.0 和更新版本所使用的相同行事曆。 java.sql.Date 且 java.sql.Timestamp 在混合式行事曆下方有另一個行事曆 (Julian + Gregorian,自 1582-10-15) ,這與 Databricks Runtime 6.x 和以下使用的舊版行事曆相同。 由於不同的行事曆系統,Spark 必須在轉換至內部 Spark SQL 標記法期間執行其他作業,並將輸入日期/時間戳記從一個行事曆重新設定為另一個行事曆。 在 1900 年之後,重新基底作業對新式時間戳記有一些額外負荷,而且對於舊時間戳記而言可能更為重要。
下列範例示範如何從 Scala 集合建立時間戳記。 第一個 java.sql.Timestamp 範例會從字串建構 物件。 方法 valueOf 會將輸入字串解譯為預設 JVM 時區中的本機時間戳,這與 Spark 的會話時區不同。 如果您需要在特定時區建構 或 java.sql.Date 的 java.sql.Timestamp 實例,請查看java.text.SimpleDateFormat (及其方法 setTimeZone) 或java.util.Calendar。
Seq(java.sql.Timestamp.valueOf("2020-06-29 22:41:30"), new java.sql.Timestamp(0)).toDF("ts").show(false)
+-------------------+
|ts |
+-------------------+
|2020-06-29 22:41:30|
|1970-01-01 03:00:00|
+-------------------+
Seq(java.time.Instant.ofEpochSecond(-12219261484L), java.time.Instant.EPOCH).toDF("ts").show
+-------------------+
| ts|
+-------------------+
|1582-10-15 11:12:13|
|1970-01-01 03:00:00|
+-------------------+
同樣地,您可以從 或 java.sql.LocalDate 的 java.sql.Date 集合建立資料 DATE 行。 實例的 java.sql.LocalDate 平行處理與 Spark 的會話或 JVM 預設時區完全無關,但實例的平行處理 java.sql.Date 並不相同。 有細微差異:
java.sql.Date 實例代表驅動程式上預設 JVM 時區的本機日期。
若要正確轉換成 Spark SQL 值,驅動程式和執行程式上的預設 JVM 時區必須相同。
Seq(java.time.LocalDate.of(2020, 2, 29), java.time.LocalDate.now).toDF("date").show
+----------+
| date|
+----------+
|2020-02-29|
|2020-06-29|
+----------+
若要避免任何行事曆和時區相關問題,我們建議使用 JAVA 8 類型作為外部類型 java.sql.LocalDate/Instant ,以平行方式將時間戳記或日期的 JAVA/Scala 集合平行化。
收集日期和時間戳記
平行處理的反向作業是從執行程式收集到驅動程式的日期和時間戳記,並傳回外部類型的集合。 例如,您可以使用 動作將 提取 DataFrame 回驅動程式 collect() :
df.collect()
[Row(timestamp=datetime.datetime(2020, 7, 1, 0, 0), date=datetime.date(2020, 7, 1))]
Spark 會將日期和時間資料行的內部值,當做 UTC 時區中的時間瞬間從執行程式傳輸到驅動程式,並在驅動程式的系統時區中執行 Python 日期時間物件的轉換,而不是使用 Spark SQL 會話時區。 collect() 不同于 show() 上一節所述的動作。 show() 會在將時間戳記轉換成字串時使用會話時區,並在驅動程式上收集結果字串。
在 JAVA 和 Scala API 中,Spark 預設會執行下列轉換:
Spark SQL DATE 值會轉換成 的 java.sql.Date 實例。
Spark SQL TIMESTAMP 值會轉換成 的 java.sql.Timestamp 實例。
這兩個轉換都是在驅動程式的預設 JVM 時區中執行。 如此一來,若要擁有您可以使用 、 等等取得 Date.getDay() 的相同日期時間欄位,以及使用 Spark SQL 函 DAY 式 、 HOUR 驅動程式上的預設 JVM 時區,以及執行程式上的會話時區應該 getHour() 相同。
類似于從 java.sql.Date/Timestamp 建立日期/時間戳記,Databricks Runtime 7.0 會執行從專業西曆到混合式行事曆 (Julian + 西曆) 。 在 1582 年) 之後 (新式日期,這項作業幾乎是免費的,而在 1900 年) 之後 (時間戳記,但可能會帶來一些額外負荷。
您可以避免這類行事曆相關問題,並要求 Spark 傳回 java.time 自 JAVA 8 以來新增的類型。 如果您將 SQL 組態 spark.sql.datetime.java8API.enabled 設定為 true,動作會 Dataset.collect() 傳回:
java.time.LocalDate 針對 Spark SQL DATE 類型
java.time.Instant 針對 Spark SQL TIMESTAMP 類型
現在,轉換不會受到行事曆相關問題的影響,因為 JAVA 8 類型和 Databricks Runtime 7.0 和更新版本都是以專業西曆為基礎。 此 collect() 動作不相依于預設 JVM 時區。 時間戳記轉換完全不相依于時區。 日期轉換會使用 SQL 設定 spark.sql.session.timeZone 中的會話時區。 例如,請考慮使用 和 資料行,並將預設 JVM 時區設定為 Europe/Moscow ,並將會話時區設定為 America/Los_Angeles 。 TIMESTAMPDATEDataset
java.util.TimeZone.getDefault
res1: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="Europe/Moscow",...]
spark.conf.get("spark.sql.session.timeZone")
res2: String = America/Los_Angeles
df.show
+-------------------+----------+
| timestamp| date|
+-------------------+----------+
|2020-07-01 00:00:00|2020-07-01|
+-------------------+----------+
動作 show() 會在會話時間 America/Los_Angeles 列印時間戳記,但如果您收集 Dataset ,則會將 它轉換成 java.sql.Timestamp ,而且 toString 方法會列印 Europe/Moscow :
df.collect()
res16: Array[org.apache.spark.sql.Row] = Array([2020-07-01 10:00:00.0,2020-07-01])
df.collect()(0).getAs[java.sql.Timestamp](0).toString
res18: java.sql.Timestamp = 2020-07-01 10:00:00.0
實際上,本機時間戳 2020-07-01 00:00:00 是 2020-07-01T07:00:00Z 于 UTC。 如果您啟用 JAVA 8 API 並收集資料集,您可以觀察:
df.collect()
res27: Array[org.apache.spark.sql.Row] = Array([2020-07-01T07:00:00Z,2020-07-01])
您可以將 java.time.Instant 物件轉換成與全域 JVM 時區無關的任何本機時間戳。 這是 優於 的 java.time.Instantjava.sql.Timestamp 其中一個優點。 前者需要變更全域 JVM 設定,這會影響相同 JVM 上的其他時間戳記。 因此,如果您的應用程式處理不同時區的日期或時間戳記,而且應用程式在使用 JAVA 或 Scala Dataset.collect() API 收集資料至驅動程式時不應彼此衝突,建議您使用 SQL 組態 spark.sql.datetime.java8API.enabled 切換至 JAVA 8 API。