Mohamd Ali
Mohamd Ali

Reputation: 2245

Accept Arabic date as query parameter in Spring Boot

I want to accept Arabic date in the query parameter

ex: ٢٠٢١-٠٧-٢٠ the English version of this date is 2021-07-20 I have found this soltion

DecimalStyle defaultDecimalStyle
        = DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle();
DateTimeFormatter arabicDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE
        .withDecimalStyle(defaultDecimalStyle.withZeroDigit('\u0660'));

String encodedArabicDateStr = "%D9%A2%D9%A0%D9%A1%D9%A9-%D9%A0%D9%A4-%D9%A1%D9%A5";
String arabicDateStr
        = URLDecoder.decode(encodedArabicDateStr, StandardCharsets.UTF_8);
LocalDate date = LocalDate.parse(arabicDateStr, arabicDateFormatter);
System.out.println("Parsed date: " + date);

And it works as expected

now, in my Spring Boot application, I handle the LocalDate with @DateTimeFormat with pattern

@DateTimeFormat(pattern = "yyyy-MM-dd") val fromDate: LocalDate

How I can tell the Spring Boot to use a custom method to parse the date? The original problem is when someone send Arabic date the application it will crash with the below error:

org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'dateRange' on field 'fromDate': rejected value [٢٠٢١-٠٧-٢٠];
codes [typeMismatch.dateRange.fromDate,typeMismatch.fromDate,typeMismatch.java.time.LocalDate,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [dateRange.fromDate,fromDate];
arguments [];
default message [fromDate]];
default message [Failed to convert value of type 'java.lang.String[]' to required type 'java.time.LocalDate';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate] for value '٢٠٢١-٠٧-٢٠'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [٢٠٢١-٠٧-٢٠]]

I have tried:

Upvotes: 3

Views: 186

Answers (2)

Chaosfire
Chaosfire

Reputation: 6985

Since you need to parse the date from query parameter, you need to implement the logic as a Converter.

@Component
public class ArabicDateConverter implements Converter<String, LocalDate> {

  private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE
          .withDecimalStyle(DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle().withZeroDigit('\u0660'));

  @Override
  public LocalDate convert(String source) {
    return LocalDate.parse(source, formatter);
  }
}

Note @Component, it's used to register the converter.

Keep in mind that this converts only arabic dates, it will fail for other formats. If you need to parse others, you may need to keep a list of possible formats to iterate over until something matches.

Upvotes: 1

Jonathan JOhx
Jonathan JOhx

Reputation: 5968

You can create your own custom deserializer for LocalDate which will contain the format field.

Creating of custom deserializer for LocalDate

public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {
    @Override
    public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {

        DecimalStyle defaultDecimalStyle = DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle();
        DateTimeFormatter arabicDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE.withDecimalStyle(defaultDecimalStyle.withZeroDigit('\u0660'));

        String arabicDateStr = URLDecoder.decode(jsonParser.getText(), StandardCharsets.UTF_8);
        return LocalDate.parse(arabicDateStr, arabicDateFormatter);
    }
}

Using the custom deserializer for LocalDate

@JsonSerialize(using = CustomDateDeserializer.class)
private LocalDate fromDate;

Upvotes: 1

Related Questions