【问题标题】:Error when converting to BigQuery timestamp from ISO 8601 date string从 ISO 8601 日期字符串转换为 BigQuery 时间戳时出错
【发布时间】:2019-03-14 03:31:27
【问题描述】:

我有一个 ISO 8601 日期字符串,格式如下:

String myIsoDateString = "2019-02-27T23:00:00.000Z"

我需要在 BigQuery 中运行的查询中使用日期字符串。我正在尝试使用com.google.cloud.bigquery.QueryParameterValue 类将其转换为QueryParameterValue,类型为timestamp,如下所示:

QueryParameterValue.timestamp(myIsoDateString)

这给了我一个错误:

java.lang.IllegalArgumentException: Invalid format: "2019-02-27T23:00:00.000Z" is malformed at "T23:00:00.000Z"

Eclipse 中时间戳方法的内联帮助指出:

Creates a QueryParameterValue object with a type of TIMESTAMP. Must be in the format"yyyy-MM-dd HH:mm:ss.SSSSSSZZ", e.g. "2014-08-19 12:41:35.220000+00:00".

如何将myIsoDateString 转换为所需的格式?有没有更好的方法可以处理从 ISO 8601 字符串转换为 BigQuery 中的时间戳?

【问题讨论】:

  • 用空格代替T

标签: java date datetime google-bigquery iso8601


【解决方案1】:

对于任何试图在 BigQuery 中解析 ISO 8601 的人(这篇文章是 Google 的第一个结果),试试这个:

SELECT
PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E*SZ',  '2018-10-12T13:22:27.120Z')

【讨论】:

【解决方案2】:

我希望 Felipe Hoffa 的答案能够奏效,并为您提供您想要的:将 T 替换为空格。以下是一些其他建议。

明确:虽然文档给出了一个非常清楚的预期字符串示例,但从错误消息和文档中都不清楚(下面的链接,但您已经在 Eclipse 中得到了它) QueryParameterValue.timestamp 是否接受 Z 作为偏移量。如果我们想确定并且更明确地提供所要求的格式(对于那些在您之后管理您的代码的人来说总是很好):

    DateTimeFormatter timestampFormatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSxxx");
    String myIsoDateString = "2019-02-27T23:00:00.000Z";
    OffsetDateTime dateTime = OffsetDateTime.parse(myIsoDateString);
    QueryParameterValue.timestamp(dateTime.format(timestampFormatter));

这会将字符串2019-02-27 23:00:00.000000+00:00 传递给timestamp

长传: timestamp 是采用Long 参数的重载版本。同样,它没有记录 long 值应该是什么。编辑:您自己的回答显示timestamp(Long) 自纪元以来需要微秒。没有任何不必要的精度损失风险的完全正确的转换方法是:

    String myIsoDateString = "2019-02-27T23:00:00.000Z";
    Instant dateTime = Instant.parse(myIsoDateString);
    long microsecondsSinceEpoch = TimeUnit.SECONDS.toMicros(dateTime.getEpochSecond())
            + TimeUnit.NANOSECONDS.toMicros(dateTime.getNano());
    QueryParameterValue.timestamp(microsecondsSinceEpoch);

将 1 551 308 400 000 000 的值传递给 timestamp

免责声明:我不是 BigQuery 用户。

链接

【讨论】:

  • 感谢@Ole V.V.我发现如果通过long,则需要通过纪元以来的微秒 - 请参阅我的答案
【解决方案3】:

错误表明这不是正确的格式:

 "2019-02-27T23:00:00.000Z"

但是这个应该是:

 "yyyy-MM-dd HH:mm:ss.SSSSSSZZ"

尝试将日期和时间之间的T 替换为

【讨论】:

    【解决方案4】:

    ISO 8601 与 SQL

    ISO 8601 标准在中间使用T 将年-月-日部分与时-分-秒部分分开。

    SQL 样式在中间使用空格而不是T

    更换。

    String input = "2019-02-27T23:00:00.000Z".replace( "T" , " " ) ;
    QueryParameterValue.timestamp( input ) ;
    

    同上去另一个方向。 java.time 类在解析/生成文本时默认使用 ISO 8601 格式。

    String input = "2019-02-27 23:00:00.000Z".replace( " " , "T" ) ;
    Instant instant = Instant.parse( input ) ;
    ZonedDateTime zdt = instant.atZone( ZoneId.of( "Pacific/Auckland" ) ) ;
    

    【讨论】:

      【解决方案5】:

      这行得通:

      import org.joda.time.DateTime;
      import org.joda.time.DateTimeZone;
      

      myIsoDateString 转换为微秒并将其存储为long

      long myDateInMicroseconds = DateTime.parse(myIsoDateString).toDateTime(DateTimeZone.UTC).getMillis() * 1000;
      

      然后将其作为参数传递给 QueryParameterValue.timestamp:

      QueryParameterValue.timestamp(myDateInMicroseconds)
      

      【讨论】:

      • 感谢您提供您找到的解决方案。我可能更喜欢更详细(但也更长)的TimeUnit.MILLISECONDS.toMicros(milliSecondValue)。反过来,它仍然会丢失字符串的微秒精度(如果它具有微秒精度)。完美的转换是存在的,但仍会更长。
      • @OleV.V.是的 - 我已经更新了答案以显示这一点,谢谢。如果您进一步编辑您的答案以删除关于毫秒的初始假设,我可以将其标记为答案(我不介意您借用我的解决方案,只要问题有一个好的和完整的答案:)
      • 谢谢。克里斯,这很好。我认为您应该接受自己的答案,因为它对解决您自己的问题最有帮助。无论如何你是对的,我的错误猜测没有任何帮助,所以我把它从我的答案中删除了。
      猜你喜欢
      • 2019-02-27
      • 2017-07-25
      • 1970-01-01
      • 1970-01-01
      • 2020-11-23
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多