Quería analizar la cadena de fecha y hora y la zona horaria con dígitos árabes-hindúes, así que escribí un código como este:
String dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+٠٢:٠٠"; char zeroDigit = '٠'; Locale locale = Locale.forLanguageTag("ar"); DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX") .withLocale(locale) .withDecimalStyle(DecimalStyle.of(locale).withZeroDigit(zeroDigit)); ZonedDateTime parsedDateTime = ZonedDateTime.parse(dateTime, pattern); assert parsedDateTime != null;Pero recibí la excepción:
java.time.format.DateTimeParseException: el texto '٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+٠٢:٠٠' no se pudo analizar en el índice 19
Revisé muchas preguntas en Stackoverflow, pero todavía no entiendo qué hice mal.
Funciona bien con dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+02:00" cuando la zona horaria no usa dígitos árabes-hindúes.
Su cadena de fecha y dateTime es incorrecta, mal entendida. Obviamente intenta ajustarse al formato ISO 8601 y falla. Porque el formato ISO 8601 usa dígitos US-ASCII.
Las clases de java.time ( Instant , OffsetDateTime y ZonedDateTime ) analizarían su cadena sin ningún formateador si solo los dígitos fueran correctos para ISO 8601. En la gran mayoría de los casos, tomaría su camino: intente analizar la cadena tal como es . No en este caso. Para mí tiene más sentido corregir la cadena antes de analizarla.
String dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+٠٢:٠٠"; char[] dateTimeChars = dateTime.toCharArray(); for (int index = 0; index < dateTimeChars.length; index++) { if (Character.isDigit(dateTimeChars[index])) { int digitValue = Character.getNumericValue(dateTimeChars[index]); dateTimeChars[index] = Character.forDigit(digitValue, 10); } } OffsetDateTime odt = OffsetDateTime.parse(CharBuffer.wrap(dateTimeChars)); System.out.println(odt);Producción:
2021-11-08T02:21:08+02:00
Editar: será aún mejor, por supuesto, si puede educar al editor de la cadena para que use dígitos US-ASCII.
Editar: sé que el artículo de Wikipedia al que enlazo a continuación dice:
Las representaciones deben escribirse en una combinación de números arábigos y los caracteres informáticos específicos (como "-", ":", "T", "W", "Z") a los que se asignan significados específicos dentro del estándar; …
Esta es una causa imaginable de la confusión. El artículo Números arábigos vinculado a dice:
Los números arábigos son los diez dígitos: 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9.
Editar: cómo convierto cada dígito: Character.getNumericValue() convierte de un char que representa un dígito a un int igual al número que representa el dígito, por lo que '٠' a 0, '٢' a 2, etc. Funciona para todos los caracteres que son dígitos (no solo los árabes y ASCII). Character.forDigit() realiza una especie de conversión opuesta, solo que siempre a US ASCII, por lo que 0 a '0' , 2 a '2' , etc.
Editar: gracias a @Holger por llamar mi atención sobre CharBuffer en este contexto. Un CharBuffer implementa CharSequence , el tipo que requieren los métodos de parse de java.time, por lo que nos evita convertir la matriz char de nuevo en String .
El mensaje de error indica que el problema está en el índice 19 en la cadena de entrada.
El carácter 19 es el carácter + en su cadena de entrada. Esto significa que el desplazamiento (representado por XXX en su patrón) no se puede analizar.
El problema no es el + en sí mismo. El problema es que las compensaciones de zona horaria, como +05:00 , nunca se localizan.
La documentación no habla de esto, así que tuve que ir al código fuente de DateTimeFormatterBuilder para verificarlo.
Dentro de esa clase está esta clase interna :
static final class OffsetIdPrinterParser implements DateTimePrinterParser {En esa clase, podemos encontrar un método de análisis que tiene llamadas a los métodos privados parseHour , parseMinute y parseSeconds .
Cada uno de esos métodos se delega a un método parseDigits privado. En ese método, podemos ver que solo se consideran dígitos ASCII :
char ch1 = parseText.charAt(pos++); char ch2 = parseText.charAt(pos++); if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') { return false; }Entonces, la respuesta aquí es que el desplazamiento de la zona horaria debe consistir en dígitos ASCII, independientemente de la configuración regional.