Преобразование строки с арабскими символами в дату

Я пытаюсь убедиться, что список элементов отсортирован в соответствии с их датой описания через Java и appium. Мне удалось извлечь даты с экрана в виде строки, но я столкнулся с трудностями при преобразовании этих строк в даты, потому что строка в основном содержит дату на арабском языке, например: يناير ٧ ٢٠٢٠

Я пытался использовать код ниже,

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu d MMMM ", new Locale("ar"));
        LocalDate orderDate = LocalDate.parse(date, formatter);

Однако я получаю следующую ошибку:

java.time.format.DateTimeParseException: Text 'يناير ٧ ٢٠٢٠' could not be parsed at index 0

    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDate.parse(LocalDate.java:428)
    at com.hs.mobile.steps.MyOrdersSteps.getDate(MyOrdersSteps.java:142)
    at com.hs.mobile.steps.MyOrdersSteps.getOrdersDates(MyOrdersSteps.java:133)
    at com.hs.mobile.steps.MyOrdersSteps.verifyOrdersSortedByDateDesc(MyOrdersSteps.java:119)
    at com.hs.mobile.tests.MyOrdersTests.navigateToOrders_OrdersShouldBeSortedByDate(MyOrdersTests.java:30)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
    at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:584)
    at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:172)
    at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
    at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:804)
    at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:145)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.testng.TestRunner.privateRun(TestRunner.java:770)
    at org.testng.TestRunner.run(TestRunner.java:591)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:402)
    at org.testng.SuiteRunner.access$000(SuiteRunner.java:41)
    at org.testng.SuiteRunner$SuiteWorker.run(SuiteRunner.java:443)
    at org.testng.internal.thread.ThreadUtil.lambda$execute$0(ThreadUtil.java:67)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)

Не могли бы вы посоветовать правильное решение этой проблемы?

Спасибо


person Abdulla Abuhammam    schedule 15.01.2020    source источник
comment
Вы знакомы с логическим порядком и визуальным порядком? Первая часть строки даты, которая появляется в вашем вопросе, — это название месяца, следовательно, первая часть строки вашего шаблона должна быть ММММ.   -  person Abra    schedule 15.01.2020


Ответы (2)


пожалуйста, ознакомьтесь с моим решением, действительно очень сложно преобразовать из ar в другую локаль, потому что, когда вы читаете с арабского, вам нужно читать текст справа налево следующим образом:

the -> "يناير ٧ ٢٠٢٠" will be: january 7 2020  

поэтому DateFormatter будет выглядеть так:

SimpleDateFormat sdf =
            new SimpleDateFormat("MMMM d yyyy", Locale.forLanguageTag("ar-SA-nu-arab"));

а затем проанализируйте его до даты:

Date d = sdf.parse(date);

и распечатайте его:

        System.out.println(d);

он напечатает это:

Tue Jan 07 00:00:00 MSK 2020
person Хамидилло Мамытов    schedule 15.01.2020
comment
Интересно... этот шаблон не работает в DateTimeFormatter.ofPattern("MMMM d yyyy"), но он говорит мне, что текст 'يناير ٧ ٢٠٢٠' не может быть проанализирован по индексу 6, в то время как исходный шаблон говорит текст 'يناير ٧ ٢٠٢٠' не удалось проанализировать по индексу 0. Это означает, что вы (по крайней мере, частично) правы, указывая на разницу между левым и правым. Вы разобрали его с помощью java.time? Я думаю, проблема в символе ٧ (я думаю, это 7, соответственно d в шаблоне). - person deHaar; 15.01.2020
comment
Это действительно сработало, спасибо. Но что, если я хочу использовать только язык, независимо от страны. Будет ли это работать, есть ли другой тег только для языка? - person Abdulla Abuhammam; 15.01.2020
comment
Пожалуйста, не учите молодежь пользоваться давно устаревшим и заведомо проблемным классом SimpleDateFormat. По крайней мере, не в качестве первого варианта. И не без оговорок. Сегодня у нас намного лучше java.time, современный API даты и времени Java, и его DateTimeFormatter. Это даже использовалось в вопросе, оно отлично справляется со своей задачей, здесь нет абсолютно никаких причин использовать что-либо еще. - person Ole V.V.; 15.01.2020
comment
@deHaar DateTimeFormatter нужно явно указать использовать арабские цифры (например, ٧), тогда как устаревшее SimpleDateFormat подбирает это из локали. Я не знаю, почему такая разница в дизайне. - person Ole V.V.; 15.01.2020
comment
@ОлеВ.В. Да, я видел это, когда искал разные подходы. Очевидно, это одна из немногих вещей, которых было легче достичь с помощью java.util... - person deHaar; 15.01.2020
comment
@deHaar Если вы хотите использовать цифры локали, да. Я считаю совершенно реалистичным представить случаи, когда кто-то хочет использовать название месяца из одной локали и цифры из другой. Мне кажется, что SimpleDateFormat этого сделать не может, а современный DateTimeFormatter может. - person Ole V.V.; 15.01.2020
comment
@ОлеВ.В. Конечно, чем мощнее, тем лучше... У меня не получилось разобрать String из вопроса, вы знаете, как это сделать? Просто из интереса, и я думаю, что принятый ответ подходит для Java 4 и ниже, но он не должен быть единственным рабочим здесь. - person deHaar; 15.01.2020
comment
@ОлеВ.В. Я думаю, что попробовал ваш код (найден на SO и на каком-то азиатском сайте, это и есть ваш текст ответа ;-) ), но это не сработало с той же проблемой (Text could not be parsed at index 0). - person deHaar; 15.01.2020
comment
@ОлеВ.В. Бьюсь об заклад, я не смог скопировать и вставить арабскую дату String ;-) - person deHaar; 16.01.2020

Просто оставайтесь с java.time и DateTimeFormatter

    char arabicZero = '\u0660';
    DateTimeFormatter dateFormatter = DateTimeFormatter
            .ofPattern("MMMM d uuuu", Locale.forLanguageTag("ar"));
    DecimalStyle arabicDecimalStyle
            = dateFormatter.getDecimalStyle().withZeroDigit(arabicZero);
    dateFormatter = dateFormatter.withDecimalStyle(arabicDecimalStyle);

    String dateString = "يناير ٧ ٢٠٢٠";

    LocalDate date = LocalDate.parse(dateString, dateFormatter);
    System.out.println("Parsed date: " + date);

Вывод из этого фрагмента:

Дата анализа: 2020-01-07

По моему самому честному мнению, вы поступили правильно, когда попытались использовать DateTimeFormatter для этой работы. Этот класс является частью java.time, современного API даты и времени Java. В отличие от старого и давно устаревшего SimpleDateFormat, по умолчанию он использует западные цифры, поэтому нам нужно указать ему явно использовать арабские цифры. Как обсуждалось в комментариях, этот дизайн позволяет нам создавать средства форматирования с любой комбинацией названий месяцев из одного языка и цифр из другого, как для форматирования, так и для синтаксического анализа.

person Ole V.V.    schedule 15.01.2020