Spark(scala API)中的时间戳格式和时区

时间:2018-09-01 06:27:31

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

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

正如评论中所建议的,我消除了代码的无关部分:

我的要求:

  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壳的结果:

enter image description here

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

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

然后我只需选择“时间戳”字段,如下所示

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

Timestamp with different milliseconds length

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

我按如下所示应用了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)

Milliseconds unified but time zone change

毫秒格式是固定的,但是时间戳从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)

to_utc_timestamp output

甚至更糟的是,不返回UTC值,并且丢失了毫秒格式。

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

BR。保罗

1 个答案:

答案 0 :(得分:2)

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

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调用,类似于您在问题中已经应用的调用。