forinputstring - number format exception java try catch
NumberFormatException mientras se analiza la fecha (5)
Tener una función que crea un objeto Date solo hora. (El motivo por el que se requiere esto es una larga historia que es irrelevante en este contexto, pero necesito comparar algunas cosas en el mundo XML donde TIEMPO (es decir, solo tiempo) es un concepto válido).
private static final SimpleDateFormat DF_TIMEONLY = new SimpleDateFormat("HH:mm:ss.SSSZ");
public static Date getCurrentTimeOnly() {
String onlyTimeStr = DF_TIMEONLY.format(new Date()); // line #5
Date onlyTimeDt = null;
try {
onlyTimeDt = DF_TIMEONLY.parse(onlyTimeStr); // line #8
} catch (ParseException ex) {
// can never happen (you would think!)
}
return onlyTimeDt;
}
Probablemente hay al menos un par de formas para crear una Fecha de solo hora en Java (o más precisamente una donde la parte de la fecha es 1970-01-01) pero mi pregunta no es realmente sobre eso.
Mi pregunta es que este fragmento de código comienza a lanzar aleatoriamente NumberFormatException en la línea # 8 después de haber ejecutado en producción durante mucho tiempo. Técnicamente diría que esto debería ser imposible, ¿verdad?
Aquí hay un extracto de NumberFormatExceptions aleatorias que provienen del código anterior:
java.lang.NumberFormatException: multiple points
java.lang.NumberFormatException: For input string: ".11331133EE22"
java.lang.NumberFormatException: For input string: "880044E.3880044"
java.lang.NumberFormatException: For input string: "880044E.3880044E3"
En primer lugar, espero que podamos estar de acuerdo en que formalmente esto debería ser imposible. El código usa el mismo formato ( DF_TIMEONLY
) como salida y luego entrada. Déjame saber si no estás de acuerdo que debería ser imposible.
No he podido reproducir el problema en un entorno independiente. El problema parece surgir cuando la JVM se ha ejecutado durante mucho tiempo (> 1 semana). No puedo encontrar un patrón para el problema, es decir, el horario de verano / invierno, AM / PM, etc. El error es esporádico, lo que significa que en un minuto lanzará NumberFormatException y en el siguiente minuto funcionará bien.
Sospecho que hay algún tipo de mal funcionamiento aritmético en alguna parte, ya sea en la JVM o quizás incluso en la CPU. Las excepciones anteriores sugieren que hay números de punto flotante involucrados, pero no veo de dónde provendrían. Por lo que sé, el objeto Date de Java es una envoltura alrededor de un long
que contiene el número de milis desde la época.
Supongo que lo que está sucediendo es que hay una cadena inesperada que solo onlyTimeStr
creó en la línea # 5, por lo que el problema realmente reside aquí en lugar de en la línea # 8.
Aquí hay un ejemplo de un stacktrace completo:
java.lang.NumberFormatException: For input string: "880044E.3880044E3"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1241)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2086)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at org.mannmann.zip.Tanker.getCurrentTimeOnly(Tanker.java:746)
Entorno: Java 7
Tiempo de joda
Para su información, la biblioteca JodaTime 2.3 proporciona una clase expresamente para su propósito, solo hora , sin ninguna fecha: LocalTime . Y, es seguro para subprocesos ( instancias inmutables ). Parece una opción mucho mejor que manejar la problemática clase java.util.Date.
LocalTime localTime = new LocalTime();
Volcar a la consola ...
System.out.println( "localTime: " + localTime );
Cuando se ejecuta ...
localTime: 16:26:28.065
java.time
Java 8 trae el nuevo paquete java.time , inspirado en Joda-Time, definido por JSR 310 .
En java.time, encontrarás una clase LocalTime similar a la de Joda-Time.
La causa probable es el hecho de que SimpleDateFormat
no es seguro para subprocesos, y lo está haciendo referencia desde varios subprocesos. Si bien es extremadamente difícil de probar (y casi tan difícil de probar), hay algunas pruebas de que este es el caso:
-
.11331133EE22
: observe cómo todo se duplica -
880044E.3880044E3
- igual aquí
Probablemente tengas al menos dos hilos intercalados. La E
me estaba lanzando, estaba pensando que intentaba lidiar con la notación científica (1E10, etc.), pero es probable que sea parte de la zona horaria .
Afortunadamente, la solución básica (de formato) es simple:
private static final String FORMAT_STRING = "HH:mm:ss.SSSZ";
public static Date getCurrentTimeOnly() {
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING);
String onlyTimeStr = formatter.format(new Date());
return formatter.parse(onlyTimeStr);
}
También hay algunas otras cosas que podría estar haciendo aquí, con algunas advertencias:
1 - Si la zona horaria es UTC (o cualquiera sin DST), esto es trivial
public static Date getCurrentTimeOnly() {
Date time = new Date();
time.setTime(time.getTime() % (24 * 60 * 60 * 1000));
return time;
}
2 - Tendrá problemas para probar este método, ya que no puede pausar el reloj de forma segura (puede cambiar la zona horaria o la ubicación). Para un mejor momento de lidiar con la fecha / hora en Java, use algo como JodaTime . Tenga en cuenta que LocalTime
no tiene una zona horaria adjunta, pero la Date
solo devuelve un desplazamiento en horas enteras (y hay zonas que no están en la hora ); por seguridad, debe devolver un Calendar
(con la zona horaria completa), o simplemente devolver algo sin él:
// This method is now more testable. Note this is only safe for non-DST zones
public static Calendar getCurrentTimeOnly() {
Calendar cal = new Calendar();
// DateTimeUtils is part of JodaTime, and is a class allowing you to pause time!
cal.setTimeInMillis(DateTimeUtils.currentTimeMillis() % (24 * 60 * 60 * 1000));
return cal;
}
Puede usar el bloque "sincronizado" para que sea seguro para subprocesos. Algo como:
synchronized (lastUpdatedFormat) {
date =
lastUpdatedFormat.parse(lastUpdatedFormat.format(currentDate));
}
Si bien la respuesta correcta es la de Clockwork-Muse (la causa de los problemas es el hecho de que SimpleDateFormat
no es seguro para subprocesos). Solo quería ofrecer otro método para crear un objeto Date de solo hora:
public static Date getCurrentTimeOnly() {
Calendar rightNow = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
int hour = rightNow.get(Calendar.HOUR_OF_DAY);
int minute = rightNow.get(Calendar.MINUTE);
int second = rightNow.get(Calendar.SECOND);
int msecond = rightNow.get(Calendar.MILLISECOND);
long millisSinceMidnight
= (hour * 60 * 60 * 1000)
+ (minute * 60 * 1000)
+ (second * 1000)
+ (msecond);
return new Date(millisSinceMidnight);
}
Este método es algo más formalmente correcto, es decir, maneja leap-seconds . No asume, como otros métodos, que todos los días desde la época siempre han tenido 24 * 60 * 60 * 1000 milisegundos.
Sin embargo, no maneja el caso donde el segundo salto está en el día actual.
Tengo la misma pregunta, la causa es que SimpleDateFormat no es seguro para subprocesos, solo agrego syncronized en el método y no vuelve a suceder.