Creando Calculadoras del Tiempo #3 - El programa principal ("Intant-Date")

  1. Creando Calculadoras del Tiempo #1 - Una breve introducción al proyecto
  2. Creando Calculadores del Tiempo #2 - El funcionamiento de VALID.py

Continuando con la
creación de nuestra calculadora de tiempo, tras haber hablado brevemente de
nuestro modulo “VALID.py”  del cual vamos
a aprovechar tres de sus funciones, ha llegado el momento de empezar a crear el
programa principal, al cual he dado el nombre de “intant-Date”, ya que su
misión va a ser la de calcular la distancia, en horas minutos y segundos,
existente entre dos horas (o instantes de tiempo).

He de hacer mención
del hecho de que nuestra calculadora puede calcular distancias, tanto entre
instantes ubicados en fechas distintas, como entre instantes pertenecientes a
un mismo día. Esta consideración tiene su importancia, debido a que, en función
de ella, he estructurado el código del programa en dos grandes partes: La
primera de ellas, es la destinada a generar la distancia entre instantes
pertenecientes a un mismo día, y la segunda de ellas (algo más compleja) la
destinada a calcular distancia entre instantes pertenecientes a fechas
diferentes (tanto del pasado como del futuro).

Atendiendo a dicha
diferencia, voy a dividir la exposición del desarrollo del programa en esas dos
partes. De ese modo en este artículo me voy a ceñir a explicar el bloque que
calcula la distancia entre horas de un mismo día.

Por motivos obvios,
para poder crear nuestro programa, debemos empezar por importar aquellos
módulos que nos permitirán hacer operaciones con fechas y que vienen, ya,
instalados en python.


Como podemos ver,
el modulo “datetime” va a tener una
importancia capital para el desarrollo de nuestro programa. En cuanto a los
módulos “time” y “subprocess” no son estrictamente
necesarios para la realización de los cálculos que vamos a realizar con las
fechas, pero tienen una utilidad centrada en aspectos relacionados con la
comodidad a la hora de utilizar el programa. En lo concerniente al modulo
“VALID” del que hablé en el artículo anterior, lo usaremos para importar las
funciones, “OKI”, “ER” y “ns”.

Por motivos
evidentes, cuando se trabaja con un programa que realiza cálculos a partir de
unos datos introducidos por el usuario, una de las primeras cosas de las que
nos tenemos que ocupar es de proporcionar a dicho usuario del programa de una
vía a través de la cual introducir los datos que, una vez procesados por el
ordenador, nos proporcionará el resultado.

De ese modo como lo
que, en este caso, lo que queremos es calcular la distancia temporal entre dos
acontecimientos, lo primero que tenemos que hacer, es pedir a nuestro usuario
que introduzca los datos numéricos, correspondientes al año, mes, día, hora, minuto
y segundo, correspondientes a cada una de los dos instantes de tiempo entre los
cuales queremos medir la distancia.

Esto lo haremos,
básicamente, mediante la función “input”,
del siguiente modo:


En este código
podemos ver el modo en el que vamos a pedir al usuario que introduzca los datos
correspondientes al primer suceso, datos que empezarán a ser pedidos 1 segundo
después de visualizar una breve explicación del propósito de nuestro programa
(la función “time.split()”) es la
que se va encargar de interrumpir el funcionamiento durante el numero de
segundos indicado entre paréntesis (en este caso 1 segundo). El modo en que
vamos a querer que se introduzcan los datos, es mediante cifras numéricas,
incluidos los meses los cuales serán representados por una escala numérica en
donde al mes de Enero le corresponderá el numero 1, a Febrero el número 2 y así
hasta completar los 12 meses del año.

Ya que los datos
que vamos a usar son numéricos, debemos asegurarnos de que nuestro usuario solo
pueda introducir este tipo de datos. Pero, a su vez, dentro de los datos
numéricos, necesitamos que estos sean números enteros. Es por ello que en todos
los inputs vamos a aplicar la función “OKI” que como recordaremos, era la
encargada de asegurarse de que solo se introdujeran números enteros (sin parte
decimal).

Pero, a la vez,
debemos recordar que estos números van a ser tratados con funciones encargadas
de gestionar fechas. Esto hace que para admitir un input, no nos baste solo con
nuestra función “OKI”, sino que necesitamos que estos datos sean coherentes con
el calendario que internamente utilizan nuestras computadoras, necesidad que
vamos a ir viendo una a una.


