【问题标题】:Timestamp formats and time zones in Spark (scala API)Spark(scala API)中的时间戳格式和时区
【发布时间】:2018-09-01 06:27:30
【问题描述】:

*********更新********

按照 cmets 中的建议,我删除了代码中不相关的部分:

我的要求:

  1. 将毫秒数统一为 3
  2. 将字符串转换为时间戳并将值保持为 UTC

创建数据框:

val df = Seq("2018-09-02T05:05:03.456Z","2018-09-02T04:08:32.1Z","2018-09-02T05:05:45.65Z").toDF("Timestamp")

这里是使用 spark shell 的结果:

************ 结束更新 ************************************

在尝试使用 scala 在 Spark 中处理时区和时间戳格式时,我头疼不已。

这是我的脚本的简化,用于解释我的问题:

 import org.apache.spark.sql.functions._

 val jsonRDD  = sc.wholeTextFiles("file:///data/home2/phernandez/vpp/Test_Message.json")

 val jsonDF =  spark.read.json(jsonRDD.map(f => f._2))

这是生成的架构:

  root
 |-- MeasuredValues: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- MeasuredValue: double (nullable = true)
 |    |    |-- Status: long (nullable = true)
 |    |    |-- Timestamp: string (nullable = true)

然后我只选择 Timestamp 字段如下

jsonDF.select(explode($"MeasuredValues").as("Values")).select($"Values.Timestamp").show(5,false)

我要修复的第一件事是每个时间戳的毫秒数并将其统一为三个。

我应用 date_format 如下

jsonDF.select(explode($"MeasuredValues").as("Values")).select(date_format($"Values.Timestamp","yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")).show(5,false)

毫秒格式已修复,但时间戳已从 UTC 转换为本地时间。

为了解决这个问题,我将 to_utc_timestamp 与我的本地时区一起应用。

jsonDF.select(explode($"MeasuredValues").as("Values")).select(to_utc_timestamp(date_format($"Values.Timestamp","yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),"Europe/Berlin").as("Timestamp")).show(5,false)

更糟糕的是,不会返回 UTC 值,并且会丢失毫秒格式。

任何想法如何处理这个?我会很感激的????

BR。保罗

【问题讨论】:

  • 我建议删除所有不相关的代码——阅读 JSON、explode 等——它们不会带来任何问题。您可以使用简单的Seq(...).toDF("timestamp") 来提供 MCVE。

标签: scala apache-spark apache-spark-2.2


【解决方案1】:

问题的原因是用于转换的时间格式字符串:

yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

如您所见,Z 在单引号内,这意味着它不会被解释为区域偏移标记,而只是像中间的T 这样的字符。

所以,格式字符串应该改为

yyyy-MM-dd'T'HH:mm:ss.SSSX

其中X 是Java 标准日期时间格式化程序模式(Z 是0 的偏移值)。

现在,可以将源数据转换为 UTC 时间戳:

val srcDF = Seq(
  ("2018-04-10T13:30:34.45Z"),
  ("2018-04-10T13:45:55.4Z"),
  ("2018-04-10T14:00:00.234Z"),
  ("2018-04-10T14:15:04.34Z"),
  ("2018-04-10T14:30:23.45Z")
).toDF("Timestamp")

val convertedDF = srcDF.select(to_utc_timestamp(date_format($"Timestamp", "yyyy-MM-dd'T'HH:mm:ss.SSSX"), "Europe/Berlin").as("converted"))

convertedDF.printSchema()
convertedDF.show(false)

/**
root
|-- converted: timestamp (nullable = true)

+-----------------------+
|converted              |
+-----------------------+
|2018-04-10 13:30:34.45 |
|2018-04-10 13:45:55.4  |
|2018-04-10 14:00:00.234|
|2018-04-10 14:15:04.34 |
|2018-04-10 14:30:23.45 |
+-----------------------+
*/

如果您需要将时间戳转换回字符串并将值标准化为具有 3 个尾随零,则应该有另一个 date_format 调用,类似于您在问题中已经应用的调用。

【讨论】:

  • 您好安托特,感谢您的回答。我应用了你提出的建议,它奏效了。如果我按照建议再次应用 date_format,我会得到如下值:“2018-04-10T13:30:34.450+02”,我认为这是错误的,偏移量应该为零,不应该是?
  • 是的,偏移量应该是 0 或 Z。+02 在那里是因为添加了本地区域偏移量。如果您在最后一个转换模式中重复使用原始引用的 'Z',则应该可以按预期工作。
猜你喜欢
  • 2011-11-30
  • 1970-01-01
  • 1970-01-01
  • 2020-01-12
  • 1970-01-01
  • 2021-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多