如何在 PyArrow、Pandas 与 Spark 之间转换时间戳并理解时区丢失和精度截断
如何在 PyArrow、Pandas 与 Spark 之间转换时间戳并理解时区丢失和精度截断【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow当你在 PyArrow/Pandas 与 PySpark 之间来回传递包含时间列的 DataFrame 时转换后的时间戳会失去时区信息并被截断到微秒精度如果会话时区设置不当数值本身还会发生偏移。这篇文章基于 Apache Arrow 官方文档 docs/source/python/timestamps.rst 中的完整示例带你走一遍 Pandas ⇄ Spark经由 Apache Arrow的时间戳转换流程并解释每一步输出为什么是这样。适用的前提是你使用 PySpark 的 Arrow 集成即spark.sql.execution.arrow.enabled配置为true并且关心 aware带时区时间戳在往返转换中的行为。先看三方的时间戳存储模型理解转换结果的前提是知道各引擎内部怎么存时间Arrow时间戳存为 64 位整数靠列元数据关联时间单位毫秒ms、微秒us或纳秒ns并附带一个可选的时区。PandasTimestamp使用表示纳秒的 64 位整数同样带可选时区。Spark时间戳存为表示 UNIX 纪元以来微秒的 64 位整数不保存任何时区元数据。Spark 按session 时区即spark.sql.session.timeZone解释这些时间戳如果未设置该配置则回退到系统默认时区。由此可以推出往返转换的三个必然结果文档原文归纳时区信息丢失——从 Spark 转换回 Arrow/Pandas 的所有时间戳都是 time zone naive时间戳被截断到微秒Spark 内部精度会话时区的不同设置会对时间戳值的换算产生非直觉的影响。另外两个术语在文档中反复出现先对齐一下不带时区的时间戳类型称为Time Zone Naive带时区的称为Time Zone Aware。第一步准备一个包含 naive 和 aware 两列的 Pandas DataFrame用下面这段文档中的示例代码构造测试数据naive列是不带时区的datetimeaware列是带UTC-08:00时区的pandas.Timestamp且纳秒位为 500用来观察截断import pandas as pd from datetime import datetime, timedelta, timezone pdf pd.DataFrame({naive: [datetime(2019, 1, 1, 0)], aware: [pd.Timestamp(year2019, month1, day1, nanosecond500, tztimezone(timedelta(hours-8)))]}) pdf文档示例输出naive aware 0 2019-01-01 2019-01-01 00:00:00.000000500-08:00Pandas 时间列在转 Arrow 时对应TimestampArray带时区的时间戳会保留时区信息例如pandas.rst中的示例里datetime64[us, UTC]会转成timestamp[us, tzUTC]也就是说 Arrow 侧是能记住时区的——时区丢失发生在进入 Spark 之后。第二步设置会话时区并创建 Spark DataFrame在 Spark 侧创建 DataFrame 前需要先确认两件事spark.sql.execution.arrow.enabled已设为true本文所有 Spark 示例都基于该配置会话时区spark.sql.session.timeZone已显式设置否则 Spark 会用系统默认时区解释时间戳行为取决于运行环境。先以 UTC 会话时区为例以下show()输出均为文档示例输出from pyspark.sql import SparkSession spark SparkSession.builder.appName(MyApp).getOrCreate() spark.conf.set(spark.sql.session.timeZone, UTC) utc_df spark.createDataFrame(pdf) utc_df.show()-------------------------------------- | naive| aware| -------------------------------------- |2019-01-01 00:00:00|2019-01-01 08:00:00| --------------------------------------注意两点行为aware 列被平移显示00:00:00-08:00显示成了08:00:00。这不是数据变了而是同一时刻在 UTC 下的表示——时区换算到会话时区后时刻本身不变。naive 列按会话时区处理Spark 把 naive 时间戳当作系统本地时区的时间并换算到 UTC 存储。此时会话时区是 UTC所以显示不变这个行为在下一步换时区后会显现出影响。再切换到美西时区PST创建第二个 DataFramespark.conf.set(spark.sql.session.timeZone, US/Pacific) pst_df spark.createDataFrame(pdf) pst_df.show()-------------------------------------- | naive| aware| -------------------------------------- |2019-01-01 00:00:00|2019-01-01 00:00:00| --------------------------------------此时 aware 列不再显示平移同一时刻在 US/Pacific 下就是原始墙钟时间。但这时如果重新查看刚才在 UTC 会话时区下创建的utc_df会得到文档示例输出-------------------------------------- | naive| aware| -------------------------------------- |2018-12-31 16:00:00|2019-01-01 00:00:00| --------------------------------------这就是文档强调的会话时区对值换算的非直觉影响utc_df的 naive 列当初是按 UTC 解释的同一数值换到 PST 会话时区下显示时比pst_df的 naive 时刻早了 8 小时。也就是说naive 时间戳在不同会话时区下创建的 DataFrame 里实际代表不同的时刻。第三步转回 Pandas 并验证时区丢失在会话时区仍为 PST 的情况下把pst_df转回 Pandaspst_df.toPandas()文档示例输出naive aware 0 2019-01-01 2019-01-01用info()检查两列的 dtypepst_df.toPandas().info()文档示例输出class pandas.core.frame.DataFrame RangeIndex: 1 entries, 0 to 0 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 naive 1 non-null datetime64[ns] 1 aware 1 non-null datetime64[ns] dtypes: datetime64ns这就是第一个可核对的结论两列都变成了不带时区的datetime64[ns]。原始aware列在 Pandas 里是带UTC-08:00的Timestamp往返后时区信息已经不存在。进一步对比 epoch 偏移Spark 先转换到会话时区再 localise 掉时区信息结果是该时间戳比原始时刻早了 8 小时pst_df.toPandas()[aware][0]文档示例输出Timestamp(2019-01-01 00:00:00)与原始值对比pdf[aware][0]Timestamp(2019-01-01 00:00:00.000000500-0800, tzUTC-08:00)计算两者 epoch 差值小时(pst_df.toPandas()[aware][0].timestamp()-pdf[aware][0].timestamp())/3600文档示例输出-8.0也就是说会话时区为 PST 时aware 时间戳转回 Pandas 后比原始时刻少了 8 小时。而在会话时区为 UTC 时同一检查得到的差值是0.0文档示例输出——此时 aware 列不会发生这个意外平移但注意它同样变成了 time zone naivespark.conf.set(spark.sql.session.timeZone, UTC) pst_df.toPandas()[aware][0]Timestamp(2019-01-01 08:00:00)所以判断方法可以归纳为转换后先用info()确认列是否变成不带时区的datetime64[ns]再用上面的 epoch 差值公式确认数值偏移量是否符合会话时区与原始时区的差。精度截断在哪里发生示例中aware值带有nanosecond500原始值显示为00:00:00.000000500-08:00但进入 Spark 后show()与转回 Pandas 的结果中都只剩下00:00:00——纳秒部分被丢掉了这正是Timestamps are truncated to microseconds的具体体现Spark 内部精度是微秒任何微秒以下的分量在往返后不可恢复。文档对 Spark 往返部分没有提供额外的截断告警或绕过选项需要纳秒精度时应避免经过 Spark。如果你不走 Spark 的内存通道而是用 Parquet 文件在框架间交换数据PyArrow 文档 parquet_type_handling.rst 对时间戳写入给出了对应的控制项可以作为可选分支了解默认写 Parquet 1.0 文件时纳秒会被 cast 到微秒用coerce_timestampsms可指定目标精度pq.write_table(table, example.parquet, coerce_timestampsms)低精度 cast 可能丢数据时默认抛异常传allow_truncated_timestampsTrue可抑制pq.write_table(table, example.parquet, coerce_timestampsms, allow_truncated_timestampsTrue)Parquet 2.6 格式可以不 cast 保存纳秒时间戳但文档同时提醒许多 Parquet 读取器尚不支持该版本跨框架兼容时推荐默认的 1.0部分旧版 Spark以及 Impala使用已废弃的INT96存储时间戳如需向这些读取器写文件在write_table中设置use_deprecated_int96_timestampsTruepq.write_table(table, example.parquet, use_deprecated_int96_timestampsTrue)限制与结论结合 timestamps.rst 的说明这条集成路径有明确的边界经 Spark 往返后时间戳必然变成 time zone naive且被截断到微秒这两点无法通过配置规避。数值是否偏移、偏移多少取决于spark.sql.session.timeZone与原始时区的关系。示例中 PST 会话时区得到-8.0小时的 epoch 差UTC 会话时区得到0.0naive 时间戳在不同会话时区下创建时甚至代表不同时刻跨时区环境下尤其要小心。若需要保留时区或纳秒精度应绕过 Spark 的 Arrow 通道在 PyArrow/Pandas 侧直接处理或用支持timestamp[us, tz...]的 Parquet 2.6 格式做文件级交换注意读取器支持范围。【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Lighttools 8.4.0虚拟相机设置与3D显示实战指南

