Lorsque l'on parle de parser/formatter des dates en Java, il y a la classe DateFormat qui fournit la base. Et dès que le format est plus étendu, il faut se tourner vers la classe SimpleDateFormat qui est complètement paramétrable.

Pour illustrer le problème, voici basiquement le code que j'ai utilisé :

import java.text.*;
import java.util.*;
 
public class TestDate
{
  /**
   * Formatter utilisé universellement dans 
   * tout le programme.
   */
  public static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
 
  public static void main(String[] args) throws Exception
  {
    String sourceDateString = "2009/01/01 00:00:00";
 
    // on veut éviter les fuseaux horaires
    TimeZone utc = TimeZone.getTimeZone("UTC");
    TimeZone.setDefault(utc);
 
    // parsing de la date
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    Date parsedDate = sdf.parse(sourceDateString);
 
    // formattage selon définition du projet
    String formattedDateString = DATE_FORMATTER.format(parsedDate);
 
    // blablabla
    System.out.println("source : "+sourceDateString);
    System.out.println("formatted : "+formattedDateString);
  }
}

Evidemment, en voyant le code compacté de la sorte, on peut se douter que le TimeZone.setDefault() est suspect la au milieu. Voici ce qu'en dit la console :

source : 2009/01/01 00:00:00
formatted : 01.01.2009 01:00:00

Il y a une heure de différence entre les deux dates ! Et cela peut varier selon le système qui exécute le code ci-dessus.

Ce qui pose problème est la constante DATE_FORMATTER : son TimeZone n'est pas le même que le reste du programme car elle est initialisée avant que le TimeZone par défaut soit défini. Dans mon cas, ce sera Europe/Zurich (GMT+1) qui sera donc utilisé.

Au final pour corriger le souci, il suffit d'ajouter la ligne suivante :

TimeZone utc = TimeZone.getTimeZone("UTC");
TimeZone.setDefault(utc);
DATE_FORMATTER.setTimeZone(utc);

Pourquoi une solution aussi simple est-elle difficile à trouver ? Le fait est que j'enregistre et récupères des dates dans une base de données et que je suis dans un environnement Java EE (Glassfish V2.1 et OpenJPA).

Le décalage horaire est apparu lorsque le serveur était connecté à une base de données MySQL, mais pas à une base de données Oracle. Cela est probablement du au parser utilisé et à son initialisation. Le comportement peut être aussi différent lorsqu'on passe sur un autre DBMS... surtout qu'il faut toujours penser à relancer l'Application Server pour réinitialiser le tout.

En annexe, un petit projet Java EE qui permet de voir le souci en utilisant une base de données Derby !