Visual Basic y el año 2000

o como manejan las distintas versiones del VB los años especificados en dos cifras

 

Fecha: 19/Feb/1998 (uso las cuatro cifras para que dentro de cien años o más sepan la fecha correcta...)



Se que no voy a descubrir la rueda, pero... creo que tengo que tocar el tema... por si aún no te has enterado de los problemas con los que te puedes encontrar.

No creas que voy a darte la tabarra en esto... voy a ir al grano, ya sabes que las florituras no van conmigo, así que... directo al grano... y al que le salpique... que se limpie... 8-)

Antes de nada, decir que la "parte técnica", (al menos en lo que se refiere a cómo funciona el OLEAUT32.DLL), de esta información la he sacado de la revista Developer Network Journal, que tampoco me gusta apuntarme "medallas" que no son mías... Lo que si es mío, son las pruebas que he hecho y el posible código que encuentres en este "artículo"... lo de posible, es que, para variar, he empezado a escribir antes de "preparar" nada, así que si al final resulta que no hay código, ya sabes porqué... ¿vale?

Una vez dicho todo esto, algunos me dicen que parezco el abuelo de la familia Cebolleta, un comic de F.Ibañez y el abuelo siempre está contando batallitas... A lo que iba, que después de este rollo, viene el quid de la cuestión...


El tema en cuestión es el manejo de los años como dos cifras, lo que voy a "explicarte" aquí es cómo lo maneja el VB, realmente las distintas versiones del VB, ya que hay diferencias, según sea el VB de 16 bits y el de 32 bits, incluso en éstos últimos también varía según la versión de la DLL que se encarga de este tema: OLEAUT32.DLL

Según dicen... el VB3 cuando se encuentra con un año de dos cifras, automáticamente lo interpreta como que es del siglo 20, es decir un año desde el 1900 al 1999 (realmente el 1900 es parte del siglo 19, pero...) y el VB4-16 lo interpretará como un año del siglo actual... es decir que el año 21 será 2021 en el siglo que viene y en este siglo, es el año 1921.
Esto lo puedes comprobar fácilmente si cambias el reloj del sistema y pruebas este código: (al final si que había código)

    Dim mDate As Date
    
    mDate = DateSerial(21, 1, 1) '1 de enero del 21
    
    Label1 = Format$(mDate, "dd/mmm/yyyy")
    
    

Este pequeño código te mostrará 01/Ene/1921 si estás probándolo con una fecha anterior al año 2000

Si la fecha la cambias a cualquier año posterior a 1999, mostrará: 01/Ene/2021

El problema para poder hacer otros cálculos es que el Win95 no soporta años anteriores al 1980 ni posteriores al 2099, así que no sé lo que ocurrirá en el año 3000, pero creo que "yo" no debo preocuparme de eso...

Con el VB3, siempre mostrará el 1/Jan/1921, con lo que se confirma que es cierto lo que dije antes...
Este es el código para probarlo en el VB3, fíjate que uso Variant, ya que el VB3 no dispone del tipo Date.

    Dim mdate As Variant

    mdate = DateSerial(21, 1, 1) 'En VB3, siempre será el 1 de enero de 1921

    label1.Caption = Format$(mdate, "dd/mmm/yyyy")

Con el VB4 de 32 bits, lo mismo que con el VB5, el tema se complica un poco, ya que, las fechas se calculan dependiendo de como esté implementado el tema en OLEAUT32.DLL.
La versión que he usado es la 2.20.4122 y con esta versión he obtenido los siguiente resultados, según quisiera ver los años que indico al lado:
Para las pruebas he creado esta función que recibe el año como parámetro y devuelve la cadena ya formateada:

Nota: Como el año a probar debe ser de sólo dos cifras, es decir del 0 al 99, me aseguro que no haya algún "listillo" que quiera hacer trampas...

Private Function LaFecha(ByVal unAño As Integer) As String
    'Devuelve la fecha formateada según el año recibido
    'el año deberá ser un valor entre 0 y 99
    
    Dim mDate As Date
    
    'Con esto nos aseguramos recibir sólo dos cifras
    unAño = Val(Right$("  " & Str$(unAño), 2))
    
    mDate = DateSerial(unAño, 1, 1) '1 de enero del año "unAño"
    
    LaFecha = Format$(mDate, "dd/mmm/yyyy")

