Conversion de date

Pour formater une date en chaîne de caractère ou pour convertir une chaîne de caractère en date, on utilise la classe java.time.format.DateTimeFormatter, depuis le JDK 8. Avec les JDK précédents, on utilisait java.text.DateFormat ou sa sous-classe java.text.SimpleDateFormat.

DateTimeFormatter.parse()

En choisissant, et imposant, le format de saisie de date, le code de parsing est simple.

public class DateFormatUtil {

  private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")

  public static TemporalAccessor parse(String dateSaisie) {
    if (dateSaisie != null) {
      TemporalAccessor date = formatter.parse(dateSaisie);
      return date;
    }
    return null;
  }
}

SimpleDateFormat.parse()

Dans une application, pour pouvoir faire les conversions, il est nécessaire d’imposer un format de saisie, qui correspond au pattern du SimpleDateFormat.

public class DateFormatUtil {

  private String dateFormat = "dd/MM/yyyy"

  public static Date parse(String dateSaisie) throws ParseException {
    if (dateSaisie != null) {
      SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
      Date date = formatter.parse(dateSaisie);
      return date;
    }
    return null;
  }
}
Contrairement à java.time.format.DateTimeFormatter, java.text.SimpleDateFormat n’est pas thread-safe. C’est pourquoi je l’utilise en variable locale.

Multi-formats

Afin de rendre l’application plus conviviale, il faut donner une plus grande souplesse de saisie à l’utilisateur et supporter une série de patterns. Avant d’en appliquer un, il faut rechercher celui qui est adapté à la saisie, ce qui peut se faire avec des expressions régulières.

public class DateFormatUtil {

  private static Map<String , DateTimeFormatter> regexToDatePattern
            = Map.of(
                  "(\\d{1})/(\\d{1})/(\\d{2})", DateTimeFormatter.ofPattern("d/M/yy"),
                  "(\\d{2})/(\\d{2})/(\\d{4})", DateTimeFormatter.ofPattern("dd/MM/yyyy"),
                  "(\\d{2})/(\\d{2})/(\\d{2})", DateTimeFormatter.ofPattern("dd/MM/yy"),
                  "(\\d{1})/(\\d{2})/(\\d{2})", DateTimeFormatter.ofPattern("d/MM/yy"),
                  "(\\d{2})/(\\d{1})/(\\d{2})", DateTimeFormatter.ofPattern("dd/M/yy"),
                  "(\\d{1})/(\\d{1})/(\\d{4})", DateTimeFormatter.ofPattern("d/M/yyyy"),
                  "(\\d{2})/(\\d{1})/(\\d{4})", DateTimeFormatter.ofPattern("dd/M/yyyy"),
                  "(\\d{1})/(\\d{2})/(\\d{4})", DateTimeFormatter.ofPattern("d/MM/yyyy")
              );

  public static TemporalAccessor parse(String dateSaisie) throws ParseException{
    if (dateSaisie != null) {
      return regexToDatePattern.entrySet().stream()
                    .filter(entry -> dateSaisie.matches(entry.getKey()))
                    .findAny()
                    .map(entry -> entry.getValue().parse(dateSaisie))
                    .orElse(null);

    }
    return null;
  }
}

Avant le JDK 8, on stockait le pattern dans la map, pour instancier un formatter à chaque besoin.

public class DateFormatUtil {

  private static Map<String , String> regexToDatePattern = new HashMap<String, String>();

  static {
    regexToDatePattern.put("(\\d{1})/(\\d{1})/(\\d{2})", "d/M/yy");
    regexToDatePattern.put("(\\d{2})/(\\d{2})/(\\d{4})", "dd/MM/yyyy");
    regexToDatePattern.put("(\\d{2})/(\\d{2})/(\\d{2})", "dd/MM/yy");
    regexToDatePattern.put("(\\d{1})/(\\d{2})/(\\d{2})", "d/MM/yy");
    regexToDatePattern.put("(\\d{2})/(\\d{1})/(\\d{2})", "dd/M/yy");
    regexToDatePattern.put("(\\d{1})/(\\d{1})/(\\d{4})", "d/M/yyyy");
    regexToDatePattern.put("(\\d{2})/(\\d{1})/(\\d{4})", "dd/M/yyyy");
    regexToDatePattern.put("(\\d{1})/(\\d{2})/(\\d{4})", "d/MM/yyyy");
  }

  public static Date parse(String dateSaisie) throws ParseException{
    if (dateSaisie != null) {
      for (Entry<String, String> entry : regexToDatePattern.entrySet()) {
        if (dateSaisie.matches(entry.getKey())) {
          SimpleDateFormat fmt = new SimpleDateFormat(entry.getValue());
          Date d = fmt.parse(dateSaisie);
          return d;
        }
      }
    }
    return null;
  }
}