java.util.Date to XMLGregorianCalendar


600

Czy nie ma wygodnej metody przejścia z pliku java.util.Date do XMLGregorianCalendar?


FYI: Obie te straszne klasy zostały wyparte lata temu przez klasy java.time zdefiniowane w JSR 310. Zobacz ZonedDateTimeklasę i nowe metody konwersji dodane do klas starszych. Szczegóły w tej odpowiedzi Ole VV
Basil Bourque

Odpowiedzi:


36

Chciałbym cofnąć się i spojrzeć nowocześnie na to 10-letnie pytanie. Wymienione klasy Datei XMLGregorianCalendarsą już stare. Rzucam wyzwanie ich wykorzystaniu i oferuję alternatywy.

  • Datebył zawsze źle zaprojektowany i ma ponad 20 lat. To proste: nie używaj go.
  • XMLGregorianCalendarteż jest stary i ma staromodny design. Jak rozumiem, był używany do tworzenia dat i godzin w formacie XML dla dokumentów XML. Jak 2009-05-07T19:05:45.678+02:00lub 2009-05-07T17:05:45.678Z. Formaty te są wystarczająco zgodne z ISO 8601, że klasy java.time, nowoczesny interfejs API daty i godziny Java, mogą je wytwarzać, co preferujemy.

Nie jest wymagana konwersja

Dla wielu (większości?) Celów współczesnym zamiennikiem Datebędzie Instant. Jest Instantmomentem w czasie (tak jak Datejest).

    Instant yourInstant = // ...
    System.out.println(yourInstant);

Przykładowe dane wyjściowe z tego fragmentu:

2009-05-07T17: 05: 45.678Z

Jest taki sam jak drugi z moich XMLGregorianCalendarciągów przykładów powyżej. Jak większość z was wie, pochodzi od Instant.toStringniejawnego wezwania System.out.println. Z java.time, w wielu przypadkach nie musimy konwersje, że w dawnych czasach mamy dokonane pomiędzy Date, Calendar, XMLGregorianCalendari innych klas (w niektórych przypadkach możemy zrobić konwersje potrzebę, choć ja pokazując parę w następnej części) .

Kontrolowanie przesunięcia

Ani wejście Dateani Instantnie ma strefy czasowej ani przesunięcia UTC. Poprzednio zaakceptowana i wciąż najwyżej głosowana odpowiedź Bena Nolanda używa bieżącej domyślnej strefy czasowej JVM do wyboru przesunięcia XMLGregorianCalendar. Aby uwzględnić przesunięcie w nowoczesnym obiekcie, używamy OffsetDateTime. Na przykład:

    ZoneId zone = ZoneId.of("America/Asuncion");
    OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
    System.out.println(dateTime);

2009-05-07T13: 05: 45.678-04: 00

Ponownie jest to zgodne z formatem XML. Jeśli chcesz ponownie użyć bieżącego ustawienia strefy czasowej JVM, ustaw zonena ZoneId.systemDefault().

Co jeśli absolutnie potrzebuję XMLGregorianCalendar?

Istnieje więcej sposobów, aby konwertować Instantdo XMLGregorianCalendar. Przedstawię parę, z których każda ma swoje zalety i wady. Po pierwsze, podobnie jak XMLGregorianCalendarciąg, taki jak łańcuch 2009-05-07T17:05:45.678Z, można go również zbudować z takiego łańcucha:

    String dateTimeString = yourInstant.toString();
    XMLGregorianCalendar date2
            = DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
    System.out.println(date2);

2009-05-07T17: 05: 45.678Z

Pro: jest krótki i nie sądzę, że daje jakieś niespodzianki. Przeciw: Dla mnie to marnotrawstwo formatowania chwili w ciąg i parsowania go z powrotem.

    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: To oficjalna konwersja. Kontrolowanie przesunięcia przychodzi naturalnie. Przeciw: Przechodzi przez kolejne kroki i dlatego jest dłuższy.

Co jeśli mamy randkę?

Jeśli masz staromodny Dateobiekt ze starszego interfejsu API, którego nie możesz sobie teraz zmienić, przekonwertuj go na Instant:

    Instant i = yourDate.toInstant();
    System.out.println(i);

Dane wyjściowe są takie same jak poprzednio:

2009-05-07T17: 05: 45.678Z

Jeśli chcesz kontrolować przesunięcie, przekonwertuj dalej na OffsetDateTimetaki sam sposób jak powyżej.

Jeśli masz staroświecki Datei absolutnie potrzebujesz staromodnego XMLGregorianCalendar, skorzystaj z odpowiedzi Bena Nolanda.

Spinki do mankietów


1034
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);

5
Czy warto zachować, aby zachować metodę getInstance () jako zmienną statyczną w niektórych klasach konwerterów? Próbowałem to sprawdzić w JavaDoc, ale nic nie znalazłem. Trochę trudno wiedzieć, czy pojawią się problemy z jednoczesnym użyciem?
Martin

36
Jeśli chcesz korzystać z JodaTime, możesz to zrobić w jednym wierszu: DatatypeFactory.newInstance (). NewXMLGregorianCalendar (new DateTime (). ToGregorianCalendar ())
Nicolas Mommaerts

3
XMLGregorianCalendar date2 = DatatypeFactory.newInstance (). NewXMLGregorianCalendar (nowy GregorianCalendar (RRRR, MM, DD));
Junchen Liu,

3
Należy pamiętać, że Kalendarz nie jest bezpieczny dla wątków, dlatego też GregorianCalender nie jest. Zobacz także stackoverflow.com/questions/12131324/…
questionaire

Wersja jednoliniowa - DatatypeFactory.newInstance (). NewXMLGregorianCalendar (new GregorianCalendar () {{setTime (yourDate);}})
Alex Vayda

205

Dla tych, którzy mogą tu skończyć, szukając odwrotnej konwersji (z XMLGregorianCalendarna Date):

XMLGregorianCalendar xcal = <assume this is initialized>;
java.util.Date dt = xcal.toGregorianCalendar().getTime();

31

Oto metoda konwersji z GregorianCalendar na XMLGregorianCalendar; Opuszczę część konwersji z java.util.Date na GregorianCalendar jako ćwiczenie dla ciebie:

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

}

EDYCJA: Slooow :-)


6
To rozwiązanie przekonwertować kalendarz gregoriański do XMLGregorianCalendar a nie to, co jest wskazane w pytaniu
ftrujillo


12

Pomyślałem, że dodam moje rozwiązanie poniżej, ponieważ powyższe odpowiedzi nie spełniają moich dokładnych potrzeb. Mój schemat Xml wymagał osobnych elementów daty i godziny, a nie pojedynczego pola daty i godziny. Standardowy konstruktor XMLGregorianCalendar użyty powyżej wygeneruje pole DateTime

Zauważ, że jest kilka gotyckich, na przykład konieczność dodania jednego do miesiąca (ponieważ Java liczy miesiące od 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);

10

Mam nadzieję, że moje kodowanie tutaj jest poprawne; D Aby przyspieszyć, po prostu użyj brzydkiego wywołania getInstance () GregorianCalendar zamiast wywołania konstruktora:

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

}

14
-1 do użycia .getInstance (). GregorianCalendar.getInstance()jest odpowiednikiem Calendar.getInstance(). Nie Calendar.getInstance()może przyspieszyć, ponieważ używa tego samego new GregorianCalendar(), ale zanim sprawdzi domyślne ustawienia regionalne i może zamiast tego utworzyć kalendarz japoński lub buddyjski, więc dla niektórych szczęśliwych użytkowników będzie ClassCastException!
kan

6

Zakładając, że dekodujesz lub kodujesz xml i używasz JAXB, możliwe jest całkowite zastąpienie powiązania dateTime i użycie czegoś innego niż `XMLGregorianCalendar 'dla każdej daty w schemacie.

W ten sposób możesz mieć JAXB wykonywać powtarzalne czynności, a jednocześnie poświęcić czas na pisanie niesamowitego kodu, który zapewnia wartość.

Przykład dla jodatime DateTime: (Robiąc to z java.util.Date również by działało - ale z pewnymi ograniczeniami. Wolę jodatime i jest on kopiowany z mojego kodu, więc wiem, że działa ...)

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

I konwerter:

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

Zobacz tutaj: jak zastąpić XmlGregorianCalendar według daty?

Jeśli z przyjemnością po prostu mapujesz na chwilę na podstawie strefy czasowej + znacznika czasu, a oryginalna strefa czasowa nie jest tak naprawdę istotna, java.util.Dateprawdopodobnie również jest w porządku.


0

Sprawdź ten kod: -

/* 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);

Pełny przykład możesz zobaczyć tutaj


Nie sądzę, aby ta odpowiedź nie zawierała niczego nowego. Być może możesz edytować poprzednią odpowiedź zamiast publikować inną prawie tak samo.
zima

1
Jak zrobić dateFormat dla XMLGregorianCalendar?
Panadol Chong
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.