将joda localtime转换为java.sql.date

Converting Joda LocalTime to java.sql.Date

要进行JDBC查询,我需要将日期传递给它。日期保存在PostgreSQL数据库的Date字段类型中,表示特定日期,不需要任何时间。

由于我只需要日期,所以我决定使用一个特定的对象,它只代表没有时间的日期,即来自Joda时间包的LocalDate。我认为这很重要,因为如果我使用datetime对象,它将携带多余的时间数据,并且可能会在夏令时结束时导致错误,此时时钟将向后推迟一小时(尽管这种情况非常罕见,但并非不可能)。

但当我开始尝试用preparedStatement.setDate方法的可接受的参数来处理LocalDate对象时,我找不到合适的方法。

setDate接受java.sql.Date作为参数。构造java.sql.Date对象的唯一选择是以毫秒为单位传递时间。

但这违背了使用Joda时间包中的LocalDate的所有目的,因为在这个转换中,我们返回到毫秒,而当这些转换发生时,时钟可能会推迟一个小时,并将日期更改为前一个日期。

所以,现在我的代码中有一行:

1
preparedStatement.setDate(1, new java.sql.Date(localDate.toDate().getTime()));

但这是将LocalDate转换为setDate格式的最佳方法吗?

我对夏令时和相应的时钟移位的担心是否合理?

是否有更好的方法将日期(并且只有没有时间的日期)传递给JDBC PreparedStatement?


使用您的技术应该是安全的,因为LocalDate#toDate将考虑所有时区问题。所得到的毫秒瞬间与上下文无关:它唯一地与您用于转换的区域设置中在该时间点有效的时区相关。换言之,如果您在一年中重复转换完全相同的毫秒值,您将始终得到完全相同的答案,即使同时您所在位置的时区规则发生了变化,因为JDK引用了一个记录全世界所有时区变化的完整历史的数据库。

在对这些问题进行推理时,重要的是要记住,当前时区对转换没有影响,转换是由您的区域设置参数化的,并且只在被转换的即时上下文中解析时区。

我真心同情你对这一切感到的不安:它把一个简单而紧张的操作变成了一个复杂的计算迷宫,它只会招来麻烦。希望Java 8和它的新事物会有一个积极的转变(是的,再一次!)日期/时间API,基于jodatime。


我今天也遇到了同样的问题。我正在使用JDK 8。经过几个小时的搜索,我终于在JavaSE 8文档中找到了答案。这就是解决方案:

1
statement.setDate(5, java.sql.Date.valueOf(personToUpdate.getBirthday()));

语句是PreparedStatement实例。"persontoUpdate.getBirthday()"是本地日期的类型。


译文;

1
2
3
4
myPreparedStatement.setObject(   // Pass java.time objects directly to database with JDBC 4.2 or later.
    … ,
    LocalDate.now()              // Get current date today. Better to pass optional `ZoneId` time zone object explicitly than rely implicitly on JVM’s current default.
)

java.time

8、Java和以后,新的框架是建立和java.time"。这successor碘-时间是由定义JSR 310和扩展,通过threeten额外项目。 <P /好的。

我们看到hopefully好eventually JDBC驱动器更新这directly处理新java.time类型。但是,直到然后我们需要继续在这java.sql。*类型。fortunately,新方法已被添加,这conveniently convert之间的类型。 <P /好的。

对一只给我个好时代,"一天的时间和井区的Java类型是LocalDate(这是相当相似的碘的时间)。 <P /好的。

作为对你的关注,这一LocalDate区相关时,它matters当你是一只translating约会这一日期时,那一刻他在timeline。给我一只是淡淡的vague与皇马的想法,直到你translate意义吧,它是一种跨弯矩的timeline(这是他在午夜和一些时间区)。例如,"今天"得到充分的测定时间区。我们使用的ZoneId和java.time类。 <P /好的。

1
LocalDate today = LocalDate.now( ZoneId.of("America/Montreal" ) );

如果你omitted,JVM的违约时间流区是用于测定的数据。与其他字,下面的两行是等价的。 <P /好的。

1
2
LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now( ZoneId.systemDefault() );

我consider第一版本,now(),这是一个可怜的API的设计选择。这个隐式应用的JVM的违约时间流区causes井端的混乱,错误,和痛苦的垃圾?他developers。为一件事,在JVM的违约varies流由机,由主机操作系统和邮件系统的管理员也settings,主编。worse,JVM的违约可以改变在任何时间、任何代码,在运行时,通过螺纹与任何应用程序,在任何JVM。这是最好的做法是,你总是specify desired /预期的时间区。 <P /好的。

