PySpark拆分日期时间字符串为日期和时间时遇null值问题排查
问题分析与解决
原始数据
读取的CSV数据如下:
| PurchaseDatetime | PurchaseId |
|---|---|
| 29/08/2020 10:09:01 | 9 |
| 5/10/2020 7:02 | 4 |
| 5/10/2020 9:00 | 6 |
| 20/06/2020 02:11:36 | 4 |
| 23/10/2020 07:02:15 | 3 |
| 6/2/2020 10:10 | 7 |
第2、3、6行的PurchaseDatetime缺少秒部分,格式与其他行不一致。
尝试的代码
我用以下代码清洗数据并拆分日期和时间列:
df=df.withColumn("PurchaseDate",to_date(col("PurchaseDatetime"),"dd/MM/yyyy HH:mm:ss")).withColumn("PurchaseTime",date_format("PurchaseDatetime","dd/MM/yyyy HH:mm:ss a"))
期望输出
希望得到如下格式的结果:
| PurchaseDate | PurchaseTime | PurchaseId | PurchaseDatetime |
|---|---|---|---|
| 29-08-2020 | 10:09:01 | 9 | 29/08/2022 10:09:01 |
| 05-10-2020 | 07:02:00 | 4 | 5/10/2020 7:02 |
| 05-10-2020 | 09:00:00 | 6 | 5/10/2020 9:00 |
| 20-06-2020 | 02:11:36 | 4 | 20/06/2020 02:11:36 |
| 23-10-2020 | 07:02:15 | 3 | 23/10/2020 07:02:15 |
| 06-02-2020 | 10:10:00 | 7 | 6/2/2020 10:10 |
实际结果
但运行后PurchaseTime列全部为null:
| PurchaseDate | PurchaseTime | PurchaseId | PurchaseDatetime |
|---|---|---|---|
| 29-08-2020 | null | 9 | 29/08/2020 10:09:01 |
| 05-10-2020 | null | 4 | 5/10/2020 7:02 |
| 05-10-2020 | null | 6 | 5/10/2020 9:00 |
| 20-06-2020 | null | 4 | 20/06/2020 02:11:36 |
| 23-10-2020 | null | 3 | 23/10/2020 07:02:15 |
| 06-02-2020 | null | 7 | 6/2/2020 10:10 |
原因分析
date_format参数类型错误:date_format要求第一个参数是时间戳类型(TimestampType),但你直接传入了字符串类型的PurchaseDatetime列,Spark无法直接解析字符串格式的时间,因此返回null。- 时间格式不统一:
to_date仅指定了带秒的格式dd/MM/yyyy HH:mm:ss,虽然部分行能兼容解析,但本质上未完成统一的时间类型转换,后续拆分时间的操作自然失效。 - 格式字符串不符合需求:你使用的
dd/MM/yyyy HH:mm:ss a包含日期部分和AM/PM标识,和你想要的仅时间部分的输出不匹配。
解决方法
正确步骤是先将字符串统一转换为时间戳类型,再拆分日期和时间列:
方法1:多格式解析时间戳(Spark 3.0+支持)
from pyspark.sql import functions as F # 转换为时间戳,同时支持两种格式 df = df.withColumn("ts", F.to_timestamp(F.col("PurchaseDatetime"), ["dd/MM/yyyy HH:mm:ss", "dd/MM/yyyy HH:mm"])) # 拆分日期和时间列 df = df.withColumn("PurchaseDate", F.date_format(F.col("ts"), "dd-MM-yyyy")) \ .withColumn("PurchaseTime", F.date_format(F.col("ts"), "HH:mm:ss")) \ .drop("ts")
方法2:判断格式后分别解析(兼容低版本Spark)
from pyspark.sql import functions as F # 判断字符串是否包含秒部分,分别解析为时间戳 df = df.withColumn("ts", F.when(F.col("PurchaseDatetime").contains(":"), F.to_timestamp(F.col("PurchaseDatetime"), "dd/MM/yyyy HH:mm:ss")) .otherwise(F.to_timestamp(F.col("PurchaseDatetime"), "dd/MM/yyyy HH:mm"))) # 拆分日期和时间列 df = df.withColumn("PurchaseDate", F.date_format(F.col("ts"), "dd-MM-yyyy")) \ .withColumn("PurchaseTime", F.date_format(F.col("ts"), "HH:mm:ss")) \ .drop("ts")
处理后缺失的秒部分会自动补为00,日期和时间列可正常生成,符合预期输出。
内容的提问来源于stack exchange,提问作者Reza7
相关产品推荐
相关产品推荐

