Antworten:
Ich möchte einen Schritt zurücktreten und einen modernen Blick auf diese 10 Jahre alte Frage werfen. Die erwähnten Klassen Date
und XMLGregorianCalendar
sind jetzt alt. Ich fordere deren Verwendung heraus und biete Alternativen an.
Date
war immer schlecht gestaltet und ist mehr als 20 Jahre alt. Das ist einfach: benutze es nicht.XMLGregorianCalendar
ist auch alt und hat ein altmodisches Design. Soweit ich weiß, wurde es zum Erstellen von Datums- und Uhrzeitangaben im XML-Format für XML-Dokumente verwendet. Wie 2009-05-07T19:05:45.678+02:00
oder 2009-05-07T17:05:45.678Z
. Diese Formate stimmen gut genug mit ISO 8601 überein, dass die Klassen von java.time, der modernen Java-API für Datum und Uhrzeit, sie erzeugen können, was wir bevorzugen.Für viele (die meisten?) Zwecke wird der moderne Ersatz für a Date
ein Instant
. An Instant
ist ein Zeitpunkt (so wie a Date
ist).
Instant yourInstant = // ...
System.out.println(yourInstant);
Eine Beispielausgabe dieses Snippets:
2009-05-07T17: 05: 45.678Z
Es ist dasselbe wie das letztere meiner XMLGregorianCalendar
obigen Beispielzeichenfolgen . Wie die meisten von Ihnen wissen, kommt es davon, Instant.toString
dass sie implizit von angerufen werden System.out.println
. Mit java.time in vielen Fällen brauchen wir nicht die Umsetzungen , die in den alten Tagen , die wir gemacht zwischen Date
, Calendar
, XMLGregorianCalendar
und anderen Klassen (in einigen Fällen wir müssen Conversions tun, obwohl, zeige ich Ihnen ein paar im nächsten Abschnitt) .
Weder a Date
noch in Instant
haben eine Zeitzone oder einen UTC-Offset. Die zuvor akzeptierte und immer noch am höchsten bewertete Antwort von Ben Noland verwendet die aktuelle Standardzeitzone der JVM zur Auswahl des Versatzes der XMLGregorianCalendar
. Um einen Versatz in ein modernes Objekt aufzunehmen, verwenden wir ein OffsetDateTime
. Zum Beispiel:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13: 05: 45.678-04: 00
Auch dies entspricht dem XML-Format. Wenn Sie die aktuelle JVM-Zeitzoneneinstellung erneut verwenden möchten, setzen Sie zone
auf ZoneId.systemDefault()
.
Es gibt mehr Möglichkeiten , konvertieren Instant
zu XMLGregorianCalendar
. Ich werde ein Paar vorstellen, jedes mit seinen Vor- und Nachteilen. Erstens kann XMLGregorianCalendar
ein String, wie er erzeugt 2009-05-07T17:05:45.678Z
, auch aus einem solchen String erstellt werden:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17: 05: 45.678Z
Pro: Es ist kurz und ich glaube nicht, dass es irgendwelche Überraschungen gibt. Con: Für mich fühlt es sich wie eine Verschwendung an, den Augenblick in einen String zu formatieren und ihn zurück zu analysieren.
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
Pro: Es ist die offizielle Konvertierung. Die Steuerung des Offsets ist selbstverständlich. Con: Es geht durch mehr Schritte und ist daher länger.
Wenn Sie ein altmodisches Date
Objekt von einer Legacy-API erhalten haben, dessen Änderung Sie sich gerade nicht leisten können, konvertieren Sie es in Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
Die Ausgabe ist dieselbe wie zuvor:
2009-05-07T17: 05: 45.678Z
Wenn Sie den Versatz steuern möchten, konvertieren Sie auf OffsetDateTime
die gleiche Weise wie oben weiter in a.
Wenn Sie eine altmodische haben Date
und unbedingt eine altmodische brauchen XMLGregorianCalendar
, verwenden Sie einfach die Antwort von Ben Noland.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
Für diejenigen, die hier landen könnten und nach der entgegengesetzten Konvertierung suchen (von XMLGregorianCalendar
nach Date
):
XMLGregorianCalendar xcal = <assume this is initialized>;
java.util.Date dt = xcal.toGregorianCalendar().getTime();
Hier ist eine Methode zum Konvertieren von einem GregorianCalendar in einen XMLGregorianCalendar. Ich überlasse den Teil der Konvertierung von java.util.Date in GregorianCalendar als Übung für Sie:
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);
}
}
EDIT: Slooow :-)
Ein einzeiliges Beispiel mit der Joda-Time- Bibliothek:
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
Dank an Nicolas Mommaerts aus seinem Kommentar in der akzeptierten Antwort .
Ich dachte nur, ich würde meine Lösung unten hinzufügen, da die obigen Antworten nicht genau meinen Anforderungen entsprachen. Für mein XML-Schema waren separate Datums- und Uhrzeitelemente erforderlich, kein einzelnes DateTime-Feld. Der oben verwendete Standardkonstruktor XMLGregorianCalendar generiert ein DateTime-Feld
Beachten Sie, dass es einige Gothcas gibt, z. B. das Hinzufügen eines Monats zum Monat (da Java Monate von 0 zählt).
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);
Ich hoffe, meine Codierung hier ist richtig; D Um es schneller zu machen, verwenden Sie einfach den hässlichen getInstance () -Aufruf von GregorianCalendar anstelle des Konstruktoraufrufs:
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()
ist das Äquivalent von Calendar.getInstance()
. Das Calendar.getInstance()
kann es nicht schneller machen, weil es dasselbe verwendet new GregorianCalendar()
, aber bevor es auch das Standardgebietsschema überprüft und stattdessen einen japanischen oder Buddish-Kalender erstellen könnte, wird es für einige glückliche Benutzer so sein ClassCastException
!
Angenommen, Sie dekodieren oder codieren XML und verwenden XML JAXB
, dann ist es möglich, die dateTime-Bindung vollständig zu ersetzen und für jedes Datum im Schema etwas anderes als "XMLGregorianCalendar" zu verwenden.
Auf diese Weise können Sie haben JAXB
Sie die sich wiederholenden Dinge erledigen, während Sie die Zeit damit verbringen können, fantastischen Code zu schreiben, der Wert liefert.
Beispiel für eine Jodatime DateTime
: (Dies mit java.util.Date zu tun würde auch funktionieren - aber mit bestimmten Einschränkungen. Ich bevorzuge Jodatime und es wird aus meinem Code kopiert, damit ich weiß, dass es funktioniert ...)
<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>
Und der Konverter:
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);
}
}
Siehe hier: Wie kann XmlGregorianCalendar durch Datum ersetzt werden?
Wenn Sie gerne nur einen Moment basierend auf der Zeitzone + dem Zeitstempel zuordnen und die ursprüngliche Zeitzone nicht wirklich relevant ist, java.util.Date
ist dies wahrscheinlich auch in Ordnung.
Überprüfen Sie diesen Code: -
/* 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);
Das vollständige Beispiel finden Sie hier
ZonedDateTime
Klasse und neue Konvertierungsmethoden, die den Legacy-Klassen hinzugefügt wurden. Details in dieser Antwort von Ole VV