Spark中ISO 8601次计时字符串的日期差异

时间:2017-07-03 11:33:57

标签: scala datetime apache-spark timestamp

我有两个ISO 8601格式的日期时间字符串:

  

2017-05-30T09:15:06.050298Z
  2017-05-30T09:15:06.054939Z

我想在Spark环境中使用Scala来区分上述两个字符串之间的时差。

3 个答案:

答案 0 :(得分:1)

Java 8 <{1}}的纳秒数

由于Spark不支持高于Seconds的日期差异,我们需要为Millis或Nanos创建UDF

与日期时间相关的进口

LocalDateTime

以纳秒为单位创建UDF到日期差异

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;

检查:help in building DateTimeFormatter pattern

  

spark.udf.register("date_diff_nano", (d1: String, d2: String) => val dtFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.n'Z'") val dt1 = LocalDateTime.parse(d1, dtFormatter) val dt2 = LocalDateTime.parse(d2, dtFormatter) (dt1.getLong(ChronoField.NANO_OF_DAY) - dt2.getLong(ChronoField.NANO_OF_DAY)) ) 修改为ChronoField.NANO_OF_DAY   在UDF的最后一行,我们也可以在微秒内获得日期差异。

现在,在任何DataFrame / DataSet对象上使用UDF。

ChronoField.MICRO_OF_DAY

此处import spark.implicits._ //to use $-notation on columns // create the dataframe df val df = ... val resultDf = df.withColumn("date_diff", date_diff_nano($"dt1", $"dt2")) dt1dt2

中的DateTime列

使用Spark SQL的df进行秒数差异

使用Spark SQL预定义unix_timestamp(date, format)函数将日期转换为当天的秒数(但Java unix_timestamp可以支持解析最多毫秒),然后您可以执行Date diff with Spark SQL using unix_timestamp

SimpleDateFormat

天使用import org.apache.spark.sql.functions.unix_timestamp val resultDf = df.withColumn("date_diff_sec", (unix_timestamp($"dt1"), unix_timestamp($"dt2")))

区分两个日期
  

它接受以下格式的日期时间值

     
      
  • datediff
  •   
  • java.sql.Timestamp
  •   
  • java.sql.Date
  • 的字符串格式   
  • 'YYYY-MM-dd'
  • 的字符串格式   
'YYYY-MM-dd HH:mm:ss'

答案 1 :(得分:1)

正如你在评论中所说,你正在使用Joda-Time,这是一个使用它的答案。

你说你正在呼叫daysBetween。但是两个日期都在同一天,所以结果总是为零。要获得具有毫秒精度的日期之间的差异,只需从两个DateTime对象中减去millis值:

import org.joda.time.DateTime

val s1 = "2017-05-30T09:15:06.050298Z"
val s2 = "2017-05-30T09:15:06.054939Z"

val diffInMillis = DateTime.parse(s2).getMillis() - DateTime.parse(s1).getMillis()

diffInMillis将为4 - 第一个日期的秒数为050298,第二个日期为054939,但joda的DateTime为毫秒精度,所以丢弃最后3位数。您可以通过执行以下操作来检查:

println(DateTime.parse(s1))
println(DateTime.parse(s2))

这将输出:

  

2017-05-30T09:15:06.050Z
  2017-05-30T09:15:06.054Z

如您所见,日期之间的差异是4毫秒。

新Java日期/时间API

Joda-Time处于维护模式,正在被新的API取代,因此我不建议使用它来启动新项目。即使在joda's website中它也说:“请注意,Joda-Time被认为是一个很大程度上”完成“的项目。没有计划大的增强。如果使用Java SE 8,请迁移到java.time(JSR) -310)。“

如果您有新的java.time API(JDK&gt; = 1.8),您也可以使用它。如果java.time类不可用(JDK <= 1.7),您可以尝试基于scala timeThreeTen Backport,这是Java 8的新日期/时间类的一个很好的后端

以下代码适用于两者。 唯一的区别是包名称(在Java 8中为java.time,在ThreeTen Backport(或Android的ThreeTenABP)中为org.threeten.bp),但类和方法名称是相同的

不同之处在于此API具有纳秒精度,因此您可以获得以纳秒为单位的日期之间的差异。

import java.time.Instant
import java.time.temporal.ChronoUnit

val s1 = "2017-05-30T09:15:06.050298Z"
val s2 = "2017-05-30T09:15:06.054939Z"

// difference in nanoseconds
val diffInNanos = ChronoUnit.NANOS.between(Instant.parse(s1), Instant.parse(s2))

diffInNanos的值为4641000。如果您仍然希望以毫秒为单位的此值,则可以将其除以1000000,或使用ChronoUnit.MILLIS代替ChronoUnit.NANOS

答案 2 :(得分:0)

您可以使用xml日期解析器,因为它必须符合ISO-8601:

val t1 = javax.xml.bind.DatatypeConverter.parseDateTime("2017-05-30T09:15:06.050298Z")

val t2 = javax.xml.bind.DatatypeConverter.parseDateTime("2017-05-30T09:15:06.054939Z")

val diff = t1.getTimeInMillis - t2.getTimeInMillis