En el primer caso
(el de la variable a la que hemos denominado “año1”), necesitamos que el
usuario introduzca el año de partida. Por lo tanto debemos asegurarnos no solo
de que se trate de un número entero, sino también que se corresponda con un año
valido. Cuando hacemos referencia a un año valido, lo que estamos diciendo es
que las computadoras trabajan con un calendario en el que los años están
comprendidos entre el año 1 y el año 9999. De modo que si quisiéramos trabajar
con un año que estuviera fuera de ese rango, el programa daría,
irremediablemente un error, lo cual es debido a que los años que están fuera de
ese rango sencillamente no existen para la computadora, y por lo tanto no se
puede trabajar con ellos.

Así, para nuestra
variable “año1”, necesitamos un número que exprese un año comprendido entre 1 y
9999 (ambos inclusive). Es por ello que al input le aplicaremos dos funciones
que actuarán como filtros: La primera función será “OKI” que se “asegurará” de
que el dato introducido es un entero. A continuación sobre el resultado
devuelto por “OKI” aplicaremos una función que excluya los números fuera del
rango (1,9999). Dicha función tendrá que haber sido establecido con
anterioridad y que sería la siguiente:


Nuestra segunda
función de comprobación (a la que he dado el nombre de “año_valido”) lo que hace es que si el año introducido (representado
aquí por la letra “a”) es menor que 0 (“a<0”) o es superior a 9999
(“a>9999”), la función volverá a aplicarse íntegramente sobre el dato que
introduzcamos tras leer el mensaje “El año no puede ser menor de 0 ni mayor de
9999:”. Esta operación se repetirá siempre, mientras que introduzcamos un dato
inadecuado.


Como se puede ver
en la imagen, mientras que hemos introducido un año fuera del rango admitido
nos ha aparecido el mismo mensaje de error, y solo cuando hemos puesto un año
dentro del rango (en el ejemplo, el año 1990) El programa ha podido avanzar
para pedirnos el mes.

Para el caso del
mes (cuyo dato almacenaremos en la variable “mes1”) tenemos que aplicar la
misma fórmula que veíamos con los años.


La diferencia
estriba en que en este caso el rango de números validos es (por motivos obvios)
de 1  a 12. Para ello tenemos que
establecer con anterioridad otra función, a la que daremos el nombre de
“mes_valido” y tendrá esta sintaxis:


En esta ocasión
hemos utilizado lo que se conoce como un ciclo “while”, el cual consiste en la repetición de una instrucción o una
serie de instrucciones de modo cíclico, mientras (de ahí su nombre) que se dé
una condición (o una serie de condiciones). De ese modo en este ejemplo tenemos
que mientras la variable “mes” (que toma como argumento inicial el input
introducido para “mes1”) sea menor de 1 (mes<1) o sea mayor de 12
(mes>12), el programa nos seguirá pidiendo la introducción de un número de
mes válido mediante el mensaje “Mes no válido”, de modo, que solo cuando se
introduzca un número comprendido entre 1 y 12 (ambos incluidos) podremos pasar
a introducir el número del día.


Así, tal y como se
aprecia en la imagen, tras haber introducido un año valido, hemos cometido el
“error” de introducir la palabra “hola” en el lugar reservado al mes. En este
caso nuestro input no ha conseguido pasar el primer filtro (el correspondiente
a nuestra función “OKI”) visualizándose el mensaje “Caracter no válido” (lo
mismo nos ha sucedido al dar a “intro” sin escribir nada). En el tercer intento
hemos introducido un número fuera de rango (13). En este caso el input ha
pasado con éxito el primer filtro (“OKI”) pero no el segundo (“mes_valido”),
apareciéndonos el mensaje “Mes no válido”. Finalmente (y tras “equivocarnos”
una vez más escribiendo “hh”) Hemos introducido un input que cumple los dos
requerimientos que necesitamos para admitirlo (que sea número entero y que este
entre 1 y 12) con lo que el programa, por fin, nos ha permitido pasar a
introducir el día.

Pasemos ahora
a  la variable “dia1”:


Para la
determinación del día, vamos a tener que crear una variable (“dia_valido”),
para cuya creación deberemos tener en cuenta, el hecho de que (a diferencia de
lo que sucedía con “año_valido” y “mes_valido”) el rango de días validos no es
constante, lo cual es debido al hecho de que hay meses que tienen 30 días,
otros tienen 31 y el de febrero tiene 28 (29 en los años bisiestos). Por ello
en nuestra función deberemos utilizar un método que, en función del año y mes
introducidos, nos diga si el número de día se encuentra dentro del rango de
días valido para ese mes y año.

