Respuestas:
Me gustaría dar un paso atrás y una mirada moderna a esta pregunta de 10 años. Las clases mencionadas, Date
y XMLGregorianCalendar
, son viejas ahora. Desafío el uso de ellos y ofrezco alternativas.
Date
siempre fue mal diseñado y tiene más de 20 años. Esto es simple: no lo use.XMLGregorianCalendar
es viejo también y tiene un diseño anticuado. Según tengo entendido, se utilizó para producir fechas y horas en formato XML para documentos XML. Me gusta 2009-05-07T19:05:45.678+02:00
o 2009-05-07T17:05:45.678Z
. Estos formatos concuerdan bastante bien con ISO 8601 que las clases de java.time, la API moderna de fecha y hora de Java, pueden producirlos, lo que preferimos.Para muchos (¿la mayoría?), El reemplazo moderno de un Date
será un Instant
. Un Instant
es un punto en el tiempo (tal como Date
es).
Instant yourInstant = // ...
System.out.println(yourInstant);
Un ejemplo de salida de este fragmento:
2009-05-07T17: 05: 45.678Z
Es lo mismo que la última de mis XMLGregorianCalendar
cadenas de ejemplo anteriores. Como la mayoría de ustedes saben, proviene de Instant.toString
ser llamado implícitamente por System.out.println
. Con java.time, en muchos casos no necesitamos las conversiones que en los viejos tiempos que hicimos entre Date
, Calendar
, XMLGregorianCalendar
y otras clases (en algunos casos que hacemos conversiones necesidad, sin embargo, estoy mostrando un par en la siguiente sección) .
Ni a Date
ni in Instant
tiene una zona horaria ni un desplazamiento UTC. La respuesta previamente aceptada y aún más votada por Ben Noland utiliza la zona horaria predeterminada actual de JVM para seleccionar el desplazamiento de la XMLGregorianCalendar
. Para incluir un desplazamiento en un objeto moderno, usamos un OffsetDateTime
. Por ejemplo:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13: 05: 45.678-04: 00
Nuevamente, esto se ajusta al formato XML. Si desea volver a utilizar la configuración de zona horaria JVM actual, configure zone
en ZoneId.systemDefault()
.
Hay más maneras de convertir Instant
a XMLGregorianCalendar
. Presentaré una pareja, cada una con sus ventajas y desventajas. Primero, así como un XMLGregorianCalendar
produce una cadena como 2009-05-07T17:05:45.678Z
, también se puede construir a partir de dicha cadena:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17: 05: 45.678Z
Pro: es breve y no creo que dé sorpresas. Con: Para mí, se siente como un desperdicio formatear el instante en una cadena y analizarlo nuevamente.
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 la conversión oficial. Controlar el desplazamiento es algo natural. Con: pasa por más pasos y, por lo tanto, es más largo.
Si obtuvo un Date
objeto antiguo de una API heredada que no puede permitirse cambiar en este momento, conviértalo a Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
La salida es la misma que antes:
2009-05-07T17: 05: 45.678Z
Si desea controlar el desplazamiento, convierta más a OffsetDateTime
a de la misma manera que arriba.
Si tienes un anticuado Date
y necesitas un anticuado XMLGregorianCalendar
, usa la respuesta de Ben Noland.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
Para aquellos que podrían terminar aquí buscando la conversión opuesta (de XMLGregorianCalendar
a Date
):
XMLGregorianCalendar xcal = <assume this is initialized>;
java.util.Date dt = xcal.toGregorianCalendar().getTime();
Aquí hay un método para convertir de un GregorianCalendar a XMLGregorianCalendar; Dejaré la parte de convertir de java.util.Date a GregorianCalendar como ejercicio para ti:
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);
}
}
EDITAR: Slooow :-)
Un ejemplo de una línea usando la biblioteca Joda-Time :
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
Gracias a Nicolas Mommaerts por su comentario en la respuesta aceptada .
Solo pensé en agregar mi solución a continuación, ya que las respuestas anteriores no satisfacían mis necesidades exactas. Mi esquema Xml requería elementos separados de fecha y hora, no un solo campo de fecha y hora. El constructor estándar XMLGregorianCalendar utilizado anteriormente generará un campo DateTime
Tenga en cuenta que hay un par de gothca, como tener que agregar uno al mes (ya que Java cuenta meses desde 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);
Espero que mi codificación sea correcta; D Para hacerlo más rápido, simplemente use la llamada fea getInstance () de GregorianCalendar en lugar de la llamada del constructor:
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()
es el equivalente de Calendar.getInstance()
. No Calendar.getInstance()
puede hacerlo más rápido, porque usa lo mismo new GregorianCalendar()
, pero antes también comprueba la configuración regional predeterminada y podría crear un calendario japonés o budista, ¡así que para algunos usuarios afortunados lo será ClassCastException
!
Suponiendo que está decodificando o codificando xml y está utilizando JAXB
, entonces es posible reemplazar el enlace dateTime por completo y usar algo más que 'XMLGregorianCalendar' para cada fecha en el esquema.
De esa manera, puede JAXB
hacer las cosas repetitivas mientras puede pasar el tiempo escribiendo código increíble que brinde valor.
Ejemplo de jodatime DateTime
: (Hacer esto con java.util.Date también funcionaría, pero con ciertas limitaciones. Prefiero jodatime y está copiado de mi código, así que sé que funciona ...)
<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>
Y el convertidor:
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);
}
}
Vea aquí: ¿cómo reemplazar XmlGregorianCalendar por fecha?
Si está contento de mapear a un instante en función de la zona horaria + marca de tiempo, y la zona horaria original no es realmente relevante, entonces java.util.Date
probablemente también esté bien.
Mira este código: -
/* 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);
Puedes ver el ejemplo completo aquí
ZonedDateTime
clase y los nuevos métodos de conversión agregados a las clases heredadas. Detalles en esta respuesta por Ole VV