N'y a-t-il pas un moyen pratique de passer d'un Java.util.Date à un XMLGregorianCalendar?
Je voudrais prendre du recul et jeter un regard moderne sur cette question vieille de 10 ans. Les classes mentionnées, Date
et XMLGregorianCalendar
, sont anciennes maintenant. Je conteste leur utilisation et propose des alternatives.
Date
a toujours été mal conçu et a plus de 20 ans. C’est simple: ne l’utilisez pas.XMLGregorianCalendar
est vieux aussi et a un design démodé. Si je comprends bien, il a été utilisé pour produire des dates et des heures au format XML pour les documents XML. Comme 2009-05-07T19:05:45.678+02:00
ou 2009-05-07T17:05:45.678Z
. Ces formats s’accordent assez bien avec ISO 8601 pour que les classes de Java.time, l’API de date et d’heure moderne Java, puissent les produire, ce que nous préférons.Pour beaucoup (la plupart?) Des objectifs, le remplacement moderne d’un Date
sera un Instant
. Un Instant
est un point dans le temps (exactement comme un Date
).
Instant yourInstant = // ...
System.out.println(yourInstant);
Un exemple de sortie de cet extrait:
2009-05-07T17: 05: 45.678Z
C’est la même chose que la dernière de mon exemple XMLGregorianCalendar
chaînes ci-dessus. Comme la plupart d'entre vous le savent, cela provient du fait que Instant.toString
est appelé implicitement par System.out.println
. Avec Java.time, dans de nombreux cas, nous n’avons pas besoin des conversions que jadis nous faisions entre Date
, Calendar
, XMLGregorianCalendar
et d’autres classes (dans certains cas, nous avons besoin de conversions, cependant, je vous montre un couple dans la section suivante).
Ni un Date
ni dans Instant
n'a un fuseau horaire ni un décalage UTC. La réponse précédemment acceptée et toujours la plus votée par Ben Noland utilise le fuseau horaire par défaut actuel de la machine virtuelle pour sélectionner le décalage de XMLGregorianCalendar
. Pour inclure un décalage dans un objet moderne, nous utilisons un OffsetDateTime
. Par exemple:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13: 05: 45.678-04: 00
Là encore, cela est conforme au format XML. Si vous souhaitez utiliser à nouveau le paramètre de fuseau horaire actuel de la machine virtuelle Java, définissez zone
sur ZoneId.systemDefault()
.
Il existe d'autres moyens de convertir Instant
en XMLGregorianCalendar
. Je présenterai un couple, chacun avec ses avantages et ses inconvénients. Tout d’abord, tout comme un XMLGregorianCalendar
produit une chaîne telle que 2009-05-07T17:05:45.678Z
, elle peut également être construite à partir d’une telle chaîne:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17: 05: 45.678Z
Pro: c’est court et je ne pense pas que cela donne des surprises. Con: Pour moi, cela ressemble à un gaspillage de formater l'instant en une chaîne et de l'analyse.
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: C’est la conversion officielle. Contrôler le décalage vient naturellement. Con: Il faut plusieurs étapes et est donc plus long.
Si vous avez obtenu un objet Date
à l'ancienne d'une API héritée que vous ne pouvez pas vous permettre de modifier pour l'instant, convertissez-le en Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
La sortie est la même que précédemment:
2009-05-07T17: 05: 45.678Z
Si vous souhaitez contrôler le décalage, convertissez davantage en un OffsetDateTime
de la même manière que ci-dessus.
Si vous avez un Date
à l'ancienne et que vous avez absolument besoin d'un XMLGregorianCalendar
à l'ancienne, utilisez simplement la réponse de Ben Noland.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
Pour ceux qui pourraient se retrouver ici à la recherche de la conversion opposée (de XMLGregorianCalendar
à Date
):
XMLGregorianCalendar xcal = <assume this is initialized>;
Java.util.Date dt = xcal.toGregorianCalendar().getTime();
Voici une méthode pour convertir un GregorianCalendar en XMLGregorianCalendar; Je vais laisser la partie de la conversion d'un fichier Java.util.Date à GregorianCalendar comme exercice pour vous:
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 :-)
Un exemple d'une ligne utilisant Joda-Time bibliothèque:
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
Crédit à Nicolas Mommaerts de son commentaire dans la réponse acceptée .
Je pensais juste que j'ajouterais ma solution ci-dessous, car les réponses ci-dessus ne répondaient pas exactement à mes besoins. Mon schéma Xml requiert des éléments Date et Time distincts, et non un seul champ DateTime. Le constructeur XMLGregorianCalendar standard utilisé ci-dessus générera un champ DateTime
Remarquez quelques gothiques, comme par exemple en ajouter un au mois (puisque Java compte les mois à partir de 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);
J'espère que mon encodage ici est correct; D Pour l'accélérer, utilisez simplement le vilain appel getInstance () de GregorianCalendar au lieu de l'appel du constructeur:
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);
}
}
En supposant que vous décodiez ou encodiez XML et que vous utilisiez JAXB
, il est alors possible de remplacer entièrement la liaison dateTime et d'utiliser autre chose que XMLGregorianCalendar pour chaque date du schéma.
De cette façon, vous pouvez laisser JAXB
faire les choses répétitives tout en passant du temps à écrire un code génial qui offre de la valeur.
Exemple pour un jodatime DateTime
: (Faire ceci avec Java.util.Date fonctionnerait aussi - mais avec certaines limitations. Je préfère le jodatime et c'est copié de mon code, donc je sais que ça marche ...)
<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>
Et le convertisseur:
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);
}
}
Voir ici: comment remplacer XmlGregorianCalendar par Date?
Si vous souhaitez simplement mapper sur un instant basé sur le fuseau horaire + horodatage, et que le fuseau horaire d'origine n'est pas vraiment pertinent, alors Java.util.Date
convient probablement également.
Découvrez ce 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);
Vous pouvez voir un exemple complet ici