Conversion de date

Pour formater une date en chaîne de caractères ou pour convertir une chaîne de caractères 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 static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");

  public static TemporalAccessor parse(String dateSaisie) {
    if (dateSaisie != null) {
      return formatter.parse(dateSaisie);
    }
    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 static String dateFormat = "dd/MM/yyyy"

  public static Date parse(String dateSaisie) {
    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) {
    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) {
    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;
  }
}