1

我的 xmlGregorianCalendar 值为 2020-10-02T13:07:38-06:00 .. 我想传递这个 xmlGregorianCalendar 值并获得像 2020-10-02T07:07:38 这样的输出(从那时起减少 6 小时)任何请对此提出建议?

我已经使用了以下方法。

return new Timestamp(xmlGregorianCalendar.toGregorianCalendar(TimeZone.getTimeZone("GMT"),null,null).getTimeInMillis())..

但它删除了偏移值但不调整时间。我得到的输出是 2020-10-02T13:07:38 ..但我期待 2020-10-02T07:07:38 像这样。

4

2 回答 2

2

首先,我建议您从过时且容易出错的java.util日期时间 API 切换到现代 java.time日期时间 API。从Trail: Date Time了解有关现代日期时间 API 的更多信息。

您对 Zone-Offset 的理解不正确

日期时间字符串2020-10-02T13:07:38-06:00告诉我们,给定的日期和时间已经调整了 -06:00 小时的偏移量,UTC即相应的日期时间在UTC哪里2020-10-02T19:07:38Z指定Z小时的区域偏移量00:00

这意味着,如果您期望的日期时间为2020-10-02T07:07:38,则需要将给定的日期时间进一步偏移-06:00几小时,即它总共-12:00偏移UTC.

以下示例说明了这个概念:

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime odtGiven = OffsetDateTime.parse("2020-10-02T13:07:38-06:00");
        System.out.println(odtGiven);

        // Date and time at Zone-Offset of 00:00 hours
        OffsetDateTime odtUTC = odtGiven.withOffsetSameInstant(ZoneOffset.UTC);
        System.out.println(odtUTC);

        // Date and time at Zone-Offset of -12:00 hours
        OffsetDateTime odtDerived = odtGiven.withOffsetSameInstant(ZoneOffset.of("-12:00"));
        System.out.println(odtDerived);

        // Get the date-time string in the format with Zone-Offset dropped
        String strDateTimeZoneOffsetDropped = odtDerived.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        System.out.println(strDateTimeZoneOffsetDropped);
    }
}

输出:

2020-10-02T13:07:38-06:00
2020-10-02T19:07:38Z
2020-10-02T07:07:38-12:00
2020-10-02T07:07:38

使用旧版 API:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public class Main {
    public static void main(String[] args) throws DatatypeConfigurationException, ParseException {
        String givenDateTimeString = "2020-10-02T13:07:38-06:00";
        XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
                .newXMLGregorianCalendar(givenDateTimeString);
        System.out.println(xmlGregorianCalendar);

        // Derive the date-time string at Zone-Offset of UTC-12:00 hours
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT-12:00"));
        Date date = xmlGregorianCalendar.toGregorianCalendar().getTime();
        String derivedDateTimeString = sdf.format(date);
        System.out.println(derivedDateTimeString);
    }
}

输出:

2020-10-02T13:07:38-06:00
2020-10-02T07:07:38
于 2020-10-03T10:25:29.107 回答
1

JDBC 4.2 和 java.time

Arvind Kumar Avinash 已经很好地解释了偏移的工作原理(XMLGregorianCalendar误导性地称为时区)。他还推荐了现代 Java 日期和时间 API java.time。我想详细说明这项建议。假设你认为你想要一个java.sql.Timestamp用于你的 SQL 数据库,你不应该想要那个。从 JDBC 4.2 和 Hibernate 5 开始,我们可以无缝地将 java.time 的类型传输到我们的 SQL 数据库。我建议您改为这样做。

如果在 SQL 中使用带时区的时间戳,请在 Java 中使用 UTC 中的 OffsetDateTime

如果 SQL 中的数据类型是timestamp with time zone(recommended for timestamps),则将一个OffsetDateTime从 Java 传输到它。对于大多数数据库引擎来说,timestamp with time zone实际上意味着UTC 中的时间戳,所以为了清楚起见,我更喜欢传递一个UTC 格式的时间戳OffsetDateTime。有几种转换方法,每种方法都有其优点和缺点。

1. 通过 GregorianCalendar 转换

    // For demonstration build an XMLGregorianCalenadr equivalent to yours
    XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
            .newXMLGregorianCalendar("2020-10-02T13:07:38-06:00");
    
    // Convert to OffsetDateTime for your SQL database
    OffsetDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
            .toZonedDateTime()
            .toOffsetDateTime()
            .withOffsetSameInstant(ZoneOffset.UTC);

    // Show what we’ve got
    System.out.println(dateTime);

输出是:

2020-10-02T19:07:38Z

亲:这是官方的转换。

或者2. 通过 String 转换

    OffsetDateTime dateTime = OffsetDateTime.parse(xmlGregorianCalendar.toString())
            .withOffsetSameInstant(ZoneOffset.UTC);

Pro:它很短,我认为它不会给人任何惊喜。缺点:对我来说,格式化为字符串并再次解析回日期时间对象感觉像是一种浪费。

3. 通过 int 转换。您可以通过将各个字段从其中取出XMLGregorianCalendar并从中构建一个来进行转换OffsetDateTime。它变得冗长,处理秒的分数有点复杂,并且存在意外交换字段的风险,所以我不推荐它。

如何传递给 SQL。这是一个转移OffsetDateTime到 SQL 的例子。

    String sqlString = "insert into your_table(your_timestamp_column) values (?);";
    PreparedStatement ps = yourDatabaseConnection.prepareStatement(sqlString);
    ps.setObject(1, dateTime);
    int rowsInserted = ps.executeUpdate();

如果在 SQL 中使用没有时区的时间戳,则从 Java 传递 LocalDateTime

如果 SQL 中的数据类型是timestamp无时区的(不推荐用于时间戳,但经常看到),则需要LocalDateTime从 Java 传递一个。同样有几种方法可以转换。我只展示一个:

    LocalDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
            .toZonedDateTime()
            .withZoneSameInstant(ZoneId.systemDefault())
            .toLocalDateTime();

我使用 JVM 的默认时区与老式Timestamp类所做的一致,因此结果取决于时区。在我的时区(欧洲/哥本哈根)目前在 UTC 偏移 +02:00,结果是:

2020-10-02T21:07:38

你的代码出了什么问题?

您使用的三参数XMLGregorianCalendar.toGregorianCalendar​(TimeZone, Locale, XMLGregorianCalendar)旨在引入与您观察到的错误类似的错误。它承诺GregorianCalendar使用指定的时区(如果已给出)和来自XMLGregorianCalendar(年、月、日、小时、分钟等)的字段值来构造 a。所以文档说得很清楚:如果您指定的时区与 的偏移量不一致XMLGregorianCalendar,它会为您提供与XMLGregorianCalendar. XMLGregorianCalendar如果没有任何偏移并且我们知道要使用哪个时区,它有时可能会很有用。但是你XMLGregorianCalendar有偏移,所以这个方法绝对不是你想要使用的。

链接

于 2020-10-03T12:07:57.013 回答