Para ello
estructuraremos nuestra función en dos partes, la primera será la destinada a
determinar el número de días que tiene el mes del que se trate (tomando como
referencia el año) y la segunda, la destinada a valorar si el número
introducido se encuentra en los límites marcados por el número de días de ese
mes.

Es por ello, que la
función que emplearemos tendrá que ser algo más compleja que las vistas hasta
el momento. Dicha función podría ser la siguiente:


Para el
funcionamiento de esta función, vemos que va ser de gran importancia, función “date” del modulo “datetime”, la cual nos va a permitir establecer la distancia (que
se puede expresar en días mediante “.days”)
entre dos fechas y cuyo funcionamiento vamos a ilustrar con un sencillo
ejemplo:


En la foto, tenemos
un caso en el que nos hemos propuesto conocer el número de días que tuvo el mes
de Febrero del año 2016 (año que fue bisiesto). Para ello lo primero que debemos
hacer es importar desde el modulo “datetime”,
la función “date”, para a
continuación establecer las fechas entre las cuales queremos calcular la
distancia (las llamaremos “D1” y “D2”). Cada uno de los días será definido tal
y como se aprecia en la imagen (estableciendo de izquierda a derecha, primero
el año, en segundo lugar, el mes y finalmente el día). De ese modo habremos
hallado el número de días existente entre el 1 de Febrero de 2016 y el 1 de
Marzo de ese mismo año.

Volvamos ahora a
nuestra función “dia_valido” en la que aplicaremos la técnica vista
anteriormente, para hallar el número de días que tendrá el mes en el que vamos
a ubicar nuestro “dia1”.


Tras declarar la
función, lo que hacemos a continuación es definir el primer día del mes en el
que se encuentre nuestro dia1: Dicha variable vendrá definida por el “año1”,
por el “mes1” (establecidos con anterioridad), para el caso del día pondremos
el 1 (ya que partimos, en este caso del primer día del mes de que se trate).
Este primer día del mes se almacenará en la variable “d1”. 

 

Acto seguido
procederemos a establecer el que va a ser la fecha correspondiente a un mes más
tarde (a la que daremos el nombre de “d2”). Para ello tenemos que tener en
cuenta dos posibilidades. La primera de ellas (y la más común) es aquella en la
que la fecha correspondiente a un mes más tarde, se obtiene sumándole 1 al mes
correspondiente y dejando todo lo demás igual, tal y come se aprecia a
continuación:


La segunda de las
posibilidades referidas es la de que el mes en el que no sestemos moviendo sea
el de Diciembre. En este caso no podemos emplear el sistema anterior ya que de
ese modo al sumarle 1 al mes, nos daría 13, produciéndose el correspondiente
error. Para paliar este posibilidad debemos establecer que en el caso de que el
mes sea Diciembre (“if mes==12:”) el método para definir “d2” sea el siguiente:


Así, para el caso
del mes de Diciembre, la fecha correspondiente al primer día del mes siguiente
será equivalente a sumar 1 al año y establecer el 1 tanto para el mes como para
el día.

Una vez obtenido
mediante el proceso anterior, el primer día del mes y el primero del mes
siguiente, ya solo tenemos que calcular la diferencia entre ambos para obtener
el número de días que tiene nuestro mes, esta diferencia la almacenaremos en la
variable “diferencia”:


Así, una vez que
hemos calculado la distancia entre el primer día de nuestro mes y el primer día
del mes siguiente no tenemos que hacer más que asegurarnos de que el número de
día introducido por el usuario no es superior a dicha distancia (expresada en
días, mediante “.days”) ni inferior a 1:

De modo que
mientras que el “dia” sea superior a la diferencia antes calculada o inferior a
1, nos aparecerá un texto con el mensaje “Número de día no valido:”.

A continuación es
el momento de definir la variable que almacenará la hora de nuestro “primer
suceso” la cual declararemos del modo siguiente:

Para este caso
hemos asociado nuestra variable “hora1” a una función (“hora_valida”) la cual,
como se puede apreciar, no toma ningún argumento de partida. Ello es debido a
que en este caso vamos a hacer que sea la propia función la que nos pida el
dato a introducir, para posteriormente “decidir” si dicho dato es válido. La
mencionada función, la cual habrá sido declarada con anterioridad, que pasamos
a explicar a continuación es la siguiente:


