Answers:
我想退后一步,以现代的眼光看待这个已有10年历史的问题。Date
和和提到的类XMLGregorianCalendar
现在已经很旧了。我挑战使用它们并提供替代方法。
Date
总是设计不当,已有20多年的历史了。这很简单:不要使用它。XMLGregorianCalendar
也很老旧,并具有过时的设计。据我了解,它用于以XML格式为XML文档生成日期和时间。喜欢2009-05-07T19:05:45.678+02:00
或2009-05-07T17:05:45.678Z
。这些格式与ISO 8601足够吻合,因此java.time(现代的Java日期和时间API)类可以产生它们,我们更喜欢。对于许多(大多数?)目的,现代替代Date
将会是Instant
。An Instant
是一个时间点(按原样Date
)。
Instant yourInstant = // ...
System.out.println(yourInstant);
此代码段的示例输出:
2009-05-07T17:05:45.678Z
与XMLGregorianCalendar
上面的示例字符串中的后者相同。如您所知,它来自Instant.toString
被隐式调用System.out.println
。随着java.time,在许多情况下,我们并不需要,在过去的日子,我们之间进行的转换Date
,Calendar
,XMLGregorianCalendar
和其他类(在某些情况下,我们确实需要转换,但是,我向您展示一对情侣在下一节) 。
a Date
和in 都没Instant
有时区或UTC偏移量。Ben Noland先前接受并投票仍然最高的答案是使用JVM当前的默认时区来选择的偏移量XMLGregorianCalendar
。为了在现代对象中包含偏移量,我们使用OffsetDateTime
。例如:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13:05:45.678-04:00
同样,这符合XML格式。如果要再次使用当前的JVM时区设置,请设置zone
为ZoneId.systemDefault()
。
有更多的方法可以转换Instant
为XMLGregorianCalendar
。我将介绍几个优点和缺点。首先,就像XMLGregorianCalendar
产生类似的字符串一样2009-05-07T17:05:45.678Z
,它也可以从这样的字符串构建:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17:05:45.678Z
优点:很短,我认为这不会给您带来任何惊喜。缺点:对我来说,将即时信息格式化为字符串并将其解析回是一种浪费。
ZonedDateTime dateTime = yourInstant.atZone(zone);
GregorianCalendar c = GregorianCalendar.from(dateTime);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
System.out.println(date2);
2009-05-07T13:05:45.678-04:00
优点:这是官方转换。控制偏移量自然而然。缺点:它要经过更多步骤,因此会更长。
如果您是Date
从旧版API中获取的老式对象,而现在又无法更改,则将其转换为Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
输出与以前相同:
2009-05-07T17:05:45.678Z
如果要控制偏移量,请OffsetDateTime
按照上述相同的方式进一步转换为。
如果您有老式的产品,Date
并且绝对需要老式的产品XMLGregorianCalendar
,请使用Ben Noland的答案。
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
这是从GregorianCalendar转换为XMLGregorianCalendar的方法;我将保留从java.util.Date转换为GregorianCalendar的部分作为练习:
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateTest {
public static void main(final String[] args) throws Exception {
GregorianCalendar gcal = new GregorianCalendar();
XMLGregorianCalendar xgcal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gcal);
System.out.println(xgcal);
}
}
编辑:Slooow :-)
使用Joda-Time库的一行示例:
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
感谢尼古拉酒店距离Mommaerts从他的评论接受的答案。
只是以为我会在下面添加我的解决方案,因为上面的答案不能满足我的确切需求。我的Xml模式需要单独的Date和Time元素,而不是单一的DateTime字段。上面使用的标准XMLGregorianCalendar构造函数将生成DateTime字段
请注意,这里有几个gothca,例如必须在月份中加一个(因为java从0开始算月份)。
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(yourDate);
XMLGregorianCalendar xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH), 0);
XMLGregorianCalendar xmlTime = DatatypeFactory.newInstance().newXMLGregorianCalendarTime(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0);
我希望这里的编码是正确的; D为了使其更快,请使用GregorianCalendar的丑陋的getInstance()调用而不是构造函数调用:
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateTest {
public static void main(final String[] args) throws Exception {
// do not forget the type cast :/
GregorianCalendar gcal = (GregorianCalendar) GregorianCalendar.getInstance();
XMLGregorianCalendar xgcal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gcal);
System.out.println(xgcal);
}
}
GregorianCalendar.getInstance()
等于Calendar.getInstance()
。的Calendar.getInstance()
不能使其更快,是因为它使用了new GregorianCalendar()
,但是在它还检查默认语言环境并可以创建日文日历或Buddish日历之前,对于一些幸运的用户来说,它将是ClassCastException
!
假设您要对xml进行解码或编码,并使用JAXB
,则可以完全替换dateTime绑定,并对模式中的每个日期使用XMLXMLregorianCalendar之外的其他方式。
这样,您就可以JAXB
做重复的事情,同时可以花时间编写可带来价值的出色代码。
jodatime的示例DateTime
:(使用java.util.Date进行此操作也可以-但有一定的限制。我更喜欢jodatime,它是从我的代码中复制的,所以我知道它可以工作...)
<jxb:globalBindings>
<jxb:javaType name="org.joda.time.LocalDateTime" xmlType="xs:dateTime"
parseMethod="test.util.JaxbConverter.parseDateTime"
printMethod="se.seb.bis.test.util.JaxbConverter.printDateTime" />
<jxb:javaType name="org.joda.time.LocalDate" xmlType="xs:date"
parseMethod="test.util.JaxbConverter.parseDate"
printMethod="test.util.JaxbConverter.printDate" />
<jxb:javaType name="org.joda.time.LocalTime" xmlType="xs:time"
parseMethod="test.util.JaxbConverter.parseTime"
printMethod="test.util.JaxbConverter.printTime" />
<jxb:serializable uid="2" />
</jxb:globalBindings>
和转换器:
public class JaxbConverter {
static final DateTimeFormatter dtf = ISODateTimeFormat.dateTimeNoMillis();
static final DateTimeFormatter df = ISODateTimeFormat.date();
static final DateTimeFormatter tf = ISODateTimeFormat.time();
public static LocalDateTime parseDateTime(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
LocalDateTime r = dtf.parseLocalDateTime(s);
return r;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printDateTime(LocalDateTime d) {
try {
if (d == null)
return null;
return dtf.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static LocalDate parseDate(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
return df.parseLocalDate(s);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printDate(LocalDate d) {
try {
if (d == null)
return null;
return df.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printTime(LocalTime d) {
try {
if (d == null)
return null;
return tf.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static LocalTime parseTime(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
return df.parseLocalTime(s);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
请参阅此处: 如何用日期替换XmlGregorianCalendar?
如果您很乐意仅基于时区+时间戳映射到某个时刻,而原始时区并不真正相关,那么java.util.Date
也可能很好。
查看此代码:-
/* Create Date Object */
Date date = new Date();
XMLGregorianCalendar xmlDate = null;
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(date);
try{
xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println("XMLGregorianCalendar :- " + xmlDate);
您可以在此处看到完整的示例
ZonedDateTime
类,并将新的转换方法添加到旧类中。Ole VV 在此答案中的详细信息