Converter data de Delphi para Java

Alguém já precisou converter data de Delphi para Java?

Bem se não me engano o formato interno das datas em Java é um long de milissegundos desde não sei quando. Já no delphi é um ponto flutuante (double) com a seguinte faixa:

Valor mínimo: -657434.0 (01/01/0100 12:00:00.000 AM)
Valor máximo: 2958465.99999 (31/12/9999 11:59:59.999 PM)

Eu preciso disso porque o banco que acesso com Java é lido e escrito por um alienígena escrito em Delphi.

Já verifiquei e essa faixa cabe no campo do banco de dados, apesar de uma parada sinistra aí. Aliás no MySQL tem uma coisa muito louca: ÀS VEZES ele PERMITE chaves primárias IGUAIS e ÀS VEZES NÃO, sem nenhuma explicação aparente!!! Esse campo de data que é um float no formato do Delphi é que é a chave primária.

De qualquer forma continuarei pesquisando…

Quanto o Delphi retorna para a data 1/1/1970 (que para o Java é o tempo 0?) Aí dá para fazer umas contas.

[quote=thingol]Quanto o Delphi retorna para a data 1/1/1970 (que para o Java é o tempo 0?) Aí dá para fazer umas contas.

[/quote]´

É disso que estou com medo, de fazer as contas!!!
Queria achar algo pronto.

Bem vou ver isso aí e posto depois…

Se tivesse guardado no formato Date do banco de dados teria dado esse problema??? Como está guardada a data?

Aí é que tá, o programa em Delphi armazena a data como um campo float e não como um date que provavelmente o MySQL tem. E não posso mudar esse fato.

Caras por enquanto estou dando uma pesquisada numas coisas, depois posto mais… agora vou almoçar

Diquinha quente: http://joda-time.sourceforge.net/

O difícil é achar informações, mas estou olhando a documentação…

Minhas conclusões até agora:

Bem a data e a hora do Delphi ficam num TDateTime, que é um apelido para o tipo Double (8 bytes). Ele armazena o número de dias passados desde 30/12/1899, indo para valores negativos e positivos, observando-se os valores limites já citados. E também pode admitir valores fracionários, de modo que a data 30/12/1899 12h:0min:0s:0ms é representada como 0,5. A parte fracionária então representa as horas, minutos, segundos e milissegundos, ocupando para isso uma faixa de 8 dígitos na base 10, de modo que a data 30/12/1899 23h:59min:59s:999ms é representada por 0,99999999).

Parêntesis: por isso a data máxima limite indicada acima como 31/12/9999 11:59:59.999 na verdade é representada pelo valor 2958465.999999[color=red]99[/color] e não 2958465.999999, que na verdade representa a data 31/12/9999 11:59:59.136 (136 milissegundos e não 999).

Bem descobri essas coisas olhando o código das funções EncodeDate e EncodeTime, que pegam valores separados de ano/mês/dia e hora/minuto/segundo/milissegundo, respectivamente, e transformam num TDateTime cada uma, bastando somar os dois valores para obter a representação conjunta de data e hora.

Tal representação é como a do Java, só que este usa um inteiro (long) de se não me engano 8 bytes também, que representa os milisegundos desde 1/1/1970 (isso tem a ver com Unix) enquanto que TDateTime representa dias desde 30/12/1899, e de modo fracionário, o que permite também reepresentar os milissegundos.

Bem se eu não achar algo pronto, vou ter que implementar essas funções EncodeDate e EncodeTime em Java, adaptando o algoritmo usado. E também terei que adaptar as funções contrárias, de decodificação, já que fazemos consultas no banco de dados pelo Java. Bem se tiver que fazer isso provavelmente vou usar o GregorianCalendar ou então o Joda-time que o Quirino indicou. Aliás procurei e não achei nada na documentação dessa ferramenta sobre converter para esse formato do Delphi ou qualquer outro. Mas minha impressão é que ela é uma boa biblioteca para trabalhar. Bem, ela diz que a API de datas do Java é lenta e ineficiente.

PS: no meu micro o long zero é retornado pela classe java.util.Date como 31/12/1969 21h, ou seja três horas antes que o normal. Meu SO é Windows e o fuso horário é GMT -3h. Muito estranho isso, espero descobrir o que está acontecendo.

Bem vou continuar nessa labuta e continuo aceitando sugestões.

Bem acho que com o que descobri agora já dá pra fazer o que eu quero!!

Na verdade 2958465.999999[color=red]99[/color] ainda é um valor aproximado, arredondado em milissegundos. Isto explica as frações abaixo ou acima do valor original na operação contrária. Tais frações devem ser arredondadas para corrigir a aproximação inicial.

Bem quanto à isso o que acontece é que ao formatar a data eu obtenho o valor para o meu fuso horário (-3h) automaticamente. A data 1/1/1970 0h é referente ao GMT 00:00. Na verdade não compreendo qual era a minha dúvida quanto à isso!!! :?

