Problema de análisis de tiempo en Android
Estoy consiguiendo una excepción del parse al intentar analizar la secuencia de tiempo 02:22 pm
.
Tengo la siguiente función de conversión:
- Sincronizar la hora del sistema en 2 teléfonos
- Retardo de tiempo en Android
- Cómo llegar el tiempo en la ubicación actual
- ¿Cómo puedo mostrar la hora en formato 24 horas usando un TextClock?
- sincronización del tiempo del emulador androide
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){ SimpleDateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ROOT); SimpleDateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ROOT); Date date = null; try { date = originalFormat.parse(datestring); String formattedDate = targetFormat.format(date); Date parsedDate = targetFormat.parse(formattedDate); long nowMilliseconds = parsedDate.getTime(); return nowMilliseconds; } catch (ParseException e) { e.printStackTrace(); return 0; } }
El método se llama en otra actividad con un formato de hora "02:22 pm"
. olddateformat
y newdateformat
son los mismos: hh:mm a
.
Provoca el siguiente error en el registro:
java.text.ParseException: Fecha inquebrantable: "02:22 pm" (en el desplazamiento 6)
Cómo resolver este problema? El tiempo está exactamente en el formato mencionado anteriormente.
- Android: ejemplo de getRelativeTime
- Tiempo de generación de DH Keypair en Android
- Tiempo del GPS en Android
- Tiempo del temporizador de cuenta regresiva androide
- Android - Cómo contar el tiempo durante un largo período
- Problema de Android para averiguar la última corrección GPS reciente
- Java cómo incrementar un int 1 cada segundo hasta que llegue a 15
- Haga que el temporizador de cuenta regresiva pase de 10sec a 1sec
Sucede que am y pm se llaman sólo esto en la localidad gaélica. Por lo menos en mi Java 8. Estoy muy seguro de que será el caso de todos los teléfonos Android, pero puede hacer algunos experimentos con él.
String datestring = "02:22 pm"; Locale parseLocale = Locale.forLanguageTag("ga"); DateTimeFormatter originalFormat = DateTimeFormatter.ofPattern("hh:mm a", parseLocale); System.out.println(LocalTime.parse(datestring, originalFormat));
Esto imprime
14:22
Como Hugo tan calurosamente y con razón recomienda es su respuesta , estoy usando la moderna Java fecha y hora API, por lo que necesitará ThreeTenABP para el código anterior. Consulte Cómo utilizar ThreeTenABP en Android Project . Alternativamente, es posible que desee probar la misma configuración regional con su SimpleDateFormat
obsoleto.
La localidad en español de Estados Unidos muestra el mismo comportamiento en mi Java 8, por lo que también puedes probar: Locale.forLanguageTag("es-US")
.
Creo que SimpleDateFormat
no se puede personalizar para analizar la parte pm
(sólo reconoce AM
o PM
).
Así que una alternativa es eliminar los puntos:
String time = "02:22 pm"; SimpleDateFormat format = new SimpleDateFormat("hh:mm a", Locale.ROOT); date = format.parse(time.replaceAll("\\.", ""));
Un detalle: para obtener el valor nowMilliseconds
, necesita todos los campos de fecha (día / mes / año) y un huso horario. Dado que estos campos no están en la String
entrada, SimpleDateFormat
establece a 1 de enero de 1970 (y también establece los segundos y milisegundos a cero) y utiliza la zona horaria predeterminada del sistema.
No estoy seguro de si este comportamiento de conseguir enero de 1970 es coherente entre todas las versiones de Java, que es otro problema porque puede obtener diferentes valores dependiendo del entorno / dispositivo que el código está ejecutando. En realidad, es posible que tenga un resultado diferente de todos modos porque utiliza la zona horaria predeterminada del sistema y esto puede variar entre diferentes entornos.
Si ejecuto este código en mi máquina, utiliza la zona horaria predeterminada de mi sistema ( America/Sao_Paulo
), y el resultado es 62520000
. Pero si cambio la zona horaria a otra (digamos, Asia/Kolkata
), el resultado es 31920000
. Usted debe ser consciente de esta variación y comprobar si eso es lo que realmente necesita.
Otro detalle es que, si olddateformat
y newdateformat
son iguales, no hay necesidad de crear dos formateadores diferentes.
La nueva API de fecha y hora de Java
Las clases viejas ( Date
, Calendar
y SimpleDateFormat
) tienen muchos problemas y problemas de diseño y se están reemplazando por las nuevas API.
En Android puede utilizar el Backport de ThreeTen , un gran backport para las nuevas clases de fecha / hora de Java 8. También necesitará el ThreeTenABP (más información sobre cómo usarlo aquí ).
Todas las clases relevantes están en el paquete org.threeten.bp
.
Con esta nueva API, puede personalizar el texto que corresponde a AM / PM utilizando un org.threeten.bp.format.DateTimeFormatterBuilder
(por lo que no es necesario eliminar los puntos manualmente). Y hay clases específicas para cada caso – en este caso, la entrada sólo tiene los campos de tiempo (hora y minutos), así que voy a usar la clase org.threeten.bp.LocalTime
(que representa sólo una hora-hora / minuto / segundo / nanosegundo – sin fecha):
String time = "02:22 pm"; // map AM and PM values to strings "am" and "pm" Map<Long, String> map = new HashMap<Long, String>(); map.put(0L, "am"); map.put(1L, "pm"); DateTimeFormatter fmt = new DateTimeFormatterBuilder() // hour and minute .appendPattern("hh:mm ") // use custom values for AM/PM .appendText(ChronoField.AMPM_OF_DAY, map) // create formatter .toFormatter(Locale.ROOT); // parse the time LocalTime parsedTime = LocalTime.parse(time, fmt);
La variable parsedTime
contendrá los valores correspondientes a 02:22 PM
(y sólo este valor, no tiene campos de fecha (día / mes / año) ni un huso horario).
Para obtener el valor de milisegundos ( número de milisegundos desde 1970-01-01T00:00Z
), también necesita una fecha (día / mes / año) y un huso horario. Como dije anteriormente, esos campos pueden afectar al valor final.
En la API antigua, SimpleDateFormat
intenta ser "inteligente" y establece valores predeterminados para esos campos ( 1 de enero de 1970 en la zona horaria por defecto del sistema), pero la nueva API es más estricta y debe indicar explicitamente qué fecha y zona horaria usted quiere.
En este ejemplo, estoy utilizando el Asia/Kolkata
horario de Asia/Kolkata
pero puedes cambiarlo de acuerdo a tus necesidades (más información a continuación):
import org.threeten.bp.LocalDate; import org.threeten.bp.ZoneId; import org.threeten.bp.ZonedDateTime; // timezone for Asia/Kolkata ZoneId zone = ZoneId.of("Asia/Kolkata"); // current date in Kolkata timezone LocalDate now = LocalDate.now(zone); // get the parsed time at the specified date, at the specified zone ZonedDateTime zdt = parsedTime.atDate(now).atZone(zone); // get the millis value long millis = zdt.toInstant().toEpochMilli();
Si desea una fecha específica en lugar de la fecha actual, puede usar LocalDate.of(2017, 5, 20)
– esto obtendrá el 20 de mayo de 2017 , por ejemplo. Con esto, puede establecer el código anterior a la fecha y la zona horaria que necesita.
Tenga en cuenta que la API utiliza los nombres de zonas horarias de IANA (siempre en el formato Region/City
, como America/Sao_Paulo
o Asia/Kolkata
). Evite usar las abreviaturas de 3 letras (como IST
o PST
) porque son ambiguas y no estándar .
Puede obtener una lista de zonas horarias disponibles (y elegir la que mejor se adapte a su sistema) llamando a ZoneId.getAvailableZoneIds()
.
Si desea emular exactamente lo que SimpleDateFormat
hace, puede usar LocalDate.of(1970, 1, 1)
y usar el ZoneId.systemDefault()
horario por defecto con ZoneId.systemDefault()
– pero esto no se recomienda, ya que el sistema puede cambiarse por defecto sin previo aviso , incluso en tiempo de ejecución. Es mejor especificar qué zona horaria está utilizando.
O puede crear un formateador que siempre establece valores predeterminados para la fecha (utilizando la clase org.threeten.bp.temporal.ChronoField
) y siempre utiliza el mismo huso horario. Así que puede analizarlo directamente a un org.threeten.bp.Instant
y obtener el valor millis:
String time = "02:22 pm"; ZoneId zone = ZoneId.of("Asia/Kolkata"); DateTimeFormatter fmt2 = new DateTimeFormatterBuilder() // hour and minute .appendPattern("hh:mm ") // use custom values for AM/PM (use the same map from previous example) .appendText(ChronoField.AMPM_OF_DAY, map) // default value for day: 1 .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) // default value for month: January .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) // default value for year: 1970 .parseDefaulting(ChronoField.YEAR, 1970) // create formatter at my specific timezone .toFormatter(Locale.ROOT).withZone(zone); // get the millis value long millis = Instant.from(fmt2.parse(time)).toEpochMilli();
Después de los cambios que he hecho funciona bien para mí.
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){ DateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ENGLISH); DateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ENGLISH); Date date = null; try { date = originalFormat.parse(datestring.replaceAll("\\.", "")); String formattedDate = targetFormat.format(date); Date parsedDate = targetFormat.parse(formattedDate); long nowMilliseconds = parsedDate.getTime(); return nowMilliseconds; } catch (ParseException e) { e.printStackTrace(); return 0; } }
Locale.ENGLISH puedes usar tu locale, english resolvió mi problema. Referencia.
Gracias por las respuestas y referencias.
- Posible motivo de "Error: no se pueden generar carpetas de vista java.lang.NullPointerException"
- Bluetooth descubrir / escanear código no funciona después de la actualización Android 6 en mi Nexus 5