【问题标题】:Java date pattern for SQL date (ISO 9075)SQL 日期的 Java 日期模式 (ISO 9075)
【发布时间】:2011-11-23 00:50:21
【问题描述】:

我正在尝试解析一个 SQL 日期字符串 (ISO 9075),它使用微秒 (!) 而不是毫秒,例如

2010-11-22 08:08:08.123456

但是,SimpleDateFormat 也无法识别像“yyyy-MM-dd HH:mm:ss.SSSSSS”和“yyyy-MM-dd HH:mm:ss.SSS”这样的模式。

我使用的代码如下所示:

String dateString = "2010-11-22 08:08:08.123456";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSSSS";

try
{
    format = new SimpleDateFormat(pattern);
    format.setLenient(false);
    position.setIndex(0);
    Date date1 = format.parse(dateString, position);
    System.out.println("Date 1: " + date1);
    Date date2 = format.parse(dateString);
    System.out.println("Date 2: " + date1);
}
catch (Exception e) // Should not happen
{
    e.printStackTrace();
}

无论我使用 2 种模式(“.SSS”还是“.SSSSSS”)中的哪一种,date1 都是打印机 null,而 date2 会导致解析异常(java.text.ParseException:无法解析的日期)。

【问题讨论】:

标签: java sql date simpledateformat


【解决方案1】:

也许在解析日期之前将剩余的小数部分从 dateString 中剔除?我有以下

    String dateString = "2010-11-22 08:08:08.123456";
    String fraction = dateString.substring(dateString.length() - 3);
    String formatString = dateString.substring(0, dateString.length() - 3);
    String pattern = "yyyy-MM-dd HH:mm:ss.SSS";
    ParsePosition position = new ParsePosition(0);

    try
    {

        SimpleDateFormat format = new SimpleDateFormat(pattern);
        format.setLenient(false);
        position.setIndex(0);
        Date date1 = format.parse(formatString, position);          
        System.out.println("Date 1: " + date1);
        System.out.println("Date 1 fraction: " + fraction);
        Date date2 = format.parse(formatString);
        System.out.println("Date 2: " + date2);
        System.out.println("Date 2 fraction: " + fraction);
    }
    catch (Exception e) // Should not happen
    {
        e.printStackTrace();
    }

这允许日期解析到毫秒精度,而您仍然保留小数部分。

【讨论】:

  • 小数/微秒部分是否保证正好是 6 位?例如。它是:08.123450 还是以零结尾时会被截断为:08.12345?我可能会寻找小数点并在其后取 3 位数字,而不是根据答案删除长度为 3 的数字。
  • @StephenP 这是一个好问题和好建议。如果我理解正确以实现更好的互操作性,则会创建一个标准,在本次讨论中,它是 iso 9075。如果示例以 0 结尾,它应该始终以零结尾。 (我还没有签入那个 iso 9075)
【解决方案2】:

嗯。鉴于Date 没有微秒分辨率,if 你知道所有 SQL 日期字符串在秒小数点后都有完整的六位数字(例如2010-11-22 08:08:08.000000),为什么不直接砍去掉最后三位数并在余数上使用SimpleDateFormat

【讨论】:

    【解决方案3】:

    您可能想查看DATE4J,它专门尝试处理数据库日期,精确到纳秒。

    【讨论】:

      【解决方案4】:

      您可能想要调用 DateFormat 解析,因为我认为它会切断字符串。试试:

      Date date1 = format.parse(dateString);
      

      另外,不要使用“SSSSS”。根据规范,只有“SSS”对日期格式有效。

      除此之外,我同意将其截断或用 SQL 解析。

      另外,您已将 setLenient 设置为 false,因此非常严格。因此,字符串较长会导致它失败。也许这就是它返回 null 的原因。不确定,必须测试。

      【讨论】:

        【解决方案5】:

        tl;博士

        插入/更新。

        myPreparedStatement.setObject( 
            … , 
            Instant.parse( "2010-11-22 08:08:08.123456".replace( " " , "T" ) + "Z" ) 
        ) ;
        

        检索。

        LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
        String output = ldt.toString().replace( "T" , " " ) ;  // Remove the standard ISO 8601 format’s use of `T` in the middle with the SQL-style SPACE.
        

        java.time

        Java 8 带来了新的java.time package,以取代臭名昭著的麻烦的旧 java.util.Date、.Calendar 和 SimpleDateFormat 类。那些旧的遗留类仅限于毫秒分辨率,而您的输入字符串具有微秒。您不能将六位小数塞进限制为三位的数据类型中。

        java.time 中的日期时间值具有nanosecond 分辨率,对于您的微秒来说绰绰有余。这意味着小数秒最多有 九位 位小数。

        对于 JDBC 4.2 及更高版本,直接使用 Instant 类。

        解析您的输入字符串。为了符合 java.time 类中默认使用的ISO 8601 格式,请将中间的空格替换为T。您给定的示例必须来自列类型TIMESTAMP WITHOUT TIME ZONE,这意味着不是特定时刻,不是时间轴上的一个点。

        String input = "2010-11-22 08:08:08.123456".replace( " " , "T" ) ;
        LocalDateTime ldt = LocalDateTime.parse( input ) ;
        

        如果此输入是 UTC 中的一个时刻,请附加 Z。然后你确实有时间,时间轴上的一个特定点。用于TIMESTAMP WITH TIME ZONE 的列类型。

        String input = "2010-11-22 08:08:08.123456".replace( " " , "T" ) + "Z" ;
        Instant instant = Instant.parse( input ) ;
        

        发送到您的数据库。

        myPreparedStatement.setObject( … , instant ) ;
        

        ……和……

        Instant instant = myResultSet.getObject( … , Instant.class ) ;
        

        关于java.time

        java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

        Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

        要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

        您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

        从哪里获得 java.time 类?

        ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore


        更新:Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。保留此部分作为历史记录。

        乔达时间

        如上所述,您应该避免使用 java.util.Date、.Calendar 和 SimpleDateFormat 类。从 Java 8 开始,您可以选择使用 java.time 或 Joda-Time 或两者兼而有之,因为它们各有优缺点。 Joda-Time 也适用于早期版本的 Java。

        您可以使用 Joda-Time 解析该字符串。

        或者,如果您有权访问生成字符串的 java.sql.Timestamp,则可以简单地将 Timestamp 对象传递给 DateTime 的构造函数。我建议还传递一个时区,因为 DateTime 对象知道它们自己分配的时区。

        DateTime dateTime = new DateTime( mySqlTimestamp, DateTimeZone.UTC );
        

        DateTime dateTime = new DateTime( mySqlTimestamp, DateTimeZone.forID( "Europe/Paris" ) );
        

        Joda-Time 的分辨率为 milliseconds。因此,无论哪种方式生成 DateTime(解析或传递),都会丢失秒的更精细部分(忽略/截断,未四舍五入)。

        【讨论】:

          【解决方案6】:

          如果您可以控制作为 Java 代码输入的 SQL 日期,您可以尝试以可行的格式从 SQL 中检索日期(“yyyy-MM-dd HH:mm:ss.SSS” )。

          【讨论】:

            猜你喜欢
            • 2023-03-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-18
            • 1970-01-01
            • 2016-08-08
            • 1970-01-01
            相关资源
            最近更新 更多