web-dev-qa-db-fra.com

java.util.Date to XMLGregorianCalendar

N'y a-t-il pas un moyen pratique de passer d'un Java.util.Date à un XMLGregorianCalendar?

573
mac

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.

Aucune conversion nécessaire

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).

Contrôler le décalage

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().

Et si j'ai absolument besoin d'un XMLGregorianCalendar?

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.

Et si on avait une date?

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.

Liens

10
Ole V.V.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
1021
Ben Noland

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();
202
Nuno Furtado

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 :-)

31
sasuke

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 .

22
Chris Knight

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);
12
khylo

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);
   }

}
10
Daniel K.

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.

4
KarlP

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

0
Akash