来自 ZonedDateTime UTC 实例的 Java 日期和时间戳

2022-01-13 00:00:00 timestamp sql date java

我有一个 java 应用程序,我想在其中使用 UTC 时间.目前,代码混合使用 java.util.Datejava.sql.Timestamp.为了获得UTC时间,我之前的程序员使用:

日期:

 Date.from(ZonedDateTime.now(ZoneOffset.UTC)).toInstant();

对于时间戳:

 Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());

但是,我自己使用此代码运行了多个测试,并且这两行都返回当前日期/时间(在我当前的时区中).从我读过的所有内容中看来 Date/Timestamp 没有 zoneOffset 值,但我找不到具体的说明.

无论如何要在 Date 或 Timestamp 对象中保留 timeZone (UTC),还是我需要进行一些重构并在整个应用程序中使用实际的 ZonedDateTime 对象?这个 ZonedDateTime 对象还会与当前的 sql 时间戳对象兼容吗?

例子:

public static void main (String args[]){ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC);时间戳时间戳 = Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());日期日期 = Date.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());System.out.println("ZonedDateTime:" + zonedDateTime);System.out.println("时间戳:" + 时间戳);System.out.println("日期:" + date);}

输出:

 ZonedDateTime: 2017-04-06T15:46:33.099Z时间戳:2017-04-06 10:46:33.109日期:2017 年 4 月 6 日星期四 10:46:33 CDT

解决方案

tl;dr

Instant.now()//以 UTC 格式捕获当前时刻,分辨率高达纳秒.

仅使用 java.time 类.避免在 Java 8 之前添加的麻烦的旧旧日期时间类.

使用 java.time

您之前的程序员正在使用新的现代 java.time 类,这些类现在取代了臭名昭著的旧日期时间类,例如 DateCalendar时间戳.

即时

<小时>

关于java.time

java.time 框架内置于 Java 8 及更高版本中.这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date, 日历, &SimpleDateFormat.

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

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

您可以直接与您的数据库交换 java.time 对象.使用符合 JDBC 驱动程序jeps/170" rel="noreferrer">JDBC 4.2 或更高版本.不需要字符串,不需要 java.sql.* 类.

从哪里获得 java.time 类?

  • Java SE 8, Java SE 9, Java SE 10,Java SE 11 及更高版本 - 具有捆绑实现的标准 Java API 的一部分.
    • Java 9 添加了一些小功能和修复.
  • Java SE 6 和 Java SE 7
    • 大部分 java.time 功能都向后移植到 Java 6 &7 在 ThreeTen-Backport.
  • Android
    • java.time 类的 Android 捆绑包实现的更高版本.
    • 对于早期的 Android (<26),ThreeTenABP 项目适应 ThreeTen-Backport(如上所述).请参阅如何使用 ThreeTenABP….

ThreeTen-Extra 项目扩展了 java.time额外的课程.该项目是未来可能添加到 java.time 的试验场.您可以在这里找到一些有用的类,例如 Interval, YearWeek, YearQuarter 和 更多.

I have a java application in which I would like the time in UTC. Currently, the code uses a mix of java.util.Date and java.sql.Timestamp. To get the time in UTC, the programmer before me used:

For Date:

 Date.from(ZonedDateTime.now(ZoneOffset.UTC)).toInstant();

For Timestamp:

 Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());

However I have run multiple tests myself with this code and both of these lines return the current date/time(in my current timezone). From everything I have read it appears that Date/Timestamp does not have a zoneOffset value, but I cannot find a concrete statement of this.

Is there anyway to keep the timeZone (UTC) within the Date or Timestamp objects, or do I need to do some refactoring and use the actual ZonedDateTime object throughout my application? Also will this ZonedDateTime object be compatible with the current Timestamp object for sql?

Example:

public static void main (String args[])
{
    ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC);
    Timestamp timestamp = Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());
    Date date = Date.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());
    System.out.println("ZonedDateTime: " + zonedDateTime);
    System.out.println("Timestamp: " + timestamp);
    System.out.println("Date: " + date);
}

Output:

 ZonedDateTime: 2017-04-06T15:46:33.099Z
 Timestamp: 2017-04-06 10:46:33.109
 Date: Thu Apr 06 10:46:33 CDT 2017

解决方案

tl;dr

Instant.now()  // Capture the current moment in UTC with a resolution up to nanoseconds.

Use only java.time classes. Avoid the troublesome old legacy date-time classes added before Java 8.

Using java.time

The programmer before you was making use of the new modern java.time classes that now supplant the notoriously troublesome old legacy date-time classes such as Date, Calendar, Timestamp.

Instant

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction). To get the current moment in UTC is utterly simple: Instant.now.

Instant instant = Instant.now();

Converting

You should stick to the java.time classes, and avoid the legacy classes. But if absolutely necessary such as interfacing with old code not yet updated for java.time, you may convert to/from java.time. Look to new methods on old classes. The legacy class java.util.Date equivalent is Instant.

java.util.Date d = java.util.Date.from( myInstant); // To legacy from modern.
Instant instant = myJavaUtilDate.toInstant();  // To modern from legacy.

JDBC

Avoid the legacy date-time classes. Use java.time classes instead.

Your JDBC 4.2 compliant driver may be able to directly address java.time types by calling PreparedStatement::setObject and ResultSet::getObject.

myPreparedStatement.setObject( … , instant ) ;

… and …

Instant instant = myResultSet.getObject( … , Instant.class ) ;

If not, fall back to using the java.sql types, but as briefly as possible. Use new conversion methods added to the old classes.

myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;

… and …

Instant instant = myResultSet.getTimestamp( … ).toInstant() ;

No need for ZonedDateTime

Notice that we had no need for your mentioned ZonedDateTime as you said you were only interested in UTC. The Instant objects are always in UTC. That means that original code you quoted:

Date.from(ZonedDateTime.now(ZoneOffset.UTC)).toInstant();

…could have simply been shortened to:

Date.from( Instant.now() ) ;

Note that java.util.Date is always in UTC as well. However, its toString unfortunately applies the JVM’ current default time zone implicitly while generating the String. This anti-feature creates no end of confusion as you can see by searching on Stack Overflow.

If you want to see your Instant object’s UTC value through the lens of a region’s wall-clock time, assign a time zone ZoneId to get a ZoneDateTime.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as CDT or EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Chicago" );
ZonedDateTime zdt = instant.atZone( z );



About java.time

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

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

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

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and 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 (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project 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.

相关文章