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:

 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.

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.

  • Tiempo inactivo de actividad para HistoryRecord?
  • ¿Cómo obtener un temporizador de alta resolución en código nativo de Android?
  • Cómo administrar la expiración de prueba sin conexión para una aplicación de Android?
  • Comprobación si CountDownTimer se está ejecutando
  • Cómo sincronizar correctamente el código RenderScript de Android en Nvidia Shield
  • ¿Cómo puedo agregar un nuevo botón en el widget del selector de fechas en android?
  • Java cronometrar, System.nanoTime () batear que System.currentTimeMillis () pero persiste sobre sueño?
  • Tiempo de sincronización para la grabación de datos en varios dispositivos Android
  • APIs meteorológicas para Android
  • Android limitar el tiempo de grabación con la intención
  • Separar horas de minutos en un valor de tiempo dado
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.