将 Joda LocalTime 转换为 java.sql.Date

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16190035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 22:09:54  来源:igfitidea点击:

Converting Joda LocalTime to java.sql.Date

javadatejdbcjodatime

提问by ovgolovin

To make a JDBC query I need to pass date to it. The date is kept in Datefieldtype of PostgreSql database, which represents specific day without any time.

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

As I need only date, I decided to use specific object which represent only date without time, which is LocalDatefrom Joda-Time package. I thought it is important because if I used DateTime object, it would carry redundant time data as well as it might lead to bugs at end of daylight saving time when the clock are put backward one hour (though the situation is unprecedentedly rare, it's not impossible).

因为我只需要日期,所以我决定使用只代表日期而没有时间的特定对象,它LocalDate来自 Joda-Time 包。我认为这很重要,因为如果我使用 DateTime 对象,它会携带冗余的时间数据,并且当时钟向后一小时时可能会导致夏令时结束时出现错误(尽管这种情况前所未有,但并非如此)不可能的)。

But when I started trying to square LocalDateobject with accepted arguments of preparedStatement.setDatemethod, I didn't find a proper way to do it.

但是,当我开始尝试LocalDate使用可接受的preparedStatement.setDate方法参数对对象进行平方时,我没有找到合适的方法来做到这一点。

setDateaccepts java.sql.Dateas parameter. And the only option to construct java.sql.Dateobject is to pass it time in milliseconds.

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

But this defeats all the purpose of using LocalDatefrom Joda-Time package, as on this conversion we get back to milliseconds and, while these conversions happen, clock may be put back one hour and change the date to the previous date.

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

So, now I have this line in my code:

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

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

But is this the best way to convert LocalDateto accepted by setDateformat?

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

Are my concerns related to daylight saving time and corresponding clock-shifts justified?

我对夏令时和相应时钟变化的担忧是否合理?

Is there a better way to pass date (and only date without time) to JDBC preparedStatement?

有没有更好的方法将日期(并且只有没有时间的日期)传递给 JDBC 准备语句?

采纳答案by Marko Topolnik

It should be safe to use your technique because all the timezone issues will be taken into account by LocalDate#toDate. The resulting millisecond instant you have is context-independent: it uniquely relates to a timezone valid at that point in time within the locale you are using for conversion. In other words, if you repeat the conversion of the exact same millisecond value throughout a year, you will consistently get the exact same answer, even if timezone regulations change for your place in the meantime, since JDK refers to a database documenting the complete history of all timezone changes around the world.

使用您的技术应该是安全的,因为LocalDate#toDate. 您所获得的毫秒瞬间与上下文无关:它与在您用于转换的语言环境中的那个时间点有效的时区唯一相关。换句话说,如果您在一年中重复完全相同的毫秒值的转换,您将始终得到完全相同的答案,即使在此期间您所在位置的时区规定发生变化,因为 JDK 指的是记录完整历史的数据库世界各地的所有时区变化。

When reasoning about these issues it is important to remember that your current timezone has no effect on the conversion, which is parameterized by your localeand resolves the timezone only within the context of the instant being converted.

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

I wholeheartedly sympathize with the queasiness you fell about all this: it is turning a simple and straigtforward operation into a complex maze of calculations which does nothing but invite trouble. Hopefully things will take a positive turn with Java 8 and its new (yes, again!) Date/Time API, based firmly on JodaTime.

我衷心同情你对这一切的不安:它正在将一个简单而直接的操作变成一个复杂的计算迷宫,它只会招来麻烦。希望 Java 8 及其新的(是的,再次!)日期/时间 API 会发生积极的转变,该 API 牢固地基于 JodaTime。

回答by Ajang R

I got the same problem today. I'm using JDK 8. After spending some hours searching finally I found the answer at Java SE 8 Documentation. This is the solution :

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

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

statement is PreparedStatement instance. "personToUpdate.getBirthday()" is type of LocalDate.

语句是 PreparedStatement 实例。“personToUpdate.getBirthday()”是LocalDate的类型。

