您将如何在 Java 模型中表示出生日期?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4031008/
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
How would you represent date of birth in your java model?
提问by Ilja S.
And wait, don't rush to answer "java.util.Date", consider the following scenario.
等等,不要急于回答“java.util.Date”,考虑以下场景。
Person object having 2 fields: "birthday" and "nextMeeting" both java.util.Date. Now birthday stored in database as date type column (no time) for eg. 01-10-1979, and nextMeeting as datetime type for ex. 01-10-2010 20:00:00.
Person 对象有 2 个字段:“生日”和“nextMeeting”都是 java.util.Date。现在生日作为日期类型列(无时间)存储在数据库中,例如。01-10-1979 和 nextMeeting 作为日期时间类型,例如。01-10-2010 20:00:00。
You pull it from db, "birthday" will be auto set to midnight by JDBC. Now you need to send this object to other JVM using lets say RMI or whatever technology.
您从数据库中提取它,“生日”将被 JDBC 自动设置为午夜。现在您需要使用 RMI 或任何技术将此对象发送到其他 JVM。
On the other end JVM has timezone -1h from originating JVM. This is where problem starts. nextMeeting become 01-10-2010 19:00:00 which is absolutely FINE and CORRECT from user perspective etc...
另一方面,JVM 的时区与原始 JVM 的时区为 -1h。这就是问题开始的地方。nextMeeting 成为 01-10-2010 19:00:00 从用户的角度来看,这是绝对好的和正确的......
BUT birthday become 30-09-1979 23:00:00 which will be represented to user as 30th of September, which is really not what we want, cause obviously birthday is something static and NOT dependent on timezones.
但是生日变成了 30-09-1979 23:00:00 这将被表示为 9 月 30 日,这真的不是我们想要的,因为显然生日是静态的,不依赖于时区。
So column type in db chosen correctly (date). This type of column usually represented as java.util.Date. But in our case it is wrong java type to use.
所以正确选择了数据库中的列类型(日期)。这种类型的列通常表示为 java.util.Date。但在我们的例子中,使用错误的 java 类型。
So how would you represent a birthday? Consider that you need to manipulate this object on a UI like in a datepicker component etc...
那么如何代表生日呢?考虑到您需要在 UI 上操作此对象,例如在日期选择器组件等中...
采纳答案by Jeroen Rosenberg
Use LocalDatefrom JodaTimeand only store the date for the birthday, not the time.
使用LOCALDATE的从JodaTime并且只存储日期的生日,而不是时间。
回答by Basil Bourque
The other answers use outmoded classes.
其他答案使用过时的课程。
java.time
时间
Both Joda-Time and the old java.util.Date/.Calendar classes have been supplanted by the java.timeframework built into Java 8 and later. Defined by JSR 310. Extended by the ThreeTen-Extraproject. Back-ported to Java 6 & 7 by the ThreeTen-BackPortproject, which is wrapped for Android by the ThreeTenABPproject.
Joda-Time 和旧的 java.util.Date/.Calendar 类都已被Java 8 及更高版本中内置的java.time框架所取代。由JSR 310定义。由ThreeTen-Extra项目扩展。由ThreeTen-BackPort项目向后移植到 Java 6 和 7,该项目由ThreeTenABP项目为 Android 包装。
LocalDate
LocalDate
A date-only value without time-of-day and without time zone can be represented by the LocalDate
class. Such a class was lacking in the old date-time classes bundled with the earliest versions of Java. The old java.sql.Date
class pretends to be date-only but in fact has a time-of-day inherited from java.util.Date
(a date-time value).
没有时间和时区的仅日期值可以由LocalDate
类表示。与最早版本的 Java 捆绑在一起的旧日期时间类中缺少这样的类。旧java.sql.Date
类假装是仅限日期的,但实际上有一个从java.util.Date
(日期时间值)继承的时间。
LocalDate dateOfBirth = LocalDate.of( 1979 , 1 , 10 );
Without a time of day nor time zone, a date of birth is inherently inaccurate for determining one's age. But in nearly all use-cases we don't care; give-or-take part of a day is close enough.
如果没有一天中的时间或时区,出生日期对于确定一个人的年龄来说本质上是不准确的。但在几乎所有用例中,我们都不关心;一天中的给予或接受部分已经足够接近了。
ZonedDateTime
ZonedDateTime
For a meeting we cannot be so loosey-goosey. We need a date, a time, and a time zone. In java.time that means ZonedDateTime
class. The time zone is the key elementmissing from the scenario in the Question. Add the time zone and all is well.
对于会议,我们不能这么松懈。我们需要一个日期、时间和时区。在 java.time 中,这意味着ZonedDateTime
类。该时区是关键要素从问题的情况下丢失。添加时区,一切都很好。
ZoneId zoneIdMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtMontreal = ZonedDateTime.of( 2010 , 1 , 10 , 20 , 0 , 0 , zoneIdMontreal );
Now communicate the objects to another machine. Both remain intact, the same date-only value for date-of-birth (1979-01-10) and the same Montréal date-time for the meeting.
现在将对象传送到另一台机器。两者都保持不变,出生日期 (1979-01-10) 的相同日期值和会议的相同蒙特利尔日期时间。
Adjust time zone
调整时区
You might then want to adjust that meeting to another time zone expected by the person using this other machine.
然后,您可能希望将该会议调整为使用这台另一台机器的人期望的另一个时区。
ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdtMontreal.withZone( zoneIdParis );
We have the same moment on the timeline represented in two fashions, in two objects, zdtMontreal & zdtParis.
我们在时间轴上以两种方式表示同一时刻,在两个对象中,zdtMontreal 和 zdtParis。
LocalDateTime
LocalDateTime
If your nextMeeting
has no time zone nor offset-from-UTC info, then represent as a LocalDateTime
object. In the database it would be stored as a type like TIMESTAMP WITHOUT TIME ZONE
.
如果您nextMeeting
没有时区或 UTC 偏移量信息,则表示为一个LocalDateTime
对象。在数据库中,它将存储为类似TIMESTAMP WITHOUT TIME ZONE
.
Such values do notrepresent a moment in the timeline. They represent only a range of possible moments. To determine an actual moment you must provide the context of a specific time zone.
这些价值也不能代表在时间轴上的时刻。它们只代表一系列可能的时刻。要确定实际时刻,您必须提供特定时区的上下文。
For storing future date-time values such as a planned meeting further out than a few weeks, doing so without time zone may be appropriate. Politicians around the world have shown a proclivity for often changing Daylight Saving Time and otherwise redefining their time zones. And they often do so with little warning, as little as only several weeks warning.
为了存储未来的日期时间值,例如比几周更远的计划会议,这样做可能适合不带时区。世界各地的政客都表现出经常改变夏令时和重新定义时区的倾向。而且他们经常在没有警告的情况下这样做,甚至只有几周的警告。
To determine an actual moment such as showing a schedule in a calendar, apply a time zone ZoneId
to get a ZonedDateTime
.
要确定实际时刻(例如在日历中显示日程表),请应用时区ZoneId
以获取ZonedDateTime
.
ISO 8601
ISO 8601
The java.time classes use the standard ISO 8601formats by default when parsing/generating textual representations of date-time values. The ZonedDateTime
class goes one step further by extending ISO 8601 to append the name of the time zone in square brackets.
java.time 类在解析/生成日期时间值的文本表示时默认使用标准ISO 8601格式。该ZonedDateTime
班通过扩展ISO 8601追加在方括号中的时区的名称,以进一步进了一步。
If serializing values via text, use to the sensible and unambiguous formats of ISO 8601.
如果通过文本序列化值,请使用 ISO 8601 的合理且明确的格式。
None of the issues raised in the Question remain. Using an excellent date-time library and ISO 8601 such as java.time solves the problem.
问题中提出的问题都没有留下。使用优秀的日期时间库和 ISO 8601(例如 java.time)解决了这个问题。
Database
数据库
Your database should use date-only type for the birthdate, and a timestamp-with-time-zone type for the meeting (see SQL data typesin Wikipedia and in your database's documentation). Your JDBC driver mediates both types for you.
您的数据库应为生日使用仅日期类型,并为会议使用带时区的时间戳类型(请参阅维基百科和数据库文档中的SQL 数据类型)。您的 JDBC 驱动程序为您调解了这两种类型。
Eventually JDBC drivers will be updated to directly use java.time types. But until then we must convert to java.sql types such as java.sql.Date
and java.sql.Timestamp
. New methods have been added to the old classes to support these conversions.
最终 JDBC 驱动程序将更新为直接使用 java.time 类型。但在那之前,我们必须转换为 java.sql 类型,例如java.sql.Date
和java.sql.Timestamp
。已将新方法添加到旧类以支持这些转换。
java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );
Then call setDate
and setTimestamp
on your PreparedStatement
.
然后调用setDate
和setTimestamp
你的PreparedStatement
。
Going the other direction, from database to Java, call getDate
and getTimestamp
on your ResultSet
. Then translate immediately to java.time types, avoiding the use of the java.sql types in your business logic.
走向另一个方向,从数据库到Java,电话getDate
和getTimestamp
你的ResultSet
。然后立即转换为 java.time 类型,避免在业务逻辑中使用 java.sql 类型。
For the date-time value, we must go through an Instant
object. The Instant
is a moment on the timeline in UTC. We apply a time zone to get a wall-clock timefor the user.
对于日期时间值,我们必须通过一个Instant
对象。这Instant
是UTC时间线上的一个时刻。我们应用一个时区来为用户获取挂钟时间。
LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );
回答by gnud
Somehow, the two java systems will have to agree on Calendar/TimeZone information, or the Date object will need to be converted to a timestamp when being passed to the remote system.
不知何故,两个 java 系统必须就 Calendar/TimeZone 信息达成一致,否则 Date 对象在传递到远程系统时需要转换为时间戳。
The simplest method might be to simply require all clients to treat the birthday as a GMT time --- when they display/compare/whatever the birthdays, have them create a Calendar
with the "GMT"
TimeZone
, and then setTime()
on it with the supplied Date
.
最简单的方法可能是简单地要求所有客户端将生日视为 GMT 时间——当他们显示/比较/无论生日时,让他们创建一个Calendar
带有 的"GMT"
TimeZone
,然后setTime()
使用提供的Date
.
If you're working with the model locally at all, you should really have a Date object, not just a timestamp.
如果您完全在本地使用模型,那么您真的应该有一个 Date 对象,而不仅仅是一个时间戳。
回答by Damian Leszczyński - Vash
For manipulation i will advise java.util.Calendar
对于操作,我会建议 java.util.Calendar
For representation
为代表
Birthday as java.util.Date
NextMeetin as java.sql.Timestamp
生日作为 java.util.Date
NextMeetin 作为 java.sql.Timestamp
回答by krtek
That's a very good question...
这是一个很好的问题...
Android for example stores birthday as String in 'yyyy-MM-dd' format. I was wondering why they don't use java.util.Date and I guess that the reason would be the same problem which you brought here.
例如,Android 将生日存储为“yyyy-MM-dd”格式的字符串。我想知道为什么他们不使用 java.util.Date ,我想原因与您带到这里的问题相同。
So I would recomment either String or some "Timezone-independent-date". But after few minutes searching in Java and joda-time documentation I have no idea how to do it.
所以我会推荐字符串或一些“时区独立日期”。但是在 Java 和 joda-time 文档中搜索几分钟后,我不知道该怎么做。
EDIT: Seems @Jeroen is right - use LocalDate.
编辑:似乎@Jeroen 是对的 - 使用 LocalDate。