jueves, enero 24, 2008

Tipos de datos DateTime en xHarbour

Una de las cosas mas apreciadas del querido CA-Clipper es sin duda su capacidad para trabajar con tipos de datos fecha, con ellos puedes a una fecha sumar días, restarlos, o incluso obtener la diferencia de días entre 2 fechas con una simple operación de resta.

En su momento, ese poder fué de gran ayuda y sin duda aún lo sigue siendo.

Pero los tiempos han cambiado, ahora podemos usar xHarbour para que usando ADO tengamos acceso a otros formatos de base datos como por ejemplo acceder a servidores SQL y esto a su vez ha traído algunas complicaciones, que pretendo ayudar a resolver con este artículo.

Si bien la mayoría de las tablas SQL soportan los típicos formatos de datos XBase (caracter, numérico, lógico, fecha y memo), también pueden soportar otros tipos de campo como float, integer, char, string, y la razón de escribir este artículo: Los tipos de campo DateTime.

Un campo tipo DateTime guarda precisamente eso, un campo que vincula en un solo tipo esos 2 elementos, la fecha y la hora.

xHarbour provee de un completo soporte a campos DateTime, solo que tenemos que ser un poco cuidadosos para utilizarlos, pero en realidad es muy sencillo.

La sintaxis:

Un campo DateTime, requiere de un formateo especial que es :

{^ AAAA/MM/DD HH:MM:SS.CCC [AM|PM] }

La información se encierra entre llaves y se antepone un signo exponencial después de la primer llave.

En una variable del tipo DateTime, la fecha se indica primero y SIEMPRE debe expresarse con el formato año-mes-dia, siendo indispensable que el año SIEMPRE sea expresado con 4 dígitos, los meses con 2 y los días con 2, también es indispensable que se utilice la barra diagonal (/) como separador de los dígitos, no se puede utilizar ningún otro tipo de separador. A diferencia de los tipos de datos fecha (date) el formato de la fecha para el dato tipo TimeDate no tiene ninguna influencia de o para el formato que hayas indicado con SET DATE o con SET EPOCH.

La parte de la hora va después de la sección de la fecha, separada por un espacio en blanco, el formato es hora-minuto-segundo-milisegundo, es decir, los segundos se pueden expresar como fracciones decimales de hasta 3 digitos, que respresentan los milisegundos.

Ahora bien, el dato hora puede expresarse en formatos de 12 y 24 horas, en caso de que el formato en el que pongas la hora sea de 24 horas, es decir, las 13:50 para indicar la 1 una de la tarde con 50 minutos, entonces puedes omitir el indicativo AM|PM, si utilizas formato de hora de 12 horas deberás indicar 01:50 AM para indicar la una de la mañana con 50 minutos y 01:50 PM para indicar la una de la tarde con 50 minutos.

Veamos ahora las curiosidades de este tipo de campo:

Lo primero, puedes omitir la fecha o la hora en un variable DateTime y entonces se tomarán valores por default para "completar" la variable, por ejemplo si omites la fecha, xHarbour pondrá como fecha 1899/12/30, 30 de Diciembre de 1899, esta fecha no está puesta al azar, está definida por Microsoft como la fecha mínima de arranque para los controles OCX/ActiveX, es decir, que este tipo de controles no puede manejar fechas anteriores a la expresada anteriormente.

En caso de omitir la hora, se tomará 00:00:00 es decir, las 12 de la noche o las 0 horas del día indicado en la fecha.

Por otro lado los campos DateTime funcionan igual que un campo fecha, puedes añadir dias, horas, minutos y segundos, obtener diferencia de dias y horas, etc. etc. etc.

Para manejar estos campos en sistemas de bases de datos que lo soporten, debes definir el campo como de tipo "T" (soportado en la mayoría de los sistemas gestores de base de datos basados en SQL y en Advantage Database Server), el campo se puede guardar directamente en el registro sin necesidad de hacer ninguna operación. Por ejemplo si estás usando ADO para acceder a los datos de una tabla SQL desde xHarbour puedes hacer esto:

oRs:Fields("fechahora"):Value := {^ 2008/01/24 21:30}

Así como hay una función DATE() que te devuelve la fecha actual del equipo y una función TIME() que te hace lo mismo pero con la hora, en xHarbour existe la función DATETIME() que hace exactamente lo que estas pensando: devolverte la fecha y la hora del equipo en el formato de campo DateTime, así por ejemplo podrías hacer algo como esto:

dtHoy := DATETIME()

Para conversión cadena de caracteres / fecha y viceversa en Clipper y xHarbour existen los famosos CTOD() y DTOC(), para el tipo de campo DateTime también existen los equivalentes pero solo funcionan en xHarbour: CTOT() y TOTC():

dtFecha := CTOT("2008/01/24 01:50 PM")
cFechaHora := TOTC(DATETIME()) // devolvería: "2008/01/24 21:30"

Uno de los grandes dolores de cabeza del programador de Clipper es que muchos no distinguen la diferencia entre lo que hacen las funciones DTOC() y DTOS() y por eso tienen problemas al generar índices en base a estas funciones, menciono esto porque el mismo problema puede presentarse con los tipos DateTime.

DTOC() lo que hace es regresar la fecha CON EL FORMATO ESTABLECIDO EN SET DATE como una cadena de caracteres, es decir que si yo tengo esto:

SET DATE BRITISH
cFecha1 := DTOC(DATE())
? cFecha 1// "24/01/08"
SET DATE FRENCH
cFecha2 := DTOC(DATE())
? cFecha2 // "24-01-08"
SET DATE ANSI
cFecha3 := DTOC(DATE())
? cFecha3 // "08:01:24"
SET DATE AMERICAN
cFecha4 := DTOC(DATE())
? cFecha4 // "01/24/08"


Obtengo una cadena de caracteres con la fecha FORMATEADA de acuerdo a lo establecido en SET DATE, esto no es válido para un índice porque si en algún programa omitimos establecer el SET DATE al formato que teníamos definido al momento de indexar, entonces puede que nuestro índice muestre resultados erróneos.

La forma correcta de indexar un campo fecha es usando la función DTOS(), que devuelve una cadena de caracteres con la fecha EN EL FORMATO EN QUE ES ALMACENADA EN LA TABLA DBF:

SET DATE BRITISH
cFecha1 := DTOS(DATE())
? cFecha1 // "20080124"
SET DATE ANSI
cFecha2 := DTOS(DATE())
?cFecha2 // "20080124"
SET DATE FORMAT AA-DD-MM
cFecha3 := DTOS(DATE())
?cFecha 3 // "20080124"

No, no es un error, la función DTOS SIEMPRE devuelve la fecha en el formato AAAAMMDD, que es como está almacenada una fecha dentro de un DBF, y lamento informarles a todos los que pensaban que los 8 caracteres de longitud que tiene un campo fecha de un DBF 6 eran para los digitos y 2 para los separadores que no, a muchos les voy a romper el esquema, pero un campo fecha no almacena los separadores, los 8 dígitos son 4 para el año incluyendo siglo, 2 para el mes y 2 mas para el día; en ese orden.

Todo el rollo anterior sirve como antecedente para mencionar que los datos DateTime en xHarbour tienen funciones similares: TTOS() y STOT():

cFechaHora := TTOS(DATETIME()) // devolvería: "20080124212900"
dtFecHor := STOT("20080417231530") // devolvería {^ 2008/04/17 23:15:30}

Y para finalizar, algunos ejemplos interesantes de operaciones con campos DateTime, tomados del manual de xHarbour:

PROCEDURE Main
LOCAL d1, d2, nDiff

SET CENTURY ON
SET TIME FORMAT TO "hh:mm:ss.ccc"

? DateTime() // devuelve: {^ 2008/01/24 21:37:55}
? {ˆ 2007/04/26} // devuelve: 04/26/2007
? {ˆ 05:30:12.345} // devuelve: 12/30/1899 05:30:12.345
? {ˆ 05:30:12.345 PM} // devuelve: 12/30/1999 17:30:12.345

** Valores vacíos

? d1 := {ˆ 0/0/0 } // devuelve: / /
? Empty( d ) // devuelve: .T.

** Operaciones con DateTime

? d1 := {ˆ 2007/04/26 18:30:00 } //devuelve: 04/26/2007 18:30:00.000 ? d2 := StoD("20070426") // devuelve: 04/26/2007
? nDiff := d1-d2, "dias" // devuelve: 0.77 días
? TString( nDiff*86400 ) // devuelve: 18:30:00

** Añadiendo 2 dias a un DateTime
? d1 + 2 // devuelve: 04/28/2007 18:30:00.000

** Añadiendo 2 horas a un DateTime
? d1 + 2/24 // devuelve: 04/26/2007 20:30:00.000

** Añadiendo 2 minutos a un DateTime
? d1 + 2/(24*60) // devuelve: 04/26/2007 18:32:00.000

** Añadiendo 2 segundos a un DateTime
? d1 + 2/(24*3600) // devuelve: 04/26/2007 18:30:02.000

RETURN


Antes de que comiencen a preguntar les respondo: SI, todo es válido para Xailer y para MiniGUI, para FiveWin solo funciona si estás usando xHarbour, no te funcionará con Harbour ya que este tipo de campo es una de las eXtensiones que le dan la "X" a xHarbour.

Por otro lado hasta donde tengo entendido Harbour ahora cuenta con muchas de las extensiones de xHarbour pero ignoro si los tipos de campo DateTime ya han sido implementados, por lo que no garantizo que lo explicado aquí funcione bajo Harbour.

Cambiando un poco de tema, como mencioné a principio de año, estoy remodelando nuestro sitio web www.ciber-tec.com ya he terminado las nuevas páginas de Xailer, he hecho incluso un Mini-Tour explicado detalladamente las características principales del producto, pueden mirar como ha quedado en:

http://www.ciber-tec.com/xailer.htm

Espero que este artículo les sea de utilidad así como toda la nueva información que se estará integrando a nuestra página web en los proximos día.

1 comentario:

Anónimo dijo...

Gracias por compartir esta valiosa información.
Aunque no hay muchos comentarios (soy el primero -y luego de 1 año- :) puedo apostar que tienes muchas visitas de gente que, como yo, buscamos información específica.
GRACIAS y... ¡ mucho ánimo !
José Arturo García Vázquez
México.
2009.02.06