在Java中操作和存储日期的最佳实践是什么?

Bes*_*ces 27 java date

操作和存储日期的最佳实践是什么,例如在企业Java应用程序中使用GregorianCalendar?

寻找反馈,我会将任何好的答案合并到其他人可以使用的最佳实践中.谢谢!

Syn*_*r0r 25

最佳实践通常不是考虑重日期对象而是考虑存储时间点.这通常通过存储不受角落情况影响的值或潜在解析问题来完成.要做到这一点,人们通常会存储自我们称之为纪元(1970-01-01)的固定点以来经过的毫秒数(或秒数).这是非常常见的,任何Java API总是允许您将任何类型的日期转换为自纪元以来以ms表示的时间.

这是存储.如果有此需要,您还可以存储用户首选的时区.

现在这样的日期以毫秒为单位,如:

System.out.println( System.currentTimeMillis() );
1264875453
Run Code Online (Sandbox Code Playgroud)

当它显示给最终用户时不是很有用,这是理所当然的.

这就是为什么你使用例如Joda时间将它转换为某种用户友好格式然后再显示给最终用户的原因.

您要求最佳实践,这是我的看法:在数据库中存储"日期"对象而不是以毫秒为单位的时间,使用浮点数表示货币金额.

它通常是一个巨大的代码味道.

所以Joda在Java中的时间是操纵日期的方式,是的.但是Joda是去商店约会的方式吗?某些不是.


Bri*_*new 13

Joda是要走的路.为什么?

  1. 它具有比标准日期/时间API更强大和直观的界面
  2. 日期/时间格式没有线程问题.java.text.SimpleDateFormat不是线程安全的(不是很多人都知道这个!)

在某个阶段,Java Date/Time API将被取代(由JSR-310).我相信这将基于Joda背后的人所做的工作,因此你将学习一个影响新标准Java API的API.


Boz*_*zho 11

Joda时间(100%与JDK互操作)

Joda-Time为Java日期和时间类提供了高质量的替代品.该设计允许多个日历系统,同时仍提供简单的API


Bas*_*que 7

世界标准时间

UTC而不是任何时区来思考、工作和存储数据。将 UTC 视为One True Time,所有其他时区都只是变体。因此,在编码时,请忘记您自己的时区。在 UTC 中执行您的业务逻辑、日志记录、数据存储和数据交换。我建议每个程序员在他们的办公桌上保留一个设置为 UTC 的第二个时钟。

时间

现代方式是java.time类。

提到的Joda-Time项目为 java.time 类提供了灵感,该项目现在处于维护模式,团队建议迁移到 java.time 类。

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

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

从哪里获得 java.time 类?

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

ISO 8601

将日期时间值序列化为文本时,请使用ISO 8601标准。

例如,UTC 中的日期时间是 UTC2016-10-17T01:24:35ZZ缩写Zulu并表示 UTC。对于其他从 UTC 开始的偏移量,小时和分钟的偏移量出现在末尾,例如2016-01-23T12:34:56+05:30. java.time 类扩展了此标准格式,将时区名称(如果已知)附加在方括号中,例如2016-01-23T12:34:56+05:30[Asia/Kolkata].

该标准还有许多其他方便的格式,包括持续时间间隔序数年-周

数据库

用于数据库存储,使用日期时间类型日期时间值,如SQL标准的数据类型,其主要是DATETIME,和TIMESTAMP WITH TIME ZONE

让您的JDBC 驱动程序完成繁重的工作。驱动程序处理有关在 Java 如何处理数据的内部结构和您的数据库如何在其侧处理数据之间进行调解和调整的基本细节。但一定要使用示例数据进行练习,以了解驱动程序和数据库的行为。SQL 标准对日期时间处理的定义很少,因此行为差异很大,令人惊讶。

如果使用符合 JDBC 4.2 及更高版本的 JDBC 驱动程序,则可以直接通过ResultSet::getObjectPreparedStatement::setObject方法获取和存储 java.time 类型。

Instant instant = myResultSet.getObject( … );
myPreparedStatement.setObject( … , instant );
Run Code Online (Sandbox Code Playgroud)

对于较旧的驱动程序,您需要回退到通过 java.sql 类型进行转换。寻找添加到旧类的新转换方法。例如,java.sql.Timestamp.toInstant()

Instant instant = myResultSet.getTimestamp( … ).toInstant();
myPreparedStatement.setObject( … , java.sql.Timestamp.from( instant ) );
Run Code Online (Sandbox Code Playgroud)

尽可能简短地使用 java.sql 类型。它们是一个设计糟糕的黑客,例如java.sql.Date伪装成仅日期值,但实际上作为java.util.Date它的子类确实将时间设置为00:00:00UTC。而且,哦,你应该忽略继承的事实,类文档说。一个丑陋的混乱。

示例代码

获取 UTC 中的当前时刻。

Instant instant = Instant.now();
Run Code Online (Sandbox Code Playgroud)

Instant上面显示了在数据库中存储和获取该对象。

要生成 ISO 8601 字符串,只需调用toString. 默认情况下,java.time 类都使用 ISO 8601 格式来解析和生成各种日期时间值的字符串。

String output = instant.toString();
Run Code Online (Sandbox Code Playgroud)

通过应用 aZoneOffset以获得OffsetDateTime. 调用toString以生成 ISO 8601 格式的字符串。

ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 );
OffsetDateTime odt = instant.atOffset( offset );
Run Code Online (Sandbox Code Playgroud)

时区是一个偏移量加上一组用于处理夏令时 (DST)等异常的规则。当您需要通过某个地区自己的挂钟时间的镜头看到同一时刻,请应用时区 ( ZoneId) 来获取ZonedDateTime对象。

以 格式指定正确的时区名称continent/region。永远不要使用 3-4 个字母的缩写,例如ESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
Run Code Online (Sandbox Code Playgroud)

反过来,您可以Instant从 anOffsetDateTimeZonedDateTime通过调用toInstant.

Instant instant = zdt.toInstant();
Run Code Online (Sandbox Code Playgroud)

格式化

要以 ISO 8601 以外的格式向用户展示字符串,请搜索 Stack Overflow以使用DateTimeFormatter该类。

虽然您可以指定自定义格式,但通常最好让 java.time 自动本地化。要本地化,请指定:

  • FormatStyle 确定字符串的长度或缩写。
  • Locale 确定 (a) 用于翻译日名、月名等的人类语言,以及 (b) 决定缩写、大写、标点等问题的文化规范。

例子:

Locale l = Locale.CANADA_FRENCH ; 
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
Run Code Online (Sandbox Code Playgroud)

转换

最好尽可能避免使用旧的日期时间类型。但是,如果使用尚未针对 java.time 类型更新的旧代码,则可以在 java.time 类型之间进行转换。有关详细信息,请参阅问题,将 java.util.Date 转换为什么“java.time”类型?.

使用对象

使用对象而不仅仅是编码的原语和简单的字符串。例如:

  • 不要使用 1-7 来表示星期几,请使用DayOfWeek枚举,例如DayOfWeek.TUESDAY.
  • 不是将字符串作为日期传递,而是传递LocalDate对象。
  • 不是传递一对整数来表示年和月,而是传递YearMonth对象。
  • 而不是一个月的 1-12,使用更具可读性的Month枚举,例如Month.JANUARY.

使用这样的对象可以让你的代码更加自我记录,确保有效的值,并提供类型安全