Spark 2.4.0-无法将ISO8601字符串解析为TimestampType(保留ms)

时间:2019-02-09 00:12:12

标签: apache-spark databricks azure-databricks

当尝试使用强制转换(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,则可以这样做)。 / p>

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字符串均符合您计划使用的功能所理解的标准。

仍然,还没有找到在两种格式中都保留毫秒部分的方法。

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字符串。

0 个答案:

没有答案