End Function

Para comprobar los distintos años, se usaría un código como este:

    'Probar el año 98
    Label1 = LaFecha(98)

Por supuesto que el número a indicar será el que queramos probar, en mis "pruebas" he usado los años 21, 31, 50 y 98.

Estos son los resultados para VB4 de 32 bits:

Año Si la fecha está en 1980-1999 Si la fecha está en el 2000-2099
21 01/ene/1921 01/ene/2021
31 01/ene/1931 01/ene/2031
50 01/ene/1950 01/ene/2050
98 01/ene/1998 01/ene/2098

Con esto se deduce, que si la versión que tienes de OLEAUT32.DLL es igual que la mía, el VB4 de 32 bits convierte las dos cifras al año que esté en el siglo de la fecha del sistema.
Recuerda que esto del siglo no es cierto, ya que "técnicamente" hablando el siglo 21 empieza el 1 de enero del 2001.

 

Sin embargo, las mismas pruebas con VB5, dan estos resultados:

Año Sea el año que sea
21 01/ene/2021
31 01/ene/1931
50 01/ene/1950
98 01/ene/1998

Es decir que el VB5 interpreta las dos cifras como queriendo compatibilizar a los "desastrosos" del siglo 20... que es cuando se inventó esto de los ordenadores y esas cosas y que hemos "dado por hecho" que dos cifras tienen que ser para este siglo... salvo los años anteriores al 1930 que ya lo "manda" al año 2000.

Como te comentaba, aunque no lo he podido comprobar por no disponer de esas librerías, si la versión de OLEAUT32.DLL es la 2.20.4044 o anterior, el VB5 (y en teoría el VB4-32) convierte las dos cifras en el siglo actual. A partir de esa versión, según la revista comentada sería a partir de la versión 2.20.4049, el comportamiento es como el del ejemplo, es decir, si se usan dos cifras, el rango de años va desde el 1930 al 2029. Cosa que he comprobado pero que sólo ocurre en el VB5 y no en el VB4-32 como "asegura" la revista...

 

Usando CDate

Estas pruebas y resultados son las realizadas con la función DateSerial, pero con CDate la cosa cambia un poco, al menos en lo que al VB4-32 se refiere, ya que actúa como el VB5, es decir el rango de años va del 1930 al 2029.

Las pruebas en los otros VBs siguen funcionando igual que con DateSerial.

Este es el listado de la función que he usado para hacer el cálculo con CDate:

Private Function LaFechaCDate(ByVal unAño As Integer) As String
    'Devuelve la fecha formateada según el año recibido
    'el año deberá ser un valor entre 0 y 99
    
    Dim mDate As Date
    Dim sDate As String
    
    'Con esto nos aseguramos recibir sólo dos cifras
    unAño = Val(Right$("  " & Str$(unAño), 2))
    'Convertirla en cadena
    sDate = "01/Ene/" & CStr(unAño)
    mDate = CDate(sDate)
    
    LaFechaCDate = Format$(mDate, "dd/mmm/yyyy")

End Function

En VB3 hay que modificar un poco el listado, pero no tiene complicación, pero hay que sustituir CDate por CVDate. Y como el nombre del mes no lo entiende en castellano, pues hay que decirle que es Jan en lugar de Ene, aquí tienes el listado de las dos funciones en VB3:

Private Function LaFecha (ByVal unAño As Integer) As String
    'Devuelve la fecha formateada según el año recibido
    'el año deberá ser un valor entre 0 y 99
    
    Dim mDate As Variant
    
    'Con esto nos aseguramos recibir sólo dos cifras
    unAño = Val(Right$("  " & Str$(unAño), 2))
    
    mDate = DateSerial(unAño, 1, 1) '1 de enero del año "unAño"
    
    LaFecha = Format$(mDate, "dd/mmm/yyyy")

End Function


Private Function LaFechaCDate (ByVal unAño As Integer) As String
    'Devuelve la fecha formateada según el año recibido
    'el año deberá ser un valor entre 0 y 99
    
    Dim mDate As Variant
    Dim sDate As String
    
    'Con esto nos aseguramos recibir sólo dos cifras
    unAño = Val(Right$("  " & Str$(unAño), 2))
    sDate = "01/Jan/" & CStr(unAño)  '1 de enero del año "unAño"
    mDate = CVDate(sDate)
    
    LaFechaCDate = Format$(mDate, "dd/mmm/yyyy")

