Google+ Seguidores

viernes, 12 de enero de 2018

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




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.

No hay comentarios :
Write comentarios

Tu comentario es importante y nos motiva a seguir escribiendo...

Entradas más recientes

Powered by Blogger .