Bem anida queria saber isso!! Alguém tem uma luz???

Conversão de datas

Bem, o principal formato de data no Delphi é o utilizado pelo TDateTime, relacionado à “automação OLE” e com as seguntes características:

  • Valor zero: 30/12/1899 0h GMT 00:00
  • Armazenado em dias fracionados (precisão de milissegundos)
  • Limites: -657434.0 (01/01/0100 12h), 2958465.99999[color=red]99[/color] (31/12/9999 23:59:59.999) (Bem na verdade acho que dá pra ultrapassar estes valores sem problemas no Delphi, parece que o limite refere-se apenas à especificação)

No java, a classe java.util.Date armazena a data no padrão UTC usado pelo Unix, com as seguintes características:

  • Valor zero: 1/1/1970 0h GMT 00:00
  • Armazenado em milissegundos inteiros
  • Limites: São milhões de anos em ambos os sentidos, algo assim

Transformação Mágica

Bem após queimar o tico e o teco cheguei na feliz conclusão que são necessárias apenas duas linhas para fazer a conversão, uma para a conversão de Java para Delphi e outra para a conversão contrária. Para tal devemos considerar os dias passados entre os respectivos zeros, 30/12/1899 e 1/1/1970. São 25569 dias, seguindo o calendário gregoriano. Devemos considerar também os milissegundos existentes em um dia, 24 * 60 * 60 * 1000 = 86400000. As fórmulas ficam assim (onde Delphi é um double que representa os dias TDateTime e Java um long que representa os milissegundos UTC):

Delphi = (Java / 86400000) + 25569
Java = (Delphi - 25569) * 86400000

Exemplo de conversão para a data 1/1/1970 23:59:59.999 GMT 00:00:

a) Java para Delphi (Java = 86399999)
   Delphi = (86399999 / 86400000) + 25569 = ~25569,99999999 (precisão suficiente para guardar os milissegundos)
b) Delphi para Java (Delphi = 25569,99999999)
   Java = (25569,99999999 - 0,99999999) * 86400000 = 86399999,1360 = ~86399999

Bom ainda falta implementar isso mas fiquei muito contente quando meus neurônios me contaram que seria apenas uma linha para cada conversão :smiley:

O MySQL não permite chaves primárias iguais.
A questão é saber se os valores de pontos flutuantes são exatamente iguais, erros de precisão podem ocorrer.
Se você ver em Comparing floating point numbers, comparações == entre valores de pontos flutuantes podem ser falsas, mesmo você acreditando que os números são os mesmos.

Dharis, usei o MySQL-Front e cadastrei manualmente no banco, copiei o valor 38411,9890920833 e consegui criar um novo registro, mas tem hora que não dá!!!

[code]public class DateConverter {

public static double delphiDate(long javaDate) {
	return (double) javaDate / 86400000d + 25569d;
}

public static Date javaDate(double delphiDate) {
	return new Date((long) (86400000 * (delphiDate - 25569)));
}

}[/code]

Uma dúvida: quando eu converto um double para long no método javaDate acima, ele arredonda o valor ou trunca? Exemplo:

double 1,5 trunca para 1 ou arredonda para 1 ou 2?

O “cast” de double para long trunca (para baixo, se for um número positivo, e para cima, se for um número negativo.)

(long) 1.3 -> 1
(long) 1.5 -> 1
(long) 1.51 -> 1
(long) -1.3 -> -1
(long) -1.5 -> -1
(long) -1.51 -> -1

Eu digo que é “para cima”, no caso de números negativos, porque -1.5 é menor que -1.

Sacado, então tem como usar uma função de arredondamento? Porque as fórmulas não podem truncar, elas têm que arredondar!

Exemplo de data no Delphi: 39644.67821708333
-> Parte inteira: quantos dias desde 30/12/1899?
-> Parte fracioária: segundos desde a meia-noite da data

1 = 24 * 60 * 60 = 86400 segundos / dia

39644 + 30/12/1899 = [15/07/2008]

0.0000115741 = FATOR MÁGICO = 1 / 86400
0.67821708333 = PARTE FRACIONÁRIA

FRACIONARIA * 86400 = 58597 / 3600 = 16h [16:XX:XX]
-(16h * 3600) = 997 / 60 = 16min [16:16:XX]
-(16min * 60) = 37 s [16:16:37]

A função transforma a data certinho, mas da um problema no horário… ele atrasa hora em 3 horas (por causa do GMT).

[quote=renato3110][code]public class DateConverter {

public static double delphiDate(long javaDate) {
	return (double) javaDate / 86400000d + 25569d;
}

public static Date javaDate(double delphiDate) {
	return new Date((long) (86400000 * (delphiDate - 25569)));
}

}[/code]
[/quote]