现在,我们有一java.time对象为"今天",如何把它变成"数据库吗? <P /好的。

4.2与JDBC或以后,directly交易所java.time对象和你的数据库。 <P /好的。

1
myPreparedStatement.setObject( … , today ) ;

这种检索: <P /好的。

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

如果你不能这样做. 4.2以后升级或java.sql.Date:使用对象。和Java类,8岁,gained新方法,toLocalDatevalueOf。"是我们的"java.time桥实体型的java.sql型。 <P /好的。

1
java.sql.Date sqlToday = java.sql.Date.valueOf( today );

从那里的usual PreparedStatement装卸。 <P /好的。

1
myPreparedStatement.setDate( 1 , sqlToday );

我只给你时间与

或许你有concerns,这样你只需要把这件业务。 <P /好的。

例如,如果您需要知道,如果出于法律原因,合同是在一天结束前签署的,那么只有当日期表示一个特定时刻(如蒙特利尔午夜的钟声)时,日期才是错误的数据类型。新的一天在巴黎比在蒙特利尔来得早,所以巴黎的"今天"在蒙特利尔是"昨天"。如果您的合同期限在法律上定义为一天的月底,那么您必须申请时区。要应用时区,您必须具有日期时间而不是仅日期。你可以从LocalDate跳到ZonedDateTime,但我认为这太复杂了。数据库应该从一开始就使用日期时间类型。好的。

在Postgres中,日期时间类型表示TIMESTAMP WITH TIME ZONE类型。这个名字用词不当,因为时区实际上没有存储。把它看作是"时区的时间戳"。Postgres使用来自UTC的任何偏移量或与传入数据相关的时区信息来调整到UTC,然后丢弃该偏移量/区域信息。另一种类型,TIMESTAMP WITHOUT TIME ZONE,完全忽略偏移量/区域信息,对于大多数业务应用程序来说,这是错误的行为。好的。

我怀疑许多开发人员或DBA可能会使NA?凭直觉认为日期只有明显意义的错误。但事实上,如果您有特定的或严格的面向时间的需求,例如有关"签订合同"、"收到发票"或"聘用公司高管"等事件的合法性,则应使用日期时间值,而不是仅使用日期。好的。

换句话说,关于问题作者的评论:好的。

And I expected there should be a way to work with dates without resorting to time instances.

Ok.

不,我认为这是自找麻烦。如果时间很重要,请使用日期时间而不是仅日期。好的。关于JavaTimes

JavaTimeFr框架是在Java 8和之后构建的。这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat。好的。

现在处于维护模式的joda time项目建议迁移到java.time类。好的。

要了解更多信息,请参阅Oracle教程。以及搜索堆栈溢出以获得许多示例和解释。规格为JSR 310。好的。

在哪里获取java.time类?好的。

  • Java Java看见8,9,和以后
    • 内置。
    • 方的标准的Java API和一个bundled执行。
    • 9 adds Java和一些小的功能是固定的。
  • 看到Java和Java SE 7 6
    • 多功能性的java.time冰后两个Java 6 ported &;7 threeten - backport。
  • Android
    • 以后的版本的Android系束的java.time类的实现等。
    • 在之前的Android项目,threetenabp adapts threeten - backport(上述以上)。看到如何使用threetenabp…

《threeten额外项目extends java.time与额外的类。这个项目是一个证明的地面为java.time加两个可能的未来。你可以在这里找到一些有用的类如IntervalYearWeekYearQuarter,和更多。

好。 好的。


由于org.joda.time.todatemiddy()和org.joda.time.todatemiddy(datetimezone zone)已被弃用,这是一个非常适合我的解决方案。

我的典型班级,要坚持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    ...
    import org.joda.time.LocalDate;
    ...

    public class MyObject implements Serializable {

      ...
      private LocalDate startDate;

      ...
      private EndDate startDate;


      // Getters and Setters
      ...

      ...
    }

我是我坚持开始日期的另一个班级,我有:

1
    myObject.setStartDate(new LocalDate(myObject.getStartDate().toDateTimeAtStartOfDay(DateTimeZone.getDefault())));


尝试使用LocalDate#toDateMidnight(),将时间设置为0,然后使用DateMidnight#toDate()

1
Date date = localDate.toDateMidnight().toDate();

或者,如果您使用的是jodatime 1.5或更高版本,请使用LocalDate#toDateTimeAtStartOfDay(),然后使用DateTime#toDate()

希望帮助