操纵和存储日期的最佳做法是什么?在企业Java应用程序中使用GregorianCalendar?
寻找反馈,我会将任何好的答案合并到其他人可以使用的最佳实践中。谢谢!
答案 0 :(得分:25)
最佳做法通常是 NOT 来考虑重日期对象,但要存储一个时间点。这通常通过存储不受角落情况影响的值或潜在解析问题来完成。要做到这一点,人们通常会存储自我们称之为纪元(1970-01-01)的固定点以来经过的毫秒数(或秒数)。这是非常常见的,任何Java API将始终允许您将任何类型的日期转换为自纪元以来以毫秒表示的时间。
那是存储空间。如果有此需要,您还可以存储用户首选的时区。
现在是以毫秒为单位的日期,例如:
System.out.println( System.currentTimeMillis() );
1264875453
在向最终用户显示时不是很有用,这是理所当然的。
这就是为什么你使用例如Joda时间将它转换为某种用户友好格式然后再显示给最终用户的原因。
您要求最佳实践,这是我的看法:将“日期”对象存储在数据库而不是以毫秒为单位的时间就在那里,使用浮点数表示货币金额。
这通常是一种巨大的代码味道。
所以Joda在Java中的时间是操纵日期的方式,是的。但Joda是否可以去商店日期? 某些不。
答案 1 :(得分:13)
Joda是要走的路。为什么?
在某个阶段,Java Date / Time API将被取代(由JSR-310)。我相信这将基于Joda背后的人所做的工作,因此您将学习一种影响新标准Java API的API。
答案 2 :(得分:11)
Joda time(100%可与JDK互操作)
Joda-Time为Java日期和时间类提供了高质量的替代品。该设计允许多个日历系统,同时仍提供简单的API
答案 3 :(得分:3)
在UTC而不是任何时区思考,处理和存储数据。将UTC视为 One True Time ,所有其他时区仅仅是变化。因此,在编码时,忘记所有关于您自己的时区。以UTC格式进行业务逻辑,日志记录,数据存储和数据交换。我建议每个程序员在他们的桌面上设置第二个时钟设置为UTC。
现代方式是java.time类。
上述Joda-Time项目为java.time类提供了灵感,项目现在处于维护模式,团队建议迁移到java.time类。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,.Calendar
和& java.text.SimpleDateFormat
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
将日期时间值序列化为文本时,请使用ISO 8601标准。
例如,UTC中的日期时间为2016-10-17T01:24:35Z
,其中Z
是Zulu
的缩写,表示UTC。对于其他offset-from-UTC,小时和分钟的偏移量会显示在最后,例如2016-01-23T12:34:56+05:30
。 java.time类扩展了此标准格式,以便在时间括号中附加时区名称(如果已知),例如2016-01-23T12:34:56+05:30[Asia/Kolkata]
。
该标准还有许多其他方便的格式,包括durations,intervals,ordinals和year-week。
对于数据库存储,请将日期时间类型用于日期时间值,例如主要为DATE
,TIME
和TIMESTAMP WITH TIME ZONE
的{{3}}。
让你的SQL standard data types做繁重的工作。驱动程序处理有关在Java处理数据的方式和数据库如何处理数据的内部之间进行调解和调整的细节。但请务必使用示例数据来了解驱动程序和数据库的行为。 SQL标准对日期时间处理的定义很少,因此行为差异很大,令人惊讶的是。
如果使用符合JDBC 4.2及更高版本的JDBC驱动程序,则可以通过ResultSet::getObject
和PreparedStatement::setObject
方法直接获取和存储java.time类型。
Instant instant = myResultSet.getObject( … );
myPreparedStatement.setObject( … , instant );
对于较旧的驱动程序,您需要回退到通过java.sql类型进行转换。寻找添加到旧类的新转换方法。例如,JDBC driver。
Instant instant = myResultSet.getTimestamp( … ).toInstant();
myPreparedStatement.setObject( … , java.sql.Timestamp.from( instant ) );
尽可能简短地使用java.sql类型。它们是一个设计糟糕的黑客,例如java.sql.Date
伪装成仅限日期的值但实际上是java.util.Date
的子类,它确实有一个时间设置为00:00:00
在UTC中。而且,哦,你应该忽略继承说类课程的事实。一个丑陋的混乱。
以UTC格式获取当前时刻。
Instant instant = Instant.now();
上面显示了将Instant
对象存储到数据库/从数据库中获取对象。
要生成ISO 8601字符串,只需调用toString
即可。默认情况下,java.time类都使用ISO 8601格式来解析和生成各种日期时间值的字符串。
String output = instant.toString();
通过应用ZoneOffset
来调整到UTC的任何偏移量以获得OffsetDateTime
。调用toString
生成ISO 8601格式的字符串。
ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 );
OffsetDateTime odt = instant.atOffset( offset );
时区是偏移量加上用于处理java.sql.Timestamp.toInstant()
等异常的一组规则。如果您需要通过某个地区自己Daylight Saving Time (DST)的镜头看到同一时刻,请应用时区(wall-clock time)来获取ZoneId
对象。
以continent/region
的格式指定ZonedDateTime
。切勿使用诸如EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
转向另一个方向,您可以通过调用Instant
从OffsetDateTime
或ZonedDateTime
中提取toInstant
。
Instant instant = zdt.toInstant();
用于以ISO 8601以外的格式向用户呈现proper time zone name以使用DateTimeFormatter
类。
虽然您可以指定自定义格式,但通常最好让java.time自动本地化。要进行本地化,请指定:
FormatStyle
确定(a)翻译日期名称,月份名称等的人类语言,以及(b)决定缩写,大小写,标点符号等问题的文化规范。< / LI>
示例:
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
尽可能避免使用旧版日期时间类型。但是如果使用尚未针对java.time类型更新的旧代码,则可以转换为java.time类型。有关详细信息,请参阅问题Locale
。
使用对象而不仅仅是编码基元和简单字符串。例如:
DayOfWeek.TUESDAY
等Convert java.util.Date to what “java.time” type?枚举。 DayOfWeek
个对象。 LocalDate
个对象。 YearMonth
枚举,例如Month
。使用此类对象可使您的代码更加自我记录,确保有效值,并提供Month.JANUARY
。
答案 4 :(得分:-2)
为了开始讨论,以下是我的经历:
在为典型的3层Java Enterprise项目创建标准时,我通常建议项目使用GregorianCalendar来操作日期。理由是GregorianCalendar是任何其他Calendar实例的事实上的标准,例如朱利安日历等它是大多数国家公认的日历,并正确处理闰年等。最重要的是,我建议应用程序将其日期存储为UTC,以便您可以轻松执行日期计算,例如查找两者之间的差异日期(如果它存储为EST,例如,您必须考虑日光节省时间)。然后可以将日期本地化为您需要将其显示给用户的任何时区 - 例如,如果您是美国东海岸公司并且您希望在EST中显示您的时间信息,则将其本地化为EST。