代码之家  ›  专栏  ›  技术社区  ›  Adam

截断java。util。Date to LocalDate*没有*toInstant(),因为java。sql。日期给出不支持操作异常

  •  14
  • Adam  · 技术社区  · 7 年前

    如果我使用 java.util.Date toInstant() 关于一个恰好是 java.sql.Date ,我得到一个 UnsupportedOperationException

    try {
        java.util.Date input = new java.sql.Date(System.currentTimeMillis());
        LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    } catch (UnsupportedOperationException e) {
        // grrr!
    }
    

    这个 Java语言util。日期 我关心的是通过遗留API来自mysql数据库中的日期字段,实际上是一个 Java语言sql。日期 .

    下面的相关问题都很有趣:

    UnsupportedOperationException - Why can't you call toInstant() on a java.sql.Date?

    Convert java.util.Date to java.time.LocalDate

    LocalDate to java.util.Date and vice versa simplest conversion?

    但它们并没有提供任何优雅的方式来截断 要去掉时间组件并获得Java 8 LocalDate .

    我怀疑解决方案将涉及java。util。但我宁愿先确定别人做了什么,而不是制定自己的解决方案。

    我更喜欢找比这个短的东西:

    Resetting the time part of a timestamp in Java :

    Date date = new Date();                      // timestamp now
    Calendar cal = Calendar.getInstance();       // get calendar instance
    cal.setTime(date);                           // set cal to date
    cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
    cal.set(Calendar.MINUTE, 0);                 // set minute in hour
    cal.set(Calendar.SECOND, 0);                 // set second in minute
    cal.set(Calendar.MILLISECOND, 0);            // set millis in second
    Date zeroedDate = cal.getTime();             // actually computes the new Date
    
    7 回复  |  直到 7 年前
        1
  •  15
  •   Adam    7 年前

    通常最简单的解决方案最难找到:

    public LocalDate convertDateObject(java.util.Date suspectDate) {
    
        try {
            // Don't do this if there is the smallest chance 
            // it could be a java.sql.Date!
            return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    
        } catch (UnsupportedOperationException e) {
            // BOOM!!
        }
    
        // do this first:
        java.util.Date safeDate = new Date(suspectDate.getTime());
    
        return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    
    }
    
        2
  •  8
  •   user7605325 user7605325    7 年前

    如果 input java.sql.Date ,然后您可以简单地将其投射并调用 toLocalDate()

    LocalDate date = ((java.sql.Date) input).toLocalDate();
    

    不幸的是,你不能打电话 toInstant() Java语言sql。日期 因为 according to javadoc ,它总是抛出一个 UnsupportedOperationException .

    如果您不知道类型(可以是 java.util.Date 或者 Java语言sql。日期 ),您可以使用返回的值 getTime() 方法来构建 Instant ,然后将其转换为时区(下面我使用JVM的默认值),最后从中获取本地日期:

    LocalDate date = Instant
        // get the millis value to build the Instant
        .ofEpochMilli(input.getTime())
        // convert to JVM default timezone
        .atZone(ZoneId.systemDefault())
        // convert to LocalDate
        .toLocalDate();
    

    这个 toLocalDate() 方法获取日期部分(日/月/年),忽略其余部分,因此无需截断它:无论时间是午夜、上午10点还是一天中的任何其他时间, toLocalDate() 忽略它,只得到日期部分。

    但是,如果您真的想将时间设置为午夜,可以使用 with method 然后通过一个 LocalTime 致:

    LocalDate date = Instant
        // get the millis value to build the Instant
        .ofEpochMilli(input.getTime())
        // convert to JVM default timezone
        .atZone(ZoneId.systemDefault())
        // set time to midnight
        .with(LocalTime.MIDNIGHT)
        // convert to LocalDate
        .toLocalDate();
    

    但正如我所说 方法只会忽略时间部分,因此在这种情况下不需要设置时间(即 LocalDate 将是相同的)。


    您还可以检查日期的类型,并相应地选择适当的操作,如下所示:

    if (input instanceof java.sql.Date) {
        date = ((java.sql.Date) input).toLocalDate();
    } else {
        date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
    

    而不是使用JVM默认时区( ZoneId.systemDefault() ),您可以根据需要拨打电话使用任何其他时区 ZoneId.of("zoneName") ,其中区域名称是任何有效的 IANA timezones names (始终采用格式 Region/City 喜欢 America/New_York Europe/London ). CET PST ambiguous and not standard .

    您可以通过调用获取可用时区的列表(并选择最适合您的系统的时区) ZoneId.getAvailableZoneIds() can be changed without notice, even at runtime ,所以最好始终明确您使用的是哪一个。

        3
  •  2
  •   Basil Bourque    7 年前

    tl;博士

    myResultSet.getObject( … , LocalDate.class ) 
    

    您的问题开始于使用传统的日期时间类,例如 java.sql.Date , java.util.Date Calendar . 完全避免这些课程。它们现在是遗留的,被java所取代。时间课程。

    您说过,该值开始于MySQL列中类型为的存储值 DATE . 该类型仅为日期,没有一天中的时间。所以 您不必要地引入了时间价值 使用错误的类。

    使用符合JDBC 4.2或更高版本的JDBC驱动程序,使用java与数据库交换值。时间课程。

    LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
    

    然后传给 PreparedStatement .

    myPstmt.setObject( … , myLocalDate ) ;
    
        4
  •  2
  •   Omid Mohebbi    5 年前

    我不知道为什么,但这段代码对我有用:)

    .atZone(ZoneId.systemDefault())。toLocalDate();

    这是行不通的:

    LocalDate ld=个人性。getBirthday()。getTime()。toInstant() .atZone(ZoneId.systemDefault())。toLocalDate();

        5
  •  1
  •   Suhas Saheer    7 年前

    试试这个。

    java.util.Date input = new java.sql.Date(System.currentTimeMillis());
    Instant instant = Instant.ofEpochMilli(input.getTime());
    LocalDate date = instant .atZone(ZoneId.systemDefault()).toLocalDate();
    
        6
  •  0
  •   davidbuzatto    7 年前

    您是否尝试使用SimpleDataformat来帮助您?

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.util.Date;
    
    public class Utils {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
    
        public static void main( String[] args ) {
            LocalDate ld = sqlDateToLocalDate( new java.sql.Date( System.currentTimeMillis() ) );
            System.out.println( ld );
        }
    
        public static LocalDate sqlDateToLocalDate( java.sql.Date sqlDate ) {
    
            try {
                Date d = sdf.parse( sdf.format( sqlDate ) );
                return d.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate();
            } catch ( ParseException exc ) {
            }
    
            return null;
    
        }
    
    }
    
        7
  •  0
  •   Basil Bourque    6 年前

    tl;博士

    你跨越两者的前提 java.sql.Date java.util.Date 不正确。

    如果我使用java。util。变量上的日期,该变量恰好是java。sql。日期

    你违反了课堂文档中列出的合同。你被告知忽略以下事实: 是的子类 Java语言util。日期 .

    但解决方案可能相当简单。

    ZonedDateTime zdt = myJavaSqlDate.toLocalDate().atStartOfDay( ZoneId.of( "America/Montreal" ) ) ;
    

    细节

    显然你手里有一个 Java语言sql。日期 . 该类表示只包含日期的值,不包含一天中的时间和时区。至少这是那个阶级的意图。不幸的是,从中选择了一个子类 尽管有名字,但它代表一个日期 UTC中的一个时间。这种继承关系是一种黑客行为,一种糟糕的黑客行为,他们将一天的内部时间设置为“00:00:00”。文档明确指示我们忽略这种继承关系,忽略具有时间组件的事实,并假设两者 Date 课程无关。但许多人,比如作者,并没有仔细阅读该文档,而是根据类名做出粗略的假设。

    日期 类是最早版本的Java附带的日期-时间框架的一部分。这些已经被java所取代。Java 8及更高版本中内置的时间类。

    遇到遗留日期时间对象时的第一步:转换为java。使用新方法添加到旧类的时间。

    Java语言sql。日期 类型为true date only的对象 java.util.LocalDate . 本地字表示没有时区或与UTC的偏移。

    LocalDate ld = myJavaSqlDate.toLocalDate() ;
    

    显然,您希望将仅日期值转换为具有时间的日期。也许你想要一天中的第一刻。确定第一个时刻需要一个时区。例如,一个新的一天提前到来 Europe/Paris 比在 America/Montreal ,并且在 Asia/Kolkata .

    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ; 
    ZonedDateTime zdt = ld.atStartOfDay( z ) ;