回答by Oshkosh1017

Since org.joda.time.toDateMidnight() and org.joda.time.toDateMidnight(DateTimeZone zone) have been deprecated, this is the solution that works perfectly for me.

由于 org.joda.time.toDateMidnight() 和 org.joda.time.toDateMidnight(DateTimeZone zone) 已被弃用,这是对我来说完美的解决方案。

My typical Class, to be persisted:

我典型的类,要坚持:

    ...
    import org.joda.time.LocalDate;
    ...

    public class MyObject implements Serializable {

      ...
      private LocalDate startDate;

      ...
      private EndDate startDate;


      // Getters and Setters
      ...

      ...
    }

Im my other Class where I persist startDate, I have:

我是我坚持 startDate 的另一个班级,我有:

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

回答by Basil Bourque

tl;dr

tl;博士

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

时间

In Java 8 and later, the new java.time framework is now built-in. This successor to Joda-Time is defined by JSR 310and extended by the ThreeTen-Extraproject.

在 Java 8 及更高版本中,现在内置了新的 java.time 框架。Joda-Time 的后继者由JSR 310定义并由ThreeTen-Extra项目扩展。

Hopefully we well eventually see the JDBC drivers updated to directly handle the new java.time types. But until then we continue to need the java.sql.* types. Fortunately, new methods have been added to conveniently convert between the types.

希望我们最终会看到 JDBC 驱动程序更新以直接处理新的 java.time 类型。但在那之前我们继续需要 java.sql.* 类型。幸运的是,添加了新方法以方便地在类型之间进行转换。

For a date-only, with no time-of-day and no time zone, the Java type is LocalDate(quite similar to Joda-Time).

对于没有时间和时区的仅日期,Java 类型是LocalDate(与 Joda-Time 非常相似)。

As for your concern about time zone related to a LocalDate, it matters when your are translating a date-only to a date-time, to a moment on the timeline. A date-only is just a vague idea, with no real meaning, until you translate it to a time-span of moment on the timeline (midnight to midnight in some time zone). For example, determining "today" requires a time zone. In java.time we use the ZoneIdclass.

至于您对与 a 相关的时区的担忧LocalDate,当您将 date-only 转换为 date-time 到时间轴上的某个时刻时很重要。仅日期只是一个模糊的想法,没有真正的意义,直到您将其转换为时间轴上的时间跨度(某些时区的午夜到午夜)。例如,确定“今天”需要一个时区。在 java.time 中,我们使用ZoneId该类。

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

If omitted, your JVM's current default time zone is used in determining the date. In other words, the following two lines are equivalent.

如果省略,则使用 JVM 当前的默认时区来确定日期。换句话说,下面两行是等价的。

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

I consider the first version, now(), to be a poor API design choice. This implicit application of the JVM's current default time zone causes no end of confusion, bugs, and misery among na?ve developers. For one thing, the JVM's current default varies by machine, by host OS settings, and by sysadmins. Worse, the JVM's current default can change at any moment, during runtime, by any code in any thread of any app within that JVM. So best practice is to always specify your desired/expected time zone.

我认为第一个版本now()是一个糟糕的 API 设计选择。JVM 的当前默认时区的这种隐式应用会在幼稚的开发人员中造成无休止的混淆、错误和痛苦。一方面,JVM 的当前默认值因机器、主机操作系统设置和系统管理员而异。更糟的是,JVM当前的默认可以改变在任何时刻运行期间在JVM中的任何应用程序的任何线索,通过任何代码。因此,最佳做法是始终指定您想要/预期的时区。

Now that we have a java.time object for "today", how to get it into the database?

现在我们有了一个“今天”的 java.time 对象,如何把它放到数据库中?

With JDBC 4.2 or later, directly exchange java.timeobjects with your database.

使用 JDBC 4.2 或更高版本,直接java.time与数据库交换对象。

myPreparedStatement.setObject( … , today ) ;

To retrieve:

检索:

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