Tenemos aquí, una
función en la que vamos a emplear un método algo distinto del empleado en
ocasiones anteriores, en el cual pedimos la introducción de una dato (que como
ya viene siendo habitual) hacemos pasar en primer lugar por el filtro de la
función “OKI” (para asegurarnos de que sea un número entero) pero el cual se
enmarca en un “ciclo while”, el cual
solo se “romperá” (con “break”) para
el caso en el que no se de la condición (no deseada) de “h” sea menor que 0
(h<0) o mayor o igual que 24 (h>=24), (aunque también es cierto que en
este caso habría bastado con poner que “h” fuera mayor que 23 (h>23)). Por
el contrario si se da la condición no deseada la función dará un salto (con “pass”) para comenzar de nuevo desde el
principio con la petición de introducción de hora. Una vez completado el
proceso la función devolverá el resultado (almacenado en “h”), con “return”.

Finalmente, vamos a
terminar con la introducción de datos con la definición de las dos últimas
variables que nos faltan para establecer nuestra fecha y hora de partida, (“minuto1”
y “segundo1”).



Estas dos funciones
las presentamos conjuntamente, porque ambas van a utilizar la misma función (a
la que hemos dado el nombre de “mn_valido”). El motivo de compartir función
está en el hecho de que tanto para los minutos como para los segundos, el rango
de valores válidos va a ser el mismo (entre 0 y 59). Sin embargo, a la hora de
pedir la introducción del dato hemos de mostrar un texto acorde con el dato del
que se trate en ese momento. De modo que la función nos muestre el mensaje
“Introduce minuto:” en el caso de que la función se esté aplicando a la
variable “minuto1” y muestre “Introduce segundo:” en el caso de que se esté
aplicando a la variable “segundo1”.

Dicho método para
hacer que nuestra función “sepa” que es lo que nos tiene que pedir en cada
momento, es el de incluir un “string
formado por un único carácter (“m” para el caso de los minutos y “s” para el caso
de los segundos), como argumento entre los paréntesis, tal y como se aprecia en
la imagen de arriba.

Con dichos
argumentos ya estamos listos para aplicar nuestra función la cual sería la
siguiente:


Como se puede
apreciar, hemos descrito una función, basada en un “ciclo while” el cual
solo se “romperá” (con “break”), en
el caso de que el dato introducido no sea menor de 0 (mn<0) ni mayor o igual
a 60 (mn>=60), (en este caso, otra vez nos habría bastado con “mn>59”).
La única diferencia importante con respecto a la función que usamos para las
horas, es que, en este caso, a la horade pedir el dato, el texto variará en
función de si el argumento (“n”) es el string “m” o de si no lo es (por ser
“s”).

Hasta aquí hemos
visto un método para introducir los datos que definirán el primero de los
“instantes” de tiempo entre los que queremos calcular la distancia en horas,
minutos y segundos. Ahora lo que toca hacer es definir las variables que
compondrán el segundo “instante” de tiempo (compuesto por las variables “año2”,
“mes2”, “dia2”, “hora2”, “minuto2” y “segundo2”) y cuya explicación vamos a
omitir debido al hecho de que el proceso es exactamente el mismo (y utilizando
las mismas funciones) que el que hemos visto para el primer instante.

Y con estas dos
variables hemos terminado con la parte correspondiente a la introducción de
datos y la definición de las funciones que se encargarán de “asegurarse” de que
nuestros “inputs” sean correctos (de tal modo que la computadora pueda trabajar
con ellos sin que se produzca ningún error).

Así, una vez que
tenemos los datos correspondientes a los instantes entre los que queremos
calcular la distancia, ya estamos en condiciones de empezar a trabajar con
tales datos.

Tal y como dije
anteriormente en este articulo vamos a realizar el cálculo de la distancia
entre dos fechas ubicadas en un mismo dia (por lo que, de momento, no vamos a
necesitar los datos correspondientes al año, mes y día). Por ello va a ser la
diferencia entre la hora del primer suceso y la hora del segundo suceso, la
base principal sobre la que vamos a trabajar.