Lighttools 8.4.0虚拟相机设置与3D显示实战指南

用Lighttools 8.4.0做光学仿真也有一段时间了,平时用得最多的是照度图、光强图这类常规接收器结果。直到最近做光学显示项目,需要在特定观察位置判断目标面的照度分布和均匀性,才认真把“虚拟相机”这套流程完整走了一遍。折腾下来发现&#…

2026/11/10 15:29:57 阅读更多 →
3步拉起UI-TARS 1.5上线:vLLM部署实战与28 req/s吞吐冲关

3步拉起UI-TARS 1.5上线:vLLM部署实战与28 req/s吞吐冲关

3步拉起UI-TARS 1.5上线:vLLM部署实战与28 req/s吞吐冲关 【免费下载链接】UI-TARS Pioneering Automated GUI Interaction with Native Agents 项目地址: https://gitcode.com/GitHub_Trending/ui/UI-TARS 服务一启动就 CUDA out of memory,跑通…

2026/11/6 11:03:00 阅读更多 →
JasperReports报表开发:.jrxml与.jasper文件详解

JasperReports报表开发:.jrxml与.jasper文件详解

1. 理解.jrxml与.jasper文件的基础概念在报表开发领域,.jrxml和.jasper是两种密切相关的文件格式,它们共同构成了JasperReports报表系统的核心工作流。作为从业十余年的报表工程师,我经常需要向团队新人解释这两者的区别与联系。.jrxml文件本…

2026/10/25 10:20:00 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/10 23:32:01 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/10 23:32:03 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/11 2:31:33 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/10 16:14:15 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/10 12:51:50 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/10 23:32:10 阅读更多 →

日新闻

周新闻

月新闻