If you cannot upgrade yet to JDBC 4.2 or later: Use a java.sql.Dateobject. In Java 8, that old class gained new methods, toLocalDateand valueOf. The latter is our bridge from the java.time type to the java.sql type.

如果您还不能升级到 JDBC 4.2 或更高版本:使用java.sql.Date对象。在 Java 8 中,旧类获得了新方法,toLocalDate并且valueOf. 后者是我们从 java.time 类型到 java.sql 类型的桥梁。

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

From there do the usual PreparedStatementhandling.

从那里做通常的PreparedStatement处理。

myPreparedStatement.setDate( 1 , sqlToday );

Date-only vs Date-time

仅日期与日期时间

Perhaps you have concerns about such a date-only fitting your business needs.

也许您对这种仅适合您的业务需求的日期有所顾虑。

If you need to know, for example, if a contract was signed by the end of the day for legal reasons, then date-only is the wrong data-type if mean a specific moment such as the stroke of midnight in Montréal. A new day dawns earlier in Paris than in Montréal, so "today" in Paris is "yesterday" in Montréal. If your contract deadline is defined legally as the end of the day in Montréal, then you must apply a time zone. To apply a time zone, you must have a date-time rather than a date-only. You can make a jump from the LocalDateinto a ZonedDateTime, but I consider that overly complex. Your database should have used a date-time type from the beginning.

例如,如果您需要知道合同是否因法律原因在一天结束时签署,那么仅日期是错误的数据类型,如果指的是特定时刻,例如蒙特利尔的午夜。巴黎的新一天比蒙特利尔早,所以巴黎的“今天”在蒙特利尔就是“昨天”。如果您的合同截止日期在蒙特利尔的法律定义为一天结束,那么您必须应用时区。要应用时区,您必须具有日期时间而不是仅日期。您可以从 a 跳转LocalDate到 a ZonedDateTime,但我认为这过于复杂。您的数据库应该从一开始就使用日期时间类型。

In Postgres, a date-time type means the TIMESTAMP WITH TIME ZONEtype. That name is a misnomer as the time zone is not actually stored. Think of it as “timestamp with respectfor time zone”. Postgres uses any offset-from-UTCor time zone information accompanying incoming data to adjust to UTC, and that offset/zone info is then discarded. The other type, TIMESTAMP WITHOUT TIME ZONE, ignores the offset/zone info entirely, and this is the wrong behavior for most any business app.

在 Postgres 中,日期时间类型表示TIMESTAMP WITH TIME ZONE类型。该名称用词不当,因为实际上并未存储时区。将其视为“尊重时区的时间戳”。Postgres 使用任何伴随传入数据的UTC偏移量或时区信息来调整到 UTC,然后该偏移量/时区信息将被丢弃。另一种类型,TIMESTAMP WITHOUT TIME ZONE完全忽略偏移量/区域信息,这对于大多数商业应用程序来说都是错误的行为。

I suspect many developers or DBAs may make the na?ve mistake of thinking by intuition that the date-only has obvious meaning. But in fact if you have specific or strict moment-oriented needs, such as legalities regarding events such as “contract signed”, “invoice received”, or “company executive hired”, then a date-time value should be used rather than date-only.

我怀疑许多开发人员或 DBA 可能会犯天真的错误,认为仅日期具有明显的意义。但实际上,如果您有特定或严格的面向时刻的需求,例如有关“合同签署”、“收到发票”或“公司高管聘用”等事件的合法性,则应使用日期时间值而不是日期-只要。

In other words, regarding the Question's author's comment:

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

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

我希望应该有一种方法可以在不诉诸时间实例的情况下处理日期。

No, I would argue that is asking for trouble. If moments matter, use a date-time rather than a date-only.

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



About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

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

Where to obtain the java.time classes?

从哪里获得 java.time 类?

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by Khalil

Try to Use LocalDate#toDateMidnight()which sets time to 0 and then DateMidnight#toDate().

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

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

Or if you're using JodaTime 1.5 or newer, use LocalDate#toDateTimeAtStartOfDay()and then DateTime#toDate()

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

Hope that help

希望有所帮助