PySpark转换0001-01-01为TIMESTAMP后show正常collect报错问题
问题原因
show()方法正常运行的原因:Spark的show()操作完全在JVM侧完成时间格式化输出,不需要将Timestamp类型转换为Python原生的datetime对象,Spark JVM内部支持公元1年及更早的时间表示,因此可以正常输出0001-01-01 00:00:00。collect()报错的核心原因:- PySpark执行
collect()时,需要将JVM侧的Timestamp数据序列化后传递给Python进程,再反序列化为Python原生的datetime对象。 - Python标准库的
datetime模块不支持公元0年,可表示的最小合法时间为datetime.datetime(1, 1, 1, 0, 0)。 - 受时区转换逻辑、旧版本PySpark的时间转换缺陷影响,
0001-01-01对应的时间戳在转换为Pythondatetime时会被计算为公元0年的时间,直接触发范围报错。
- PySpark执行
解决方案
方案1:Spark侧提前转字符串后拉取
避免PySpark自动做类型转换,直接拉取格式化后的时间字符串,后续如果需要时间运算可以用支持更早时间的第三方库(如pendulum、arrow)自行解析:spark.sql("SELECT CAST(CAST('0001-01-01' AS TIMESTAMP) AS STRING) AS ts").collect()方案2:统一Spark会话时区为UTC
非UTC时区(如东八区UTC+8)会导致0001-01-01 00:00:00的本地时间对应UTC时间的公元0年12月31日,调整时区为UTC可避免该偏移问题:spark.conf.set("spark.sql.session.timeZone", "UTC") spark.sql("SELECT CAST('0001-01-01' AS TIMESTAMP)").collect()方案3:升级PySpark到3.1及以上版本
PySpark 3.1版本修复了早期版本中极早时间的转换逻辑缺陷,对公元1年附近的时间转换兼容性大幅提升。方案4:过滤超出范围的极早时间
如果业务不需要用到公元1年附近的极早时间,可以在查询阶段直接过滤掉会触发报错的数据:spark.sql(""" SELECT CAST('0001-01-01' AS TIMESTAMP) AS ts WHERE ts >= '0001-01-02' """).collect()
内容的提问来源于stack exchange,提问作者eduardo0
相关产品推荐
相关产品推荐

