【问题标题】:XMLGregorianCalendar to GregorianCalendarXMLGregorianCalendar 到 GregorianCalendar
【发布时间】:2017-01-08 13:54:31
【问题描述】:

我只是想使用下面的代码将 XMLGregorianCalendar 的实例(我从 JAXWS 获得)转换为特定时区的 GregorianCalendar

日期即将到来,我想将其转换为 GMT 以便进一步保存到 DB

//soap response <ns4:TimeStamp>2016-06-18T04:43:54-04:00</ns4:TimeStamp>
//dtime is what i got from JAXB for the above date, so I wrote::
Date date = dTime.toGregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US, null).getTime();
System.out.println(date);

输出:Sat Jun 18 14:13:54 IST 2016

由于上面没有按预期工作,所以我尝试了 DateFormat 并给出了预期的结果。

    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    GregorianCalendar gc =  dTime.toGregorianCalendar();
    System.out.println(df.format(gc.getTime()));

输出:2016-06-18 08:43:54 +0000

toGregorianCalendar(...) 没有给出想要的结果,这可能是什么问题?

我还注意到上面从 toGregorianCalendar 获得的 GregorianCalendar 实例的 fieldSet=false。不确定这是否会导致问题。

java.util.GregorianCalendar[time=1468382241000,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset= 0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=6,WEEK_OF_YEAR=29,WEEK_OF_MONTH=3,DAY_OF_MONTH=12 ,DAY_OF_YEAR=194,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=57,SECOND=21,MILLISECOND=0,ZONE_OFFSET=-14400000,DST_OFFSET=0]

任何帮助将不胜感激..!!

【问题讨论】:

  • 如果输入是04:43:54,输出怎么可能是05:27:21?时区更改不会影响秒数。
  • 抱歉,我错误地添加了错误的输出。现已更正。
  • 仅供参考,诸如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 等非常麻烦的日期时间类现在是 legacy,被 Java 8 及更高版本中内置的 java.time 类所取代.见Tutorial by Oracle

标签: java date timezone


【解决方案1】:

tl;博士

myPreparedStatement
.setObject(                     // Exchange java.time objects with your database in JDBC 4.2 and later.
    … ,                         // Specify which `?` placeholder in your SQL statement.
    myXMLGregorianCalendar      // A legacy class. Better to use *java.time* whenever possible.
    .toGregorianCalendar()      // Convert from the one legacy class to another, as a bridge towards the modern `ZonedDateTime` class.
    .toZonedDateTime()          // Convert to the modern class.
    .toInstant()                // Adjust from a time zone to UTC. Same moment, same point on the timeline, different wall-clock time.
    .atOffset(                  // Adjust from basic `Instant` class to the more flexible `OffsetDateTime` class, if your JDBC driver does not offer the optional support for `Instant`. 
        ZoneOffset.UTC          // Specify UTC using this constant.
    )                           // Returns a `OffsetDateTime` object.
)

java.time

您正在使用可怕的旧日期时间类,这些类在几年前被 JSR 310 中定义的现代 java.time 类所取代。

将 XMLGregorianCalendar(我从 JAXWS 获得)的实例转换为 GregorianCalendar

第一次致电XMLGregorianCalendar::toGregorianCalendar()

GregorianCalendar gc = myXMLGregorianCalendar.toGregorianCalendar() ;

从传统类 GregorianCalendar 转换为现代类 java.time.ZonedDateTimeCall the new conversion method 添加到旧类中。

ZonedDateTime zdt = gc.toZonedDateTime() ;

日期是美国东部标准时间,我想把它转换成格林尼治标准时间

java.time.Instant 类代表UTC 中的时刻,始终采用UTC。您可以从ZonedDateTime 中提取Instant。同一时刻,不同的挂钟时间。

Instant instant = zdt.toInstant() ;

为了进一步保存到数据库

如果您的 JDBC 驱动程序支持,您可能可以将Instant 传递给数据库。 Instant 类型在 JDBC 4.2 中是可选的。

myPreparedStatement.setObject( … , instant ) ;

如果不支持,请使用OffsetDateTime,因为 JDBC 4.2 需要支持。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;

检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

美国东部时间即将到来

EST 不是实时时区。

Continent/Region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 2-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

