Encapsulation d'une date dans un Bean

A partir du moment où l'on utilise Spring pour développer une application web (MVC), on est confronté tôt ou tard à la problématique des backing beans Spring.

Un backing bean est un simple objet Java encapsulant des données métier qui va servir à réaliser un mapping automatisé d'un côté vers une base de données, et de l'autre vers un formulaire HTML. C'est cette dernière utilisation qui va nous intéresser aujourd'hui.

public class User {

  private String name = null;
  private String firstName = null;
  private Date birthDate = null;

  public User() {
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getFirstName() {
    return this.firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public Date getBirthDate() {
    return this.birthDate;
  }

  public void setBirthDate(Date birthDate) {
    this.birthDate = birthDate;
  }

}

Nous avons ici un objet classique User qui encapsule une date de naissance en utilisant un objet Date.
La prochaine étape consiste à créer un formulaire pour ajouter un utilisateur.

<form:form>
  <table>
    <tr>
      <td><label>Nom :</label></td>
      <td><form:input path="name"></form:input></td>
    </tr>
    <tr>
      <td><label>Prénom :</label></td>
      <td><form:input path="firstName"></form:input></td>
    </tr>
    <tr>
      <td><label>Date de naissance :</label></td>
      <td><form:input path="birthDate"></form:input></td>
    </tr>
  </table>
  <input type="submit" value="Créer" />
</form:form>

Si on utilise une telle méthode, alors on va être confronté à deux problèmes très rapidement :
  • Comment Spring va convertir automatiquement une date au format jj/mm/aaaa vers un objet Date ?
  • Comment Spring va convertir automatiquement un objet Date au format jj/mm/aaaa pour l'afficher dans le formulaire ?
Pour répondre au premier besoin, il existe ce qu'on appelle des CustomBeanEditor. Ce sont des objets spéciaux permettant de décrire comment convertir une chaîne de caractères vers un objet complexe (un objet Date dans notre cas).

Toutefois, ces CustomBeanEditor ne répondent pas au second problème. De plus, si nous avons besoin d'utiliser des format différents entre l'affichage texte (jj/mm/aaaa), le stockage dans la base de données (aaaammjj) et éventuellement une saisie utilisateur nous ne pouvons pas utiliser plusieurs CustomBeanEditor pour le même type java.util.Date.

Commençons par écrire un objet spécifique qui sera chargé d'encapsuler un objet Date et qui saura le convertir vers différents formats.

public class DateDay {

  private static DateFormat textFormat = new SimpleDateFormat("dd/MM/yyyy");
  private static DateFormat dbFormat = new SimpleDateFormat("yyyyMMdd");

  private Date date = null;

  // Constructeur public
  public DateDay() {
    this.date = new Date();
  }

  // Constructeur privé
  private DateDay(Date date) {
    this.date = date;
  }

  public Date getDate() {
    return this.date;
  }

  public String toTextString() {
    return textFormat.format(this.date);
  }

  public String toDbString() {
    return dbFormat.format(this.date);
  }

  public static DateDay parseText(String src) {
    try {
      return new DateDay(textFormat.parse(src));
    }
    catch(ParseException e) {
      throw new RuntimeException("Bad format");
    }
  }

  public static DateDay parseDb(String src) {
    try {
      return new DateDay(dbFormat.parse(src));
    }
    catch(ParseException e) {
      throw new RuntimeException("Bad format");
    }
  }
}

Il ne reste plus qu'à utiliser à bon escient ce nouvel objet en modifiant notre classe User.

public class User {
  
  private DateDay birthDate = null;
  ...

  public User() {
    this.birthDate = new DateDay();
  }

  ...

  public String getBirthDateText() {
    return birthDate.toTextString();
  }

  public void setBirthDateText(String birthDateText) {
    this.birthDate = DateDay.parseText(birthDateText);
  }

}

La dernière étape consiste à modifier très légèrement notre formulaire Spring pour prendre en compte ces changements.

...
    <tr>
      <td><label>Date de naissance :</label></td>
      <td><form:input path="birthDateText"></form:input></td>
    </tr>
...

Les dates (suite)

Toujours concernant la problématique de gestion des dates, nous allons voir aujourd'hui comment réaliser des calculs sur une date. Par exemple nous aimerions savoir quel jour on était il y a 6 mois.

L'objet java.util.Calendar


C'est une classe abstraite fournie de base, dont il existe une implémentation appelée GregorianCalendar. Comme son nom l'indique il s'agit d'un objet calendrier s'appuyant sur le calendrier Grégorien.

// Récupération d'une instance de calendrier
Calendar c = GregorianCalendar.getInstance();
// Calcul de la date 6 mois avant
c.add(Calendar.MONTH, -6);
// Récupération de la date
Date sixMonthAgo = c.getTime();

Comme on peut le constater, la méthode add permet d'ajouter (ou de soustraire) un nombre entier se rapportant à un intervalle de temps (année, mois, semaine, jour, heure, minute, seconde). Il est possible de réaliser plusieurs opérations à la suite. Le calcul n'est réalisé qu'au moment où on appelle la méthode getTime().

Il est possible d'initialiser le calendrier avec une date pré-calculée.

// Récupération d'une instance de calendrier
Calendar c = GregorianCalendar.getInstance();
Date date = null;
try {
  // Création d'une date arbitraire
  date = new SimpleDateFormat("dd/MM/yyyy").parse("16/06/1980");
  // Initialisation de la date du calendrier
  c.setTime(date);
  // Calcul de la date 6 mois avant
  c.add(Calendar.MONTH, -6);
  // Récupération de la date
  date = c.getTime();
}
catch(ParseException e) {
  // Erreur de format
}

Les dates (suite)

Nous allons poursuivre notre étude de la gestion des dates en Java en examinant de nouveaux objets.

L'objet java.text.SimpleDateFormat


Cet objet permet deux choses : d'une part afficher un objet Date sous une forme texte en utilisant un formatage personnalisé, d'autre part de parser une date au format texte pour la convertir en objet Date.

// Affichage d'une date au format jj/mm/aaaa
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date date = new Date();
System.out.println(format.format(date));

Comme on peut le voir, il est possible de modifier le format d'affichage. L'intégralité des codes de formatage est disponible sur les JavaDocs de la classe SimpleDateFormat.


La conversion inverse (date sous forme de texte vers un objet Date) se réalise toujours avec l'objet SimpleDateFormat.

// Conversion d'une date texte en objet
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date date = null;
try {
  date = format.parse("01/10/2009");
}
catch(ParseException e) {
  // Erreur de format
}

Comme pour toutes les opérations de parsing en Java, une exception peut se produire qu'il est nécessaire de traiter.

Il est à noter que certains codes de formatage produisent des noms de jours ou de mois. Cette conversion est sensible à la Locale courante, ainsi on aura bien en français lundi, mardi etc. alors qu'on aura monday, tuesday etc. en anglais.

Les dates

J'ouvre ce blog avec un petit article qui concerne une problématique récurrente en Java, la gestion des dates. En effet, à première vue, Java ne propose pas la même richesse à ce sujet qu'un Perl ou un PHP.

Nous allons vite voir que c'est une idée préconçue et que Java propose de nombreux outils autour de la gestion des dates, souvent méconnus, et envisager leur utilisation concrète dans le cadre d'une application Spring.

L'objet java.util.Date


C'est l'objet le plus simple, sa précision est de l'ordre de la seconde (il existe néanmoins des objets plus précis). Par défaut, un nouvel objet Date contient l'heure courante.

import java.util.Date;
...
Date d = new Date(); // Récupération de la date courante

La fonction de l'objet Date se limite à encapsuler la date courante, d'autres objets permettront ensuite de manipuler cette date ou de l'afficher, nous les étudierons par la suite.