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

时间:2017-04-06 15:37:47

标签: java sql date timestamp

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

日期:

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

对于时间戳:

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

但是我自己使用此代码运行了多个测试,这两行都返回当前日期/时间(在我当前的时区)。从我读过的所有内容出现日期/时间戳没有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

3 个答案:

答案 0 :(得分:29)

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中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(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兼容driver可以通过调用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,您可以看到这种反特征不会产生混淆。

如果您希望通过区域wall-clock time的镜头查看Instant对象的UTC值,请指定时区ZoneId以获得ZoneDateTime

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

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

关于 java.time

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

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

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

答案 1 :(得分:5)

在Java中,Date代表一个时间点。它与时间戳无关。当您调用toString()对象的Date方法时,它会将该时间转换为平台的默认时间戳,例如以下将以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);

答案 2 :(得分:0)

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

public class SimpleJdbcUpdate {

private final JdbcTemplate jdbcTemplate;
private DataSource dataSource;
private String tableName;
private final TableMetaDataContext tableMetaDataContext = new TableMetaDataContext();
Map<String, ColumnInfo> propertyToColumnMap = new HashMap<>();
private boolean compiled;

public SimpleJdbcUpdate(DataSource dataSource) {
    jdbcTemplate = new JdbcTemplate(dataSource);
    this.dataSource = dataSource;
}

private List<String> 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<Object> 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<Object> 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<Object> 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<Object> 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<String> 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;
    }
  }
}