1 de diciembre de 2000

La función SYS(2015) y su inversa

Autor: Luis María Guayán

La función SYS(2015)

En Visual FoxPro, con la función SYS(2015), podemos devolver una cadena "única" de 10 caracteres que puede ser utilizada como nombre de procedimiento, función o archivo. Esta cadena comienza con un guión bajo "_" y continua con una combinación de 9 caracteres de números y letras, y se crea a partir de la fecha y de la hora del sistema. Si llamamos repetidamente a la función SYS(2015) devolverá una cadena de caracteres única por cada intervalo de milisegundos.

El formato de la cadena devuelta por SYS(2015) es el siguiente:

_DDDSSSSSS

en donde:

_: Caracter de guión bajo con el que comienza la cadena.

DDD: Es el número de días transcurridos desde el "01/01/00".La función SYS(2015) tiene dos curiosas caracteristicas:a. No tiene en cuenta la centuria, o sea que solo toma años entre 00 y 99. Toma a todos los años de 367 días, o sea que para:1 de Enero del '00 = 12 de Enero del '00 = 2...1 de Enero del '01 = 368... etc.

SSSSSS: Es el número de segundos (con las milésimas, y sin la coma decimal) pasados desde la medianoche.

Por ejemplo si la función SYS(2015) nos devuelve la cadena "_08G12H0PC", esto corresponde a:

_: Caracter de guión bajo con el que comienza la cadena.

08G: Es el número de días transcurridos desde el "01/01/00" en años de "367 días"

12H0PC: Es el número de segundos pasados desde la medianoche, con las milésimas, y sin la coma decimal.

Pero como vemos, los valores devueltos no están en el sistema decimal (base 10) que todos conocemos. Estos valores están en un sistema de base 36, que toma los 10 dígitos numéricos y las 26 letras del alfabeto, o sea los 36 caracteres incluidos en los intervalos [0..9] y [A..Z].

La inversa de SYS(2015)

Para conocer entonces la inversa de la función SYS(2015), debemos cortar la cadena devuelta, transformar los valores en base 36 a base 10, calcular la fecha según el año y los días del año transcurridos desde el "01/01/xx" - 1, y calcular la hora según los segundos y milésimas devueltos.

Si cortamos y transformamos la cadena devuelta en el ejemplo "_08G12H0PC" al sistema decimal, el resultado es el siguiente:

08G -> 304 días transcurridos desde el "01/01/00"
Año = INT(304 / 367)
Días transcurridos del "Año"= MOD(304, 367)
12H0PC -> 64619,472 segundos -> 17:56:59,472

Con esto sabemos que la función SYS(2015) retornó el valor "_08G12H0PC" el día 30 de Octubre del '00 a las 17:56:59,472 horas.

La función en código VFP

A continuación se muestra el código de la función que retorna la inversa de SYS(2015). La función se llama Inv2015() e invoca a las funciones:

B36TOB10(): Convierte un valor en el sistema numérico de base 36, a un valor en el sistema numérico de base 10.

S2HMSm(): Convierte los segundos y milésimas pasados desde la medianoche a un formato HH:MM:SS,mmm.
*-------------------------------
* FUNCTION Inv2015(tcCadena)
*-------------------------------
* Función que invierte el retorno de SYS(2015)
* USO: Inv2015("_0AB0123AB")
* RETORNA: Caracter "DD/MM/AAAA HH:MM:SS.mmm"
*-------------------------------
FUNCTION Inv2015(tcCadena)
  LOCAL lnDia, lnAnio
  lnDia = B36TOB10(SUBS(tcCadena, 2, 3))
  lnAnio = INT(lnDia / 367)
  lnDia = MOD(lnDia, 367)
  SET STRICTDATE TO 0
  RETURN DTOC(EVAL("{^"+STR(lnAnio,2)+"/01/01}") ;
    + lnDia - 1) + " " ;
    + S2HMSm(B36TOB10(SUBS(tcCadena, 5, 6))/1000)
ENDFUNC

*-------------------------------
* FUNCTION B36TOB10(tcN36)
*-------------------------------
* Función que pasa un número de base 36 a base 10
* Usada por Inv2015()
* RETORNA: Numérico
*-------------------------------
FUNCTION B36TOB10(tcN36)
  LOCAL lnN10, lcChr, lnLen, lnI, lnAux
  tcN36 = ALLTRIM(UPPER(tcN36))
  lnLen = LEN(tcN36)

  lnN10 = 0
  lnI = 1
  FOR lnI = 1 TO lnLen
    lcChr = SUBS(tcN36, lnI, 1)
    lnAux = ASC(lcChr)-IIF(lcChr < 'A', 48, 55)
    lnN10 = lnN10 + lnAux * 36^(lnLen-lnI)
  ENDFOR
  RETURN INT(lnN10)
ENDFUNC

*-------------------------------
* FUNCTION S2HMSm(tnSeg)
*-------------------------------
* Transforma segundos a formato HH:MM:SS.mmm
* Usada por Inv2015()
* RETORNA: Caracter 'HH:MM:SS.mmm'
*-------------------------------
FUNCTION S2HMSm(tnSeg)
  LOCAL lnHor, lnMin, lnSeg, lnMil
  lnHor = INT(tnSeg/3600)
  lnMin = INT(((tnSeg - (lnHor*3600))/60))
  lnSeg = MOD(tnSeg, 60)
  lnMil = (tnSeg - INT(tnSeg))*1000
  RETURN TRANSFORM(lnHor, "@L 99") + ":" + ;
    TRANSFORM(lnMin, "@L 99") + ":" + ;
    TRANSFORM(lnSeg, "@L 99") + "." + ;
    TRANSFORM(lnMil, "@L 999")
ENDFUNC

*-------------------------------
Notas finales

Conocer la inversa de la función SYS(2015), nos puede ser útil cuando guardamos la cadena retornada en un campo de una tabla al grabar o modificar un registro, o para saber cuando se creó un archivo que almacenamos con el nombre devuelto con SYS(2015).

El único problema de conocer la inversa de la función SYS(2015) es que no conocemos la centuria. La función Inv2015() tomará los valores actuales de SET CENTURY y ROLLOVER de VFP, para evaluar por ejemplo: {^00/01/01} ó {^99/01/01}.

2 comentarios :

  1. Estimado,

    Se que es bastante antigua esta entrada, pero necesitaba saber como funciona la funcion SYS(2015) para poder replicarla en PHP.

    Les comparto la solución por si alguien la llegara a necesitar. Saludos.

    public function sys2015_VisualFox()
    {
    $inicio = new \DateTime(date('Y').'-01-01');
    $diasDesdeInicio = $inicio->diff(new \DateTime())->format('%a')+1;
    $diasTotales = $diasDesdeInicio+ (date('y')*367);
    $diasTotalesBase36 = base_convert($diasTotales, 10, 36);

    $horaMilisec = date('H')*3600000;
    $minMilisec = date('i')*60000;
    $secMilisec = date('s')*1000;
    $milisec = floor(gettimeofday()['usec']/1000);

    $horaMilisegundos = $horaMilisec+$minMilisec+$secMilisec+$milisec;
    $horaBase36 = sprintf('%06s',base_convert($horaMilisegundos, 10, 36));
    $sys2015 = '_'.$diasTotalesBase36.$horaBase36;
    return $sys2015;
    }

    ResponderEliminar
  2. Luis Maria se podrá crear una función Sys(2015) para MySQL?

    ResponderEliminar