При попытке преобразовать строки 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
Очевидно, НЕ соответствует стандарту 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.