java - specific - ¿La mejor forma de extraer el objeto TimeZone de una Cadena?
timezone gettimezone utc (7)
Tengo un campo de base de datos que contiene un campo de fecha sin procesar (almacenado como datos de caracteres), como
Viernes, 26 de septiembre de 2008 8:30 p. M., Hora del este del este
Puedo analizar esto como una fecha fácilmente, con SimpleDateFormat
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);
Lo que me gustaría hacer es extraer un objeto TimeZone de esta cadena. La TimeZone predeterminada en la JVM en la que se ejecuta esta aplicación es GMT, por lo que no puedo usar .getTimezoneOffset()
desde la Date
analizada anteriormente (porque devolverá la TimeZone predeterminada).
Además de tokenizar la cadena sin procesar y encontrar la posición de inicio de la cadena de Zona horaria (ya que sé que el formato siempre será EEEE, MMMM dd, yyyy hh:mm aa zzzz
), hay alguna manera de utilizar la API DateFormat / SimpleDateFormat / Date / Calendar para extraer un objeto TimeZone, que tendrá la misma DateFormat.parse()
que la Cadena que he analizado con DateFormat.parse()
?
Una cosa que me molesta acerca de Date
vs Calendar
en la API de Java es que se supone que Calendar
reemplaza a Date
en todos los lugares ... pero luego decidieron, oh hey, sigamos usando Date
en las clases de DateFormat
.
Bueno, como solución parcial, podría usar una coincidencia RegEx para obtener la zona horaria, ya que siempre tendrá el mismo texto antes. Am o PM.
No sé lo suficiente sobre los husos horarios de Java para obtener la última parte.
@Ed Thomas:
He intentado algo muy similar a tu ejemplo y obtengo resultados muy diferentes:
String testString = "Friday, September 26, 2008 8:30 PM Pacific Standard Time";
DateFormat df = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println("The default TimeZone is: " + TimeZone.getDefault().getDisplayName());
System.out.println("DateFormat timezone before parse: " + df.getTimeZone().getDisplayName());
Date date = df.parse(testString);
System.out.println("Parsed [" + testString + "] to Date: " + date);
System.out.println("DateFormat timezone after parse: " + df.getTimeZone().getDisplayName());
Salida:
El TimeZone predeterminado es: Hora estándar del este
DateFormat timezone antes del análisis: Eastern Standard Time
Parsed [viernes, 26 de septiembre de 2008 8:30 PM Hora estándar del Pacífico] hasta la fecha: sáb Sep 27 00:30:00 EDT 2008
Zona horaria de DateFormat después del análisis: hora estándar del este
Parece que DateFormat.getTimeZone()
devuelve el mismo TimeZone antes y después del parse()
... incluso si lanzo un setTimeZone()
explícito antes de llamar a parse()
.
Al mirar el origen de DateFormat y SimpleDateFormat, parece que getTimeZone()
simplemente devuelve el TimeZone del Calendario subyacente ... que de manera predeterminada será el Calendario del Locale / TimeZone predeterminado a menos que especifique uno para usar.
Ed tiene razón. desea la zona horaria en el objeto DateFormat una vez que se haya analizado la hora.
String rawDate = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time";
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);
System.out.println(rawDate);
System.out.println(scheduledDate);
System.out.println(dbFormatter.getTimeZone().getDisplayName());
produce
Friday, September 26, 2008 8:30 PM Eastern Daylight Time
Fri Sep 26 20:30:00 CDT 2008
Eastern Standard Time
La diferencia principal entre Fecha y Calendario es que esa Fecha es solo un objeto de valor sin métodos para modificarlo. Por lo tanto, está diseñado para almacenar información de fecha / hora en algún lugar. Si usa un objeto de calendario, puede modificarlo después de establecerlo en una entidad persistente que realiza alguna lógica comercial con la información de fecha / hora. Esto es muy peligroso, porque la entidad no tiene forma de reconocer este cambio. La clase Calendar está diseñada para operaciones en fecha / hora, como agregar días o algo así.
Jugando con tu ejemplo me sale lo siguiente:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class TimeZoneExtracter {
public static final void main(String[] args) throws ParseException {
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println(dbFormatter.getTimeZone());
dbFormatter.parse("Fr, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(dbFormatter.getTimeZone());
}
}
Salida:
sun.util.calendar.ZoneInfo [id = "Europe / Berlin" ... sun.util.calendar.ZoneInfo [id = "Africa / Addis_Ababa" ...
¿Es este el resultado que querías?
Recomiendo ver la API de fecha y hora de Joda Time . Recientemente me convertí en creyente, ya que tiende a ser muy superior al soporte integrado para fechas y horas en Java. En particular, debería verificar la clase DateTimeZone . Espero que esto ayude.
Encontré que lo siguiente:
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
Date scheduledDate = dbFormatter.parse("Friday, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(scheduledDate);
System.out.println(dbFormatter.format(scheduledDate));
TimeZone tz = dbFormatter.getTimeZone();
System.out.println(tz.getDisplayName());
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(dbFormatter.format(scheduledDate));
Produce lo siguiente:
Fri Sep 26 20:30:00 CDT 2008
Friday, September 26, 2008 08:30 PM Eastern Standard Time
Eastern Standard Time
Friday, September 26, 2008 08:30 PM Central Daylight Time
De hecho, me pareció algo sorprendente. Pero, supongo que eso muestra que la respuesta a su pregunta es simplemente llamar a getTimeZone en el formateador después de haberlo analizado.
Editar: Lo anterior se ejecutó con Sun''s JDK 1.6.
tl; dr
ZonedDateTime.parse(
"Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ,
DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" )
).getZone()
java.time
La forma moderna es con las clases java.time. La Pregunta y otras Respuestas usan las problemáticas clases heredadas de fecha y hora o el proyecto Joda-Time, los cuales ahora son suplantados por las clases java.time.
Defina un objeto DateTimeFormatter
con un patrón de formato para que coincida con sus datos.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" );
Asigne una Locale
para especificar el idioma humano del nombre del día y el nombre del mes, así como las normas culturales para otros problemas de formato.
f = f.withLocale( Locale.US );
Por último, realice el análisis para obtener un objeto ZonedDateTime
.
String input = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ;
ZonedDateTime zdt = ZonedDateTime.parse( input , f );
zdt.toString (): 2008-09-26T20: 30-04: 00 [America / New_York]
Puede solicitar la zona horaria de ZonedDateTime
, representada como un objeto ZoneId
. Luego puede interrogar a ZoneId
si necesita más información sobre la zona horaria.
ZoneId z = zdt.getZone();
Véalo usted mismo en IdeOne.com .
ISO 8601
Evite intercambiar datos de fecha y hora en este tipo de formato terrible . No asuma el inglés, no personalice su salida con cosas como el nombre del día, y nunca use pseudo-husos horarios, como Eastern Daylight Time
.
Para husos horarios: especifique un nombre de zona horaria adecuado en el formato continent/region
, como America/Montreal
, Africa/Casablanca
o Pacific/Auckland
. Nunca utilice la abreviatura de 3-4 letras, como EST
o IST
ya que no son zonas horarias verdaderas, no están estandarizadas y ni siquiera son únicas (!).
Para serializar valores de fecha y hora en texto, use solo los formatos ISO 8601 . Las clases java.time usan estos formatos de forma predeterminada al analizar / generar cadenas para representar su valor.
Acerca de java.time
El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases heredadas de fecha y hora como java.util.Date
, Calendar
y SimpleDateFormat
.
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a java.time.
Para obtener más información, consulte el Tutorial de Oracle . Y busque para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
¿Dónde obtener las clases de java.time?
- Java SE 8 y SE 9 y posteriores
- Incorporado.
- Parte de la API Java estándar con una implementación integrada.
- Java 9 agrega algunas características y correcciones menores.
- Java SE 6 y SE 7
- Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
- Androide
- El proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente) específicamente para Android.
- Vea Cómo usar ...
El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval
, YearWeek
, YearQuarter
y más .