【讨论】:

    【解决方案2】:

    请记住,Java Date 对象没有时区。它们在内部使用 UTC。时区仅在打印(格式化)时显示。

    这个简单的代码有效:

    XMLGregorianCalendar xmlCal = XMLGregorianCalendarImpl.parse("2016-06-18T04:43:54-04:00");
    GregorianCalendar cal = xmlCal.toGregorianCalendar();
    java.util.Date date = cal.getTime();
    
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    format.setTimeZone(TimeZone.getTimeZone("GMT"));
    System.out.println(format.format(date)); // prints: 2016-06-18 08:43:54
    

    由于您的目标是将其保存到数据库中,因此您实际上并不关心将其格式化为文本。假设您使用的是 JDBC(不是一些 NoSQL),您需要一个 Timestamp

    XMLGregorianCalendar xmlCal = XMLGregorianCalendarImpl.parse("2016-06-18T04:43:54-04:00");
    GregorianCalendar cal = xmlCal.toGregorianCalendar();
    java.sql.Timestamp date = new java.sql.Timestamp(cal.getTimeInMillis());
    

    现在您可以使用setTimestamp() 将其提供给PreparedStatement

    【讨论】:

    • 使用这个xmlCal.toGregorianCalendar().getTimeInMillis() 不会解决这个问题,因为这是使用默认的服务器时区。而我想将日期保存到格林威治标准时间。所以这就是我用的。 xmlCal.toGregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US, null).getTime() 但这不起作用。
    • @bbajaj xmlCal.toGregorianCalendar().getTimeInMillis() 使用默认的服务器时区。 GregorianCalendar 将从XMLGregorianCalendar 继承时区,XMLGregorianCalendar 使用输入字符串中的时区 (-04:00)。 Timestamp 值没有时区。它的内部值始终为 UTC。调用date.toString()呈现默认时区的日期,但那是仅用于呈现date 是自 Epoch UTC(又名 1/1/1970)以来的毫秒数。
    【解决方案3】:

    我正在使用以下代码将来自具有给定时区(例如 GMT+0)的给定源的 XMLGregorianCalendar 转换为具有当前系统时区的 GregorianCalendar

    GregorianCalendar.from(xmlGregorianCalendar
        .toGregorianCalendar()
        .toZonedDateTime()
        .withZoneSameInstant(ZoneId.systemDefault()))
    

    仅使用XMLGregorianCalendar.toGregorianCalendar() 时,返回的GregorianCalendar 与源系统的时区相同,而不是当前系统的时区。 在收到数据时转换时区更安全:这样可以减少代码中潜在问题的数量,因为所有日期都基于系统时区。

    例如:

    XMLGregorianCalendar xmlGregorianCalendarFromSource = DatatypeFactory.newInstance()
        .newXMLGregorianCalendar("1983-09-30T23:00:00.000Z"); // the source system is on GMT+0
    GregorianCalendar gregorianCalendarWithSourceTZ = xmlGregorianCalendarFromSource.toGregorianCalendar();
    GregorianCalendar gregorianCalendarWithSystemTZ = GregorianCalendar.from(xmlGregorianCalendarFromSource 
        .toGregorianCalendar()
        .toZonedDateTime()
        .withZoneSameInstant(ZoneId.systemDefault()));
    System.out.println(xmlGregorianCalendarFromSource); // displays "1983-09-30T23:00:00.000Z" (i.e. GMT+0)
    System.out.println(gregorianCalendarWithSourceTZ.toZonedDateTime()); // displays "1983-09-30T23:00Z[GMT]" (i.e. GMT+0)
    System.out.println(gregorianCalendarWithSystemTZ.toZonedDateTime()); // displays "1983-10-01T00:00+01:00[Europe/Berlin]" (my system timezone is GMT+1 or GMT+2 depending on daylight saving)
    

    【讨论】:

      【解决方案4】:

      作为另一条路线,您可以考虑在代码中使用日期而不是 XMLGregorianCalendars,方法是使用 this 堆栈溢出问题中讨论的 jaxb 绑定。

      【讨论】:

        猜你喜欢
        • 2011-12-31
        • 2014-05-22
        • 2019-08-24
        • 2010-10-24
        • 2012-09-24
        • 2014-02-21
        • 2019-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多