尝试使用 cast(TimestampType) 将带有时区信息的 ISO8601 字符串转换为 TimestampType 时,仅接受使用时区格式 +01:00 的字符串。如果时区以 ISO8601 合法方式 +0100(不带冒号)定义,则解析失败并返回 null。我需要在保留 ms 部分的同时将字符串转换为 TimestampType。
2019-02-05T14:06:31.556+0100 Returns null
2019-02-05T14:06:31.556+01:00 Returns a correctly parsed TimestampType
我尝试使用 to_timestamp() 和 unix_timestamp().cast(TimestampType) 函数。不幸的是,他们截断了我需要保留的时间戳的 ms 部分。此外,您需要将它们应用于新列,并且不能对复杂类型中的属性进行就地替换(如果我在 from_json 函数的架构中将 ApiReceived 属性设置为 TimestampType ,这是可能的)。
df
.select($"body".cast(StringType))
.select(from_json($"body", schema).as("Payload"))
.select($"Payload.Metadata.ApiReceived".as("Time"))
.withColumn("NewTime", to_timestamp($"Time", "yyyy-MM-dd'T'HH:mm:ss.SSSZ"))
.withColumn("NewTime2", unix_timestamp($"Time", "yyyy-MM-dd'T'HH:mm:ss.SSSZ").cast(TimestampType))
.withColumn("NewTime3", $"Time".cast(TimestampType))
上述DataFrame的输出类型
df:org.apache.spark.sql.DataFrame
Time:string
NewTime:timestamp
NewTime2:timestamp
NewTime3:timestamp
和输出值
Time 2019-02-05T14:06:31.556+0100
NewTime 2019-02-05 13:06:31
NewTime2 2019-02-05 13:06:31
NewTime3 null
有没有办法让 Spark 在不求助于 UDF:s 的情况下处理转换?
更新
经过更彻底的调查,我发现 Sparks 的日期时间解析有些不一致。:)
val df = Seq(
//Extended format
("2019-02-05T14:06:31.556+01:00"),
("2019-02-05T14:06:31.556+01"),
("2019-02-05T14:06:31.556"),
//Basic Format
("20190205T140631556+0100"),
("20190205T140631556+01"),
("20190205T140631556"),
//Mixed extended with basic
("2019-02-05T14:06:31.556+0100"),
("20190205T140631556+01:00")
).toDF
val formatStrings = Seq(
("yyyy-MM-dd'T'HH:mm:ss.SSSZ"),
("yyyy-MM-dd'T'HH:mm:ss.SSSX"),
("yyyyMMdd'T'HHmmssSSSZ"),
("yyyyMMdd'T'HHmmssSSSX")
)
val format = formatStrings(0)
val df2 = df
.select($"value".as("Time"))
.withColumn("NewTime3", $"Time".cast(TimestampType))
.withColumn("NewTime", to_timestamp($"Time", format))
.withColumn("NewTime2", unix_timestamp($"Time", format).cast(TimestampType))
.withColumn("NewTime4", date_format($"Time", format))
display(df2)
我运行这些数据帧并比较输出,这有点令人沮丧。最宽松的 formatString 是第二个 SSSX
处理此问题的唯一合理方法是确保所有 ISO8601 字符串都符合您计划使用的函数理解的标准的 UDF。
尽管如此,还没有找到一种方法来保留两种格式的毫秒部分。
2019-02-05T14:06:31.556+01:00 and
2019-02-05T14:06:31.556+0100
更新 2
显然,混合基本形式和扩展形式不符合 ISO8601 标准。字符串“2019-02-05T14:06:31.556+0100”不是标准格式。不过根据 RFC822 似乎是正确的。
如果我正确理解 JIRA 票证,则标准解析(即字符串列上的 cast() )仅处理正确格式化的 ISO8601 字符串,而不是 RFC822 或其他边缘情况(即混合扩展格式和基本格式)。如果您有边缘情况,则必须提供格式字符串并使用另一种解析方法。
我无权访问 ISO8601:2004 标准,因此无法检查,但如果 JIRA 中的评论正确,则互联网需要更新。许多网页将 RFC822 和 ISO8601 混为一谈,并将“2019-02-05T14:06:31.556+0100”列为合法的 ISO8601 字符串。