Spark 2.4.0 - невозможно проанализировать строку ISO8601 в TimestampType с сохранением мс

При попытке преобразовать строки ISO8601 с информацией о часовом поясе в TimestampType с использованием приведения (TimestampType) принимаются только строки с использованием формата часового пояса +01:00. Если часовой пояс определен юридическим способом ISO8601 +0100 (без двоеточия), синтаксический анализ завершается ошибкой и возвращает значение null. Мне нужно преобразовать строку в TimestampType, сохранив часть ms.

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). К сожалению, они обрезают раздел мс временной метки, который мне нужно сохранить. Кроме того, вам нужно применить их к новому столбцу и нельзя выполнить замену атрибута на месте в сложном типе (что возможно, если я сделаю свойство ApiReceived TimestampType в схеме для функции from_json).

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.

Единственный разумный способ справиться с этим — UDF, который гарантирует, что все строки 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.


person Molotch    schedule 09.02.2019    source источник
comment
Вы провели хорошее исследование.. спасибо..   -  person stack0114106    schedule 09.02.2019
comment
Для информации: приведение TimestampType к double сохраняет дроби, приведение к LongType усекает до секунд.   -  person Molotch    schedule 06.09.2019
comment
сохранение миллисекунд и микросекунд доступно как часть Spark 3.1, поэтому вы можете преобразовать в эпоху и использовать новые функции. issues.apache.org/jira/browse/SPARK-31710   -  person Michael West    schedule 21.06.2021