英文:
My xmlGregorianCalendar coming with offset value ..how to adjust it? in java
问题
My xmlGregorianCalendar value coming as 2020-10-02T13:07:38-06:00 .. I want to pass this xmlGregorianCalendar value and get the output like 2020-10-02T07:07:38 (which reduces 6 hours from the time) any suggestion on this please?
I have used this below method.
return new Timestamp(xmlGregorianCalendar.toGregorianCalendar(TimeZone.getTimeZone("GMT"),null,null).getTimeInMillis())..
But it removing the off set value but not adjusting the time. the output I am getting is 2020-10-02T13:07:38.. But I am expecting the 2020-10-02T07:07:38 like this.
英文:
My xmlGregorianCalendar value coming as 2020-10-02T13:07:38-06:00 .. I want to pass thisxmlGregorianCalendar value and get the output like 2020-10-02T07:07:38(which reduces 6 hours from the time) any suggestion on this please?
I have used this below method.
return new Timestamp(xmlGregorianCalendar.toGregorianCalendar(TimeZone.getTimeZone("GMT"),null,null).getTimeInMillis())..
But it removing the off set value but not adjusting the time. the output i am getting is 2020-10-02T13:07:38..But i am expecting the 2020-10-02T07:07:38 like this.
答案1
得分: 2
首先,我建议您从过时且容易出错的 java.util
日期时间 API 切换到现代的 java.time
日期时间 API。从 Trail: Date Time 了解更多关于现代日期时间 API 的内容。
你对 Zone-Offset 的理解是不正确的
日期时间字符串 2020-10-02T13:07:38-06:00
告诉我们,给定的日期和时间已经根据偏移量调整为比 UTC
少 6 小时,即相应的 UTC
日期时间将是 2020-10-02T19:07:38Z
,其中 Z
指定了 00:00
小时的区偏移量。
这意味着,如果您期望的日期时间是 2020-10-02T07:07:38
,您需要进一步将给定的日期时间减去 6 小时,即总共相对于 UTC
的偏移量为 -12:00
小时。
以下示例说明了这个概念:
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
英文:
First of all, I recommend you switch from the outdated and error-prone java.util
date-time API to the modern java.time
date-time API. Learn more about the modern date-time API from Trail: Date Time.
Your understanding of Zone-Offset is not correct
The date-time string, 2020-10-02T13:07:38-06:00
tells us that the given date and time has been adjusted with an offset of -06:00 hours from UTC
i.e. the corresponding date-time at UTC
would be 2020-10-02T19:07:38Z
where Z
specifies a Zone-Offset of 00:00
hours.
It means that if you are expecting a date-time of 2020-10-02T07:07:38
, you need to offset the given date-time further by -06:00
hours i.e. it will be at a total of -12:00
hours offset from UTC
.
The following example illustrates this concept:
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);
}
}
Output:
2020-10-02T13:07:38-06:00
2020-10-02T19:07:38Z
2020-10-02T07:07:38-12:00
2020-10-02T07:07:38
Using the legacy 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);
}
}
Output:
2020-10-02T13:07:38-06:00
2020-10-02T07:07:38
答案2
得分: 2
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
(建议用于时间戳),从 Java 中传输一个 OffsetDateTime
到它。对于大多数数据库引擎,timestamp with time zone
实际上意味着 UTC 时间戳,所以为了清晰起见,我更喜欢传递一个也在 UTC 中的 OffsetDateTime
。有几种转换的方法,每种方法都有其优缺点。
1. 通过 GregorianCalendar 进行转换。
// 为了演示,构建与您的等效的 XMLGregorianCalendar
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("2020-10-02T13:07:38-06:00");
// 转换为适用于您的 SQL 数据库的 OffsetDateTime
OffsetDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.toOffsetDateTime()
.withOffsetSameInstant(ZoneOffset.UTC);
// 显示结果
System.out.println(dateTime);
输出为:
> 2020-10-02T19:07:38Z
优点:这是官方的转换方法。
或者 2. 通过字符串进行转换。
OffsetDateTime dateTime = OffsetDateTime.parse(xmlGregorianCalendar.toString())
.withOffsetSameInstant(ZoneOffset.UTC);
优点:这很简洁,我认为不会有任何意外。缺点:对我来说,将其格式化为字符串并再次解析为日期时间对象似乎有些浪费。
3. 通过整数进行转换。您可以通过从 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
(不建议用于时间戳,但经常见到),您需要从 Java 中传递一个 LocalDateTime
。同样,有几种转换的方法。我只展示一种:
LocalDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
我使用了 JVM 的默认时区,与旧式的 Timestamp
类所做的方式保持一致,因此结果取决于时区。在我的时区(Europe/Copenhagen),目前的 UTC 偏移为 +02:00,结果是:
> 2020-10-02T21:07:38
在您的代码中出了什么问题?
您之前使用的三参数 XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
设计上会引入类似您观察到的问题。它承诺使用指定的时区(如果提供了时区)构造一个带有 XMLGregorianCalendar
的字段值(年、月、日、小时、分钟等)的 GregorianCalendar
。因此文档已经非常清楚地指出:如果您指定的时区与 XMLGregorianCalendar
的偏移不一致,它将给您提供不同于 XMLGregorianCalendar
指定的时间点。如果 XMLGregorianCalendar
没有偏移,并且我们知道所需的时区,那么这种方法可能偶尔有用。但是您的 XMLGregorianCalendar
已经有偏移,所以这绝对不是您想要使用的方法。
链接
- Oracle 教程:日期时间 解释了如何使用 java.time。
- 三参数
XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
的文档
英文:
JDBC 4.2 and java.time
Arvind Kumar Avinash has already nicely explained how the offset works (what XMLGregorianCalendar
misleadingly calls a time zone). He has also already recommended java.time, the modern Java date and time API. I should like to elaborate on this recommendation. Assuming that you thought you wanted a java.sql.Timestamp
for your SQL database, you should not want that. Since JDBC 4.2 and Hibernate 5 we can seamlessly transfer the types of java.time to our SQL databases. Which I recommend that you do instead.
If using timestamp with time zone in SQL, use OffsetDateTime in UTC in Java
If the datatype in SQL is timestamp with time zone
(recommended for timestamps), transfer an OffsetDateTime
from Java to it. To most database engines timestamp with time zone
really means timestamp in UTC, so for clarity I prefer to pass an OffsetDateTime
that is in UTC too. There are a couple of ways to convert, each with their pros and cons.
1. Convert via 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);
Output is:
> 2020-10-02T19:07:38Z
Pro: it’s the official conversion.
Or 2. Convert via String.
OffsetDateTime dateTime = OffsetDateTime.parse(xmlGregorianCalendar.toString())
.withOffsetSameInstant(ZoneOffset.UTC);
Pro: it’s short, and I don’t think it gives any surprises. Con: To me it feels like a waste to format into a string and parse back into a date-time object again.
3. Convert via int. You may convert by taking the individual fields out of the XMLGregorianCalendar
and building an OffsetDateTime
from them. It’s getting long-winded, handling fraction of second is a bit complicated, and there’s a risk of accidentally swapping fields, so I don’t recommend it.
How to pass to SQL. Here’s an example of transferring the OffsetDateTime
to SQL.
String sqlString = "insert into your_table(your_timestamp_column) values (?);";
PreparedStatement ps = yourDatabaseConnection.prepareStatement(sqlString);
ps.setObject(1, dateTime);
int rowsInserted = ps.executeUpdate();
If using timestamp without time zone in SQL, instead pass a LocalDateTime from Java
If the datatype in SQL is timestamp
without time zone (not recommended for timestamps, but often seen), you need to pass a LocalDateTime
from Java. Again there are a couple of ways to convert. I am showing just one:
LocalDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
I am using the default time zone of the JVM in agreement with what the old-fashioned Timestamp
class did, so the result is time zone dependent. In my time zone (Europe/Copenhagen) currently at UTC offset +02:00, the result is:
> 2020-10-02T21:07:38
What went wrong in your code?
The three-arg XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
that you were using is designed to introduce bugs like the one you observed. It promises to construct a GregorianCalendar
with the specified time zone (if one is given) and with the field values from the XMLGregorianCalendar
(year, month, day, hour, minute, etc.). So the documentation says it quite clearly: if you specify a time zone that does not agree with the offset of the XMLGregorianCalendar
, it gives you a different point in time than the one specified by the XMLGregorianCalendar
. It might occasionally be useful if the XMLGregorianCalendar
hadn’t got any offset and we knew which time zone was intended. But your XMLGregorianCalendar
has got offset, so this method definitely is not what you want to use.
Links
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论