Czy nie ma wygodnej metody przejścia z pliku java.util.Date do XMLGregorianCalendar?
Czy nie ma wygodnej metody przejścia z pliku java.util.Date do XMLGregorianCalendar?
Odpowiedzi:
Chciałbym cofnąć się i spojrzeć nowocześnie na to 10-letnie pytanie. Wymienione klasy Date
i XMLGregorianCalendar
są już stare. Rzucam wyzwanie ich wykorzystaniu i oferuję alternatywy.
Date
był zawsze źle zaprojektowany i ma ponad 20 lat. To proste: nie używaj go.XMLGregorianCalendar
też 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:00
lub 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.Dla wielu (większości?) Celów współczesnym zamiennikiem Date
będzie Instant
. Jest Instant
momentem w czasie (tak jak Date
jest).
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 XMLGregorianCalendar
ciągów przykładów powyżej. Jak większość z was wie, pochodzi od Instant.toString
niejawnego wezwania System.out.println
. Z java.time, w wielu przypadkach nie musimy konwersje, że w dawnych czasach mamy dokonane pomiędzy Date
, Calendar
, XMLGregorianCalendar
i innych klas (w niektórych przypadkach możemy zrobić konwersje potrzebę, choć ja pokazując parę w następnej części) .
Ani wejście Date
ani Instant
nie 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 zone
na ZoneId.systemDefault()
.
Istnieje więcej sposobów, aby konwertować Instant
do XMLGregorianCalendar
. Przedstawię parę, z których każda ma swoje zalety i wady. Po pierwsze, podobnie jak XMLGregorianCalendar
cią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.
Jeśli masz staromodny Date
obiekt 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 OffsetDateTime
taki sam sposób jak powyżej.
Jeśli masz staroświecki Date
i absolutnie potrzebujesz staromodnego XMLGregorianCalendar
, skorzystaj z odpowiedzi Bena Nolanda.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
Dla tych, którzy mogą tu skończyć, szukając odwrotnej konwersji (z XMLGregorianCalendar
na Date
):
XMLGregorianCalendar xcal = <assume this is initialized>;
java.util.Date dt = xcal.toGregorianCalendar().getTime();
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 :-)
Przykład w jednym wierszu z wykorzystaniem biblioteki Joda-Time :
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
Podziękowania dla Nicolasa Mommaertsa z jego komentarza w zaakceptowanej odpowiedzi .
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);
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);
}
}
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
!
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.Date
prawdopodobnie również jest w porządku.
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
ZonedDateTime
klasę i nowe metody konwersji dodane do klas starszych. Szczegóły w tej odpowiedzi Ole VV