将 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
Converting Joda LocalTime to java.sql.Date
提问by ovgolovin
To make a JDBC query I need to pass date to it. The date is kept in Date
fieldtype 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 LocalDate
from 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 LocalDate
object with accepted arguments of preparedStatement.setDate
method, I didn't find a proper way to do it.
但是,当我开始尝试LocalDate
使用可接受的preparedStatement.setDate
方法参数对对象进行平方时,我没有找到合适的方法来做到这一点。
setDate
accepts java.sql.Date
as parameter. And the only option to construct java.sql.Date
object is to pass it time in milliseconds.
setDate
接受java.sql.Date
作为参数。构造java.sql.Date
对象的唯一选择是以毫秒为单位传递时间。
But this defeats all the purpose of using LocalDate
from 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.
但这违背了使用LocalDate
Joda-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 LocalDate
to accepted by setDate
format?
但这是转换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 ZoneId
class.
至于您对与 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.time
objects 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.Date
object. In Java 8, that old class gained new methods, toLocalDate
and 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 PreparedStatement
handling.
从那里做通常的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 LocalDate
into 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 ZONE
type. 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 类?
- Java SE 8, Java SE 9, and later
- Built-in.
- Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6and Java SE 7
- Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android, the ThreeTenABPproject adapts ThreeTen-Backport(mentioned above). See How to use ThreeTenABP….
- Java SE 8、Java SE 9及更高版本
- 内置。
- 具有捆绑实现的标准 Java API 的一部分。
- Java 9 添加了一些小功能和修复。
- Java SE 6和Java SE 7
- 多的java.time功能后移植到Java 6和7在ThreeTen-反向移植。
- 安卓
- 更高版本的 Android 捆绑实现 java.time 类。
- 对于早期的 Android,ThreeTenABP项目采用了ThreeTen-Backport(上面提到过)。请参阅如何使用ThreeTenABP ...。
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 的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。
回答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
希望有所帮助