来自ZonedDateTime UTC实例的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值,但我找不到具体的声明。

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

例:

 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); } 

输出:

  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;博士

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

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

使用java.time

在你使用新的现代java.time类之前,程序员现在取代了臭名昭着的旧的遗留日期时间类,如DateCalendarTimestamp

Instant

Instant类表示UTC时间轴上的一个时刻,分辨率为纳秒 (最多九(9)位小数)。 获取UTC中的当前时刻非常简单: Instant.now

 Instant instant = Instant.now(); 

转换

您应该坚持使用java.time类,并避免使用遗留类。 但是如果绝对必要,例如与尚未针对java.time更新的旧代码接口,则可以转换为/从java.time。 查看旧类的新方法。 遗留类java.util.Date等效于Instant

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

JDBC

避免使用旧版日期时间类。 请改用java.time类。

您的JDBC 4.2兼容驱动程序可以通过调用PreparedStatement::setObjectResultSet::getObject直接寻址java.time类型。

 myPreparedStatement.setObject( … , instant ) ; 

……而且……

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

如果没有,请回到使用java.sql类型,但尽可能简短。 使用添加到旧类的新转换方法。

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

……而且……

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

不需要ZonedDateTime

请注意,我们不需要您提到的ZonedDateTime因为您说您只对UTC感兴趣。 Instant对象始终为UTC。 这意味着您引用的原始代码:

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

……可以简单地缩短为:

 Date.from( Instant.now() ) ; 

请注意, java.util.Date也始终为UTC。 但是,不幸的是,它的toString在生成String时隐式地应用了JVM的当前默认时区。 通过在Stack Overflow上搜索,您可以看到这种反特征不会产生混淆。

如果要通过区域挂钟时间的镜头查看Instant对象的UTC值,请指定时区ZoneId以获取ZoneDateTime

continent/region的格式指定适当的时区名称 ,例如America/MontrealAfrica/CasablancaPacific/Auckland 。 切勿使用3-4字母缩写,如CDTESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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

关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

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

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

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

从哪里获取java.time类?

  • Java SE 8Java SE 9Java SE 10Java SE 11及更高版本 – 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。

在Java中, Date代表一个时间点。 它与时间戳无关。 当您调用Date对象的toString()方法时,它会将该时间转换为Platform的默认时间戳,例如,Follow将以UTC格式打印日期/时间(因为它将默认时区设置为UTC):

 TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 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); 

我创建了如下所示的SimpleJdbcUpdate类:

 public class SimpleJdbcUpdate { private final JdbcTemplate jdbcTemplate; private DataSource dataSource; private String tableName; private final TableMetaDataContext tableMetaDataContext = new TableMetaDataContext(); Map propertyToColumnMap = new HashMap<>(); private boolean compiled; public SimpleJdbcUpdate(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); this.dataSource = dataSource; } private List getColumnNames() { return Collections.emptyList(); } private String[] getGeneratedKeyNames() { return new String[0]; } public SimpleJdbcUpdate withTableName(String tableName) { this.tableName = tableName; return this; } public int execute(BeanPropertySqlParameterSource parameterSource, String[] keys) { if (!compiled) { compile(); } return doExecute(parameterSource, keys); } private int doExecute(BeanPropertySqlParameterSource parameterSource, String[] keys) { String[] propertyNames = parameterSource.getParameterNames(); List values = new ArrayList<>(); String updateSql = updateSql(parameterSource, propertyNames, values); String wherePart = wherePart(parameterSource, keys, values); String updateSqlWithWhere = updateSql + wherePart; return jdbcTemplate.update(updateSqlWithWhere, values.toArray()); } private String updateSql(BeanPropertySqlParameterSource parameterSource, String[] propertyNames, List values) { StringBuilder updateSqlBuilder = new StringBuilder("update " + tableName + " set "); boolean first = true; for (String propertyName : propertyNames) { ColumnInfo columnInfo = propertyToColumnMap.get(propertyName); if (columnInfo == null) { continue; } addValue(parameterSource, values, propertyName); if (!first) { updateSqlBuilder.append(", "); } updateSqlBuilder.append(columnInfo.columnName + " = ?"); first = false; } return updateSqlBuilder.toString(); } private String wherePart(BeanPropertySqlParameterSource parameterSource, String[] keys, List values) { StringBuilder wherePartBuilder = new StringBuilder(); boolean first = true; for (String key : keys) { ColumnInfo columnInfo = propertyToColumnMap.get(key); if (columnInfo == null) { continue; } addValue(parameterSource, values, key); if (first) { wherePartBuilder.append(" WHERE "); } else { wherePartBuilder.append(" AND "); } wherePartBuilder.append(columnInfo.columnName + " = ?"); first = false; } return wherePartBuilder.toString(); } private void addValue(BeanPropertySqlParameterSource parameterSource, List values, String propertyName) { if (parameterSource.hasValue(propertyName)) { Object typedValue = SqlParameterSourceUtils.getTypedValue(parameterSource, propertyName); if (typedValue.getClass().isEnum()) { typedValue = new SqlParameterValue(Types.VARCHAR, ((Enum) typedValue).name()); } values.add(typedValue); } } private void compile() { tableMetaDataContext.setTableName(tableName); this.tableMetaDataContext.processMetaData(dataSource, getColumnNames(), getGeneratedKeyNames()); List tableColumns = tableMetaDataContext.getTableColumns(); for (int i = 0; i < tableColumns.size(); i++) { String column = tableColumns.get(i); String propertyName = JdbcUtils.convertUnderscoreNameToPropertyName(column); propertyToColumnMap.put(propertyName, new ColumnInfo(column, i)); } compiled = true; } private static class ColumnInfo { String columnName; Integer index; public ColumnInfo(String columnName, Integer index) { this.columnName = columnName; this.index = index; } } }