GUJ Discussões   :   últimos tópicos   |   categorias   |   GUJ Respostas

SimpleDateFormat bugou 2 vezes

Boa tarde,

Estou resolvendo um problema aqui na empresa em que o sistema retornou um XML de resposta com campos de data com o seguinte formado: 2006-0005-02

Porém, deveria ser retornado assim: 2006-05-02

Na classe Java que carrega todos os dados interessados no resultSet, para jogar no arquivo XML o campo da data eu fiz um método formatDate (para a String data, retornada da query):

  // Date Formatter 
  private static final SimpleDateFormat dateFormatter;

  static {
    dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
    dateFormatter.setLenient(false);
  }

	public static String formatDate(String date)
	{
		if (date == null)
		{
			return "";
		}

		try
		{
			return dateFormatter.format(Timestamp.valueOf(date));
		}
		catch (Exception ex)
		{
			logger.info("Unable to format date : " + date);
			return "";
		}
	}

O sistema fica correndo processos diários no servidor para integração de diversos dados.

Alguém sabe se uma sobrecarga do sistema pode fazer com que o SimpleDateFormat fique louco ?

Ao invés de seguir o “yyyy-MM-dd” ele retorne 2006-0005-02 ???

E estou presionado, pois preciso dar uma resposta para o cliente pra ontem, mas todos os testes que eu faço estão retornando corretamente a data pelo método formatDate().

Complementando:
O formato retornado pelo Oracle é 15-05-2006 (dd-mm-yyyy).

Agradeço se puderem ajudar. :?

   private static final SimpleDateFormat dateFormatter;
 
   static {
     dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
     dateFormatter.setLenient(false);
   }

A sua intenção é boa (ter apenas um objeto SimpleDateFormat), mas provavelmente seu sistema é multithread (por exemplo, você chama isso a partir de um servlet ou JSP).
Você sabe que essa classe não é thread-safe, por isso você pode ter algumas surpresas desagradáveis como esse “2006-0005-02”.
É melhor ter uma instância desse objeto por cada thread. Daqui a pouco eu posto como você deveria ter feito isso.

Aqui eu estou dando um exemplo para uma classe Java normal, mas poderia ser uma servlet ou JSP, ou um POJO chamado a partir de uma servlet ou JSP.

import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

class TesteSimpleDateFormatThreadSafe {
    
    /** Isto declara uma instância de SimpleDateFormat para cada thread que acessa sua servlet */
    ThreadLocal tl = new ThreadLocal() {
        protected synchronized Object initialValue() {
            DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");
            df.setLenient (false);
            return df;
        };
    };

    /** Isto mostra como obter a variável "threadLocal" */
    public void efetuaTeste() {
        DateFormat df = (DateFormat) tl.get();
        System.out.println (df.format (new Date()));
    }
    
    /** Este teste não consegue mostrar exatamente o que ocorre. Em um 
     * servlet, várias threads estariam simultaneamente acessando o mesmo objeto servlet,
     * e para cada thread há uma instância de SimpleDateFormat, não havendo então o
     * problema de a data ficar "zoada".
     */
    public static void main(String[] args) {
        TesteSimpleDateFormatThreadSafe teste = new TesteSimpleDateFormatThreadSafe();
        teste.efetuaTeste();
    }
}

Consegui até reproduzir seu erro. Pra mim deu os seguintes resultados malucos, quando pus para rodar com 10 threads em cima do mesmo "static":

Erro! 2006-05-0023
Erro! 2006-0005-23
Erro! 2006-05-0023
Erro! 2006-05-0023
Erro! 2006-05-0023
Erro! 2006-05-0023
Erro! 2006-05-0023
Erro! 2006-05-0023
Erro! 2006-05-0023

import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

class TesteComBug implements Runnable {
    
    private static DateFormat staticDf;
    static {
        staticDf = new SimpleDateFormat ("yyyy-MM-dd");
        staticDf.setLenient (false);
    }
    
    /** Isto declara uma instância de SimpleDateFormat para cada thread que acessa sua servlet */
    ThreadLocal tl = new ThreadLocal() {
        protected synchronized Object initialValue() {
            DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");
            df.setLenient (false);
            return df;
        };
    };

    /** Isto mostra como obter a variável "threadLocal" */
    public void efetuaTeste() {
        DateFormat df = (DateFormat) tl.get();
        System.out.println (df.format (new Date()));
    }
 
    /** Para o teste de threads */
    public void run() {
        while (true) {
//            DateFormat df = (DateFormat) tl.get();  // --> método correto
            DateFormat df = staticDf;               // --> método incorreto
            String s = df.format (new Date());
            if (!s.equals ("2006-05-23")) {
                System.out.println ("Erro! " + s);
                break;
            }
        }
    }
    
    /** 
     * O teste a seguir reproduz o seu erro se você descomentar
     * a linha escrita "método incorreto" e não dá erro se você 
     * descomentar a linha escrita "método correto" acima.
     */
    public static void main(String[] args) {
        TesteComBug teste = new TesteComBug();
        for (int i = 0; i < 10; ++i) {
            new Thread (teste).start();
        }
    }
}

Massa, thingol.

Que versão do java que você estava rodando?

Eu estava tentando reproduzir, mas não consegui, deve ser alguma coisa no meu setup.

Receio que deixei um bug ou dois deste tipo nos sistemas que desenvolvi na Finlândia :x

[]s,
Sami

Caraca thingol !

Valeu mesmo !!!

Thanksssssss, vou alterar com ThreadSafe e testar se retorna ok.

Muito obrigado !!!

Esse post é tão velho que não usa Generics. Passando-o para generics:

import java.util.Date;  
import java.text.DateFormat;  
import java.text.SimpleDateFormat;  
  
class TesteSimpleDateFormatThreadSafe {  
      
    /** Isto declara uma instância de SimpleDateFormat para cada thread que acessa sua servlet */  
    ThreadLocal<DateFormat> tl = new ThreadLocal<DateFormat>() {  
        protected synchronized DateFormat initialValue() {  
            DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");  
            df.setLenient (false);  
            return df;  
        };  
    };  
  
    /** Isto mostra como obter a variável "threadLocal" */  
    public void efetuaTeste() {  
        DateFormat df = tl.get();  
        System.out.println (df.format (new Date()));  
    }  
      
    /** Este teste não consegue mostrar exatamente o que ocorre. Em um  
     * servlet, várias threads estariam simultaneamente acessando o mesmo objeto servlet, 
     * e para cada thread há uma instância de SimpleDateFormat, não havendo então o 
     * problema de a data ficar "zoada". 
     */  
    public static void main(String[] args) {  
        TesteSimpleDateFormatThreadSafe teste = new TesteSimpleDateFormatThreadSafe();  
        teste.efetuaTeste();  
    }  
}