De ese modo, lo
primero que tenemos que hacer es especificarle al programa, en que supuesto
vamos a trabajar solo con horas, minutos y segundos (que como ya hemos
señalado, es en el caso de que los datos correspondientes al año, mes y día,
coincidan para ambos  sucesos. De ese
modo, el código que calculará la distancia en estos casos podría ser como el
que se muestra a continuación:

Así, lo primero que
hemos hecho, ha sido decir que en caso de que “año1”, “mes1”, y “dia1”, sean
iguales a “año2”, “mes2” y “dia2”, respectivamente, declararemos una variable
con el nombre “difer_horas”, en el que se almacenará la diferencia entre la
“hora1” y la “hora2” (“abs(hora2-hora1)”). De ese modo obtenemos una diferencia
entre horas de tipo solo provisional (ahora explicaré el  porqué). Sin embargo este sistema tiene un
problema, y es que considera que la hora inicial, es una hora completa y que la
hora final no se ha de contar. Este problema se ve más claro si tenemos en
cuenta que no es lo mismo establecer como hora inicial las 4:00 que establecer
como hora inicial las 4:30, ya que en el primer caso tendremos que contar la
primera hora de modo completo, mientras que en el segundo caso habremos de
contabilizar solo la mitad de dicha primera hora. Algo similar sucede respecto
del segundo instante, ya que no es lo mismo establecer como hora final, las
7:00 que establecer las 7:15 (ya que en este segundo caso tendríamos que contar
también los 15 minutos que exceden de la segunda hora.

Es por ello que
tendremos que corregir tal imprecisión con una sencilla operación matemática,
la cual empezaremos aplicando para calcular los “Minutos_totales”.



Calculamos, así los
“Minutos_totales”, multiplicando (como es evidente) los 60 minutos que tiene la
hora, por la cantidad de horas transcurridas (pero, todavía, sin tener en
cuenta la precisión que comentábamos en el párrafo anterior), a la que
aplicamos la corrección, que en función de lo que comentamos en el párrafo
anterior, teníamos que hacer. De modo que a la diferencia en horas, hemos de
restarle el “minuto1” (porque en ese caso la hora primera no se ha de contar
íntegramente) y, a su vez, al resultado de esta operación hemos de sumarle el
“minuto2” (por que representa, en minutos, el tiempo que excede a nuestra
diferencia en horas, inicial).

La misma técnica
emplearemos para calcular los “Segundos_totales”, con la salvedad que en este
caso, usaremos como base los “Minutos_totales” calculados con anterioridad,
sobre cuyo resultado aplicaremos, de nuevo, la corrección que aplicamos en el
caso anterior.

Ahora, si, estamos
en condiciones de calcular con un grado aceptable de precisión la distancia, en
horas, entre nuestros dos sucesos. Para ello inicialmente hemos realizado el
cálculo final, de las horas, partiendo, como base del resultado obtenido en
“Minutos_totales”, así tenemos:


Así, dado que una
hora consta de 60 minutos, lo que hemos hecho, ha sido calcular, cuantas veces
se cubren esos 60 minutos (o lo que es lo mismo, cuantas horas), dentro de
dicha cifra total de minutos.

Haciendo uso de
estos cálculos, veamos ahora, a nuestro programa en acción:


Como se ve, hemos
calculado el tiempo que media entre 1:30:00 y las 4:00:02 del 1 de Enero de
2018, en donde el número de horas totales es de 2,5 debido a que descontamos
los 30 primeros minutos.

Sin embargo nuestro
resultado en horas, aún, adolece de cierto grado de imprecisión. Eso es debido
a que la unidad de tiempo que empleamos para calcular las horas totales, es el
minuto. Pero si empleamos el segundo como unidad básica para el cálculo de las
horas, empleando el siguiente procedimiento, podremos solventar dicha
imprecisión:


De modo que, una
vez introducida esta modificación en nuestro código, si volvemos a ejecuta nuestro
programa, introduciendo los mismos datos que en el ejemplo anterior, obtenemos:


Como se puede ver,
el resultado referido a los minutos y a los segundos totales es exactamente el
mismo que obteníamos antes de realizar el cambio. La diferencia estriba en el
resultado que, ahora, hemos obtenido para las horas totales, cambio consistente
en la obtención de una mayor precisión en el resultado final.

Hasta aquí, la
explicación del modo de calcular el tiempo entre horas, dentro de un mismo día.
En el próximo articulo abordaremos la segunda parte de este programa, que es la
destinada a calcular la distancia entre sucesos acaecidos en días distintos.

El código de los
programas referidos en este artículo puede verse, en mi perfil de GitHub, en la
siguiente dirección: https://github.com/antonioam82/Timer.

Articulo escrito por Antonio Alfonso Martinez.
  1. Alex dice:

    Alguien puede pasar el archivo .py??

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir
White Monkey