You need to enable JavaScript to run this app. 为何Spark 3写入1900年前Timestamp未抛SparkUpgradeException,1582年前Date却会?
优惠活动
大模型
产品
解决方案
定价
更多

为何Spark 3写入1900年前Timestamp未抛SparkUpgradeException,1582年前Date却会?

问题背景

我们可以查阅到如下技术说明:

从Parquet文件中读取1582-10-15之前的日期或1900-01-01T00:00:00Z之前的时间戳可能存在歧义,因为这些文件可能由Spark 2.x或旧版本Hive写入,后者使用的传统混合日历与Spark 3.0+的Proleptic Gregorian日历不同。

复现场景

首先确认当前写入时的重定基配置为EXCEPTION:

scala> spark.conf.get("spark.sql.legacy.parquet.datetimeRebaseModeInWrite")
res27: String = EXCEPTION

写入1899年的时间戳时未抛出异常:

scala> Seq(java.sql.Timestamp.valueOf("1899-01-01 00:00:00")).toDF("col").write.parquet("/tmp/someDate")
// 执行成功,无报错

但写入1581年的日期时,直接抛出升级异常:

scala> Seq(java.sql.Date.valueOf("1581-01-01")).toDF("col").write.parquet("/tmp/someOtherDate")
21/03/10 19:07:19 ERROR Utils: Aborting task
org.apache.spark.SparkUpgradeException: You may get a different result due to the upgrading of Spark 3.0: writing dates before 1582-10-15 or timestamps before 1900-01-01T00:00:00Z into Parquet files can be dangerous, as the files may be read by Spark 2.x or legacy versions of Hive later, which uses a legacy hybrid calendar that is different from Spark 3.0+'s Proleptic Gregorian calendar. See more details in SPARK-31404. You can set spark.sql.legacy.parquet.datetimeRebaseModeInWrite to 'LEGACY' to rebase the datetime values w.r.t. the calendar difference during writing, to get maximum interoperability. Or set spark.sql.legacy.parquet.datetimeRebaseModeInWrite to 'CORRECTED' to write the datetime values as it is, if you are 100% sure that the written files will only be read by Spark 3.0+ or other systems that use Proleptic Gregorian calendar.

行为差异原因

两种场景的差异核心是Spark对Date类型和Timestamp类型的重定基校验逻辑、底层存储格式的兼容性边界不同,具体拆解如下:

  • 两套日历的偏差范围:Spark 2.x及旧Hive使用的混合日历,仅在1582-10-15之前和Proleptic Gregorian日历存在计算偏差。1582-10-15是格里高利历正式推行的日期,之后的日期计算规则两套日历完全一致,不存在任何转换歧义。
  • Date类型的校验逻辑:Date是纯日历日期,不涉及时区、时分秒偏移,只要日期早于1582-10-15,两套日历的实际天数计算就会存在差异。因此EXCEPTION模式下会直接触发校验抛错,也就是写入1581年日期时的行为。
  • Timestamp类型的校验逻辑:官方文档提及的1900-01-01T00:00:00Z阈值,仅针对旧版Parquet的INT96格式Timestamp。INT96是为兼容旧Hive设计的存储格式,以1900-01-01为天数计算起点,因此早于该时间的INT96格式Timestamp会存在存储歧义。但Spark 3.0+默认写入的Timestamp是INT64格式(TIMESTAMP_MILLIS/TIMESTAMP_MICROS),这种格式以1970-01-01 UTC为epoch起点存储偏移量,1582-10-15之后的Timestamp两套日历计算的偏移量完全一致,不存在歧义。因此你写入的1899年Timestamp(晚于1582-10-15),即使早于1900-01-01,也不会触发EXCEPTION校验。
  • 补充验证:如果手动配置spark.sql.parquet.outputTimestampType=INT96开启旧格式Timestamp写入,再写入1899年的Timestamp时,就会正常抛出异常。

内容的提问来源于stack exchange,提问作者IntrestedInSpark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 13:15:04