End Function

Para probarlo, crea un form con un TextBox, un CommandButton y como mínimo dos Labels y en el Command1_Click escribe esto:

    'Probar el año indicado
    Label1 = LaFecha(Text1.Text)
    Label2 = LaFechaCDate(Text1.Text)

 

Espero que con toda esta "retahila" te hayas aclarado un poco y que sepas a que te enfrentas si no manejas las fechas de forma adecuada cuando llegue el año 2000, por suerte, el día 1 es domingo y te daría tiempo a cambiar el código... je, je...

 

El resumen

Para resumir lo que debes tener en cuenta es que al tratar los años sólo con dos cifras, no debes dar nada "por hecho", es decir, no asumas que se comportará como se comporta ahora.
Al menos si los años los has guardado como una cadena de texto o un valor numérico que no es "compatible" con el formato de fecha que usa el VB.
Porque una cosa es como muestres las fechas y otra muy distinta y la que realmente "crea" estos problemas es la forma de almacenarla y la forma en que "filtras" los años, ya que es muy corriente que el formato de la fecha se introduzca con dos cifras para el año...

Si tienes casos en los que usas el MaskEdit o cualquier otro "filtro" de fechas, cuando estés en el año 2000 (o posterior), deberías sumarle 2000 a las dos cifras introducidas, de esa forma "guardarás" el año de la forma adecuada.
Para conseguir esto, podrías crearte una función simple que haga este trabajo y que compruebe "la centuria" en la que estás trabajando, sumándole un múltiplo de 100, según las dos primeras cifras del año actual:

Private Function AjustarAño(ByVal unAño As Integer) As Integer
    Dim elSiglo As Integer
    
    elSiglo = Val(Right$(Format$(Now, "dd/mm/yyyy"), 4))
    
    AjustarAño = Fix(elSiglo \ 100) * 100 + unAño
End Function

Fijate que me he complicado un poco con el cálculo del "siglo", es porque al hacer esto:

    elSiglo = Val(Left$(Format$(Now, "yyyy"), 2))
    
    AjustarAño = elSiglo * 100 + unAño

Lo que ocurre es que no se muestra el siglo de forma correcta, al menos en el VB5.
Así que con el código que está en la función todo marcha como debe.

Para usar esta función sólo tendrás que hacer esto:

Label4 = AjustarAño(Text1.Text)

Y obtendrás el año en cuatro cifras, siempre dependiendo del "siglo" actual y desechando cualquier cálculo que el Visual Basic pueda hacer.

También puedes usar esta función para que el VB calcule correctamente el año según "la centuria" actual.

unaFecha = Format$(DateSerial(AjustarAño(elAñoEn2Cifras), elMes, elDia), "dd/mm/yyyy")

En este caso, siempre obtendrás una fecha "válida", ya que el DateSerial sólo se "lía" si el año lo especificas en dos cifras.

Todo esto sólo debería ser válido para las "nuevas" fechas introducidas.
La forma de manipular cualquier otra fecha almacenada en una base de datos o fichero, correrá por tu cuenta... normal, no pretenderás que te lo solucione yo... ¡hum!

Así que... cúrate en salud y guarda siempre las fechas en campos Date o bien con las cuatro cifras del año...

 

Despedida y cierre:

Bueno, creo que ya es suficiente, espero que te haya quedado claro cual es el problema este del año 2000, al menos en lo que se refiere al VB.

 

Nos vemos.
Guillermo

P.S.
Te recomiendo que le eches un vistazo al artículo publicado por Harvey Triana: "
Visual Basic en la Crisis Informática del 2000"

Nota del 01/Nov/2010:
El articulo de Harvey estaba originalmente en:
http://www.geocities.com/SiliconValley/Horizon/5305/vb2000.htm
pero ya no esta allí, haciendo una búsqueda me lo encontré aquí:
http://vexpert.mvps.org/articles/vb2000.htm
Espero que siga ahí durante mucho tiempo... y si no, que yo me de cuenta de que lo ha cambiado... ;-)))


la Luna del Guille o... el Guille que está en la Luna... tanto monta...