10

尝试使用 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

https://issues.apache.org/jira/browse/SPARK-17545?jql=project%20%3D%20SPARK%20AND%20text%20~%20iso8601

显然,混合基本形式和扩展形式不符合 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 字符串。

4

0 回答 0