Databricks Spark集群时区配置:Spark与系统设置冲突问题
问题解答
问题1:为什么部分时区设置下to_timestamp会修改日期?我将字符串转时间戳时不希望数据被改动。
to_timestamp解析不带时区标识的字符串时,会先按当前Spark会话时区将字符串转换为UTC时间戳(Spark内部时间戳统一存储为UTC毫秒数),当你调用collect将结果返回到Python端时,Python的datetime对象会自动读取操作系统本地时区,将UTC时间戳转换为本地时间字符串展示。两次时区转换的叠加就会导致你看到的时间和原始字符串不一致。
如果不希望时间被改动,需要保证Spark会话时区、操作系统时区、原始字符串所属时区三者完全一致,或者解析时明确指定字符串对应的时区,避免隐式转换。
问题2:为什么UTC+02:00和Europe/Amsterdam返回结果不同?看起来UTC+02:00不支持夏令时。
你的判断是对的,二者的时区规则完全不同:
UTC+02:00属于固定偏移量时区,全年恒定比UTC早2小时,没有任何夏令时调整规则Europe/Amsterdam属于区域时区,遵循欧盟夏令时规则:每年3月到10月执行夏令时(CEST,UTC+2),11月到次年2月执行冬令时(CET,UTC+1)
所以11月的日期用Europe/Amsterdam解析时会自动按UTC+1偏移,和固定+2的UTC+02:00结果自然不同。
问题3:为什么修改系统时区后结果发生变化?Spark除了自身配置外还会读取系统时区吗?为什么现在UTC+02:00支持夏令时而Europe/Amsterdam不支持?
Spark本身的计算逻辑仅读取spark.sql.session.timeZone配置,不会主动读取系统时区,结果变化的原因出在Python端的展示环节:你收集到的Spark返回值是UTC时间戳,Python的datetime展示时会调用系统时区将UTC时间戳转换为本地时间,系统时区修改后展示结果自然发生变化。
你观察到的UTC+02:00支持夏令时、Europe/Amsterdam不支持是错觉,实际是两次时区转换的抵消效果:当Spark会话时区和系统时区均为Europe/Amsterdam时,Spark先按Europe/Amsterdam将字符串转成UTC时间戳,Python又用Europe/Amsterdam把UTC时间戳转回来,两次转换刚好抵消,所以你看到的时间和原始字符串一致,并不是Europe/Amsterdam没有做夏令时转换。而UTC+02:00的场景下,Spark按固定+2偏移转UTC,Python用Europe/Amsterdam转回来,11月冬令时偏移为+1,就会出现1小时的差异,并不是UTC+02:00支持了夏令时。
正确配置方案
要同时满足Python日志时间正确、Spark时间转换正确、支持夏令时的需求,推荐使用集群全局配置,避免单节点修改失效:
1. 配置集群所有节点的系统时区
单独在Notebook中执行timedatectl仅能修改当前执行命令的节点,worker节点和集群重启后配置都会失效,需要通过集群初始化脚本统一配置:
- 新建初始化脚本(例如命名为
set_timezone.sh),内容如下:
#!/bin/bash timedatectl set-timezone Europe/Amsterdam
- 将脚本上传到DBFS路径,在集群配置的「初始化脚本」栏添加该脚本路径,重启集群后所有节点的系统时区都会永久生效。
2. 配置Spark全局时区
在集群的「Spark配置」栏添加以下参数,重启集群后所有Spark会话默认使用支持夏令时的Europe/Amsterdam时区:
spark.sql.session.timeZone Europe/Amsterdam spark.driver.extraJavaOptions -Duser.timezone=Europe/Amsterdam spark.executor.extraJavaOptions -Duser.timezone=Europe/Amsterdam
临时测试配置(仅单Notebook生效)
如果只是临时测试,不需要修改集群全局配置,可以在Notebook开头执行以下命令:
%sh timedatectl set-timezone Europe/Amsterdam
spark.conf.set("spark.sql.session.timeZone", "Europe/Amsterdam")
内容的提问来源于stack exchange,提问作者Matthijs van Kesteren

