Google+ Seguidores

sábado, 20 de enero de 2018

Creando Calculadoras del Tiempo #4 - Los cálculos finales

Articulo escrito por Antonio Alfonso Martinez.
Continuamos con la serie de artículos dedicados a la creación de nuestra calculadora de tiempo, la cual, una vez terminada, va a ser capaz de calcularnos, en horas, minutos y segundos, la distancia existente entre dos instantes de tiempo, ubicados en cualquier fecha entre el 1 de Enero del año 1 y el 31 de Diciembre de 9999.
En el artículo anterior, empezamos ya a realizar nuestros primeros cálculos, los cuales efectuábamos sobre instantes ubicados en un mismo día del año. En el presente artículo, vamos a proceder a ver como logramos hacer ese mismo tipo de cálculos, esta vez, entre instantes pertenecientes a distintas fechas.
Como recordarán, en el anterior artículo, empezábamos explicando la manera en la que pedíamos al usuario de nuestro programa, los datos necesarios para hacer los correspondientes cálculos y de las medidas que empleábamos (mediante el uso de varias funciones) para asegurarnos de la idoneidad de dichos datos introducidos. Ni que decir que no vamos a volver a explicar esa parte en tanto que las operaciones que vamos a explicar a continuación, trabajarán, también, con esos mismos datos.
De ese modo, para comenzar la explicación, partiremos del código que empleamos para instantes acaecidos en un mismo día:
Así teníamos que en el caso de que el año, mes y día coincidieran, pasábamos a emplear el código que ya vimos en el artículo anterior. Por su parte con “else:” estamos diciendo que, de no ser así (por no darse tales todas esas coincidencias), pasaremos a ejecutar la parte del código que vamos a explicar.
Para empezar, diremos que debido a que en esta ocasión no hay coincidencia de fechas, las diferencias que haya con respecto al año, mes y días van a ser determinantes para el resultado final. Por ello, en esta ocasión, si vamos a hacer un mayor uso del modulo “datetime” el cual a importamos en su momento.
Debido a que vamos a hacer uso de una nueva función (la función “datetime”) para calcular la distancia entre instantes ubicados en fechas distintas, vamos a proceder a poner un sencillo ejemplo de su funcionamiento.
Como se puede ver, hemos utilizado la formula “datetime.datetime”, primero para definir los instantes de tiempo y luego efectuar la diferencia entre los mismos. En este ejemplo queremos calcular la distancia existente entre la hora de inicio del 2017 y las 4 horas del día 3 de Febrero de 2018. Para ello, hemos introducido (para los dos instantes, “I1” y “I2”), de izquierda a derecha, el año, mes, día, hora, minuto y segundos correspondientes. En la variable “difer” hemos almacenado la diferencia entre ambos instantes. Esto nos ha dado un resultado de 398 días y 4 horas. Lo cual tiene sentido ya que a los 365 días que hay entre 2017 y 2018, se le suman los 30 días correspondientes al mes de Enero del 2018 y los 3 primeros días de Febrero (ya que el instante segundo se situaba en el día 3 de dicho mes). A esto, finalmente, se le añaden las 4 horas especificadas para el segundo instante.
Con esta función ya tenemos la base para hacer nuestros cálculos. Sin embargo, podemos observar en el ejemplo, como los periodos completos de 24 horas, se han agrupado en días, cuando, nosotros lo que queremos es obtener el resultado en horas, minutos y segundos totales (sin que se produzca agrupaciones en otras medidas de tiempo). Es por ello, que sobre este tipo de resultado tendremos que seleccionar, solo, algunos campos, y realizar algunas modificaciones sobre el formato de modo que obtengamos el resultado deseado.
Para aplica el método “datetime.datetime” a nuestro caso concreto, lo primero que tenemos que hacer, naturalmente, es aplicar este método a los datos que el usuario introdujo, ya, en su momento. Lo cual haremos con una función que aplicaremos a cada uno de los instantes entre los que queremos efectuar el cálculo de la distancia, tomando como argumento, cada uno de los campos que componen la definición de cada instante (año, mes, día, hora, minuto y segundo). De ese modo las variables (a las que hemos dado el nombre de “D1” y “D2”) que emplearemos para efectuar el cálculo de la distancia quedaría definidas del modo siguiente:


En donde “D1” y “D2” son nuestros instantes de tiempo y “entering_date” la función que se encargará de definirlas mediante el método “datetime.datetime”, cuyo código sería el siguiente:
Como se puede apreciar, hemos creado una función que lo que va a hacer es tomar los datos (año, mes, día, hora, minuto y segundo) correspondientes a cada uno de nuestros instantes, y convertirlos a un formato que permita a la computadora trabajar con ellos como fechas.
A continuación, una vez que tenemos nuestros instantes de tiempo en el formato adecuado para fechas, es el momento de efectuar el cálculo de la diferencia entre ambas. En este punto debemos recordar que para nuestro propósito de expresar el resultado solo en horas, minutos y segundos, vamos a emplear como dato principal la parte del resultado correspondiente al número de días, por una parte, y al número de horas, por otra. Por ello debemos encontrar un modo de “separar” las partes del resultado que vamos a utilizar para hacer el cálculo final. El método que emplearemos lo podemos ilustrar con la siguiente secuencia de fases:
Partimos de una situación en la que hemos establecido los instantes “I1” e “I2” mediante “datetime.datetime”, cuya diferencia hemos almacenado en la variable “diferencia”, dándonos como resultado 576 días, 23 horas, 22 minutos y 7 segundos. Para poder trabajar con los días y las horas por separado, tenemos que empezar efectuando una primera división de los mismos:
Como se ve, hemos efectuado una primera separación del resultado “diferencia”, creando una lista con dos elementos: Por una parte, el referente al número de días (“576 days”) y por otra, el referente a las horas, minutos y segundos (“23:22:07”). Obsérvese que para ello hemos utilizado la función “.split” la cual ha separado usando, en este caso, la coma (“,”) como criterio separador (nótese también que el resultado “diferencia” ha tenido que ser pasado al formato “string” para realizar dicha operación). Sin embrago, con cada uno de estos elementos no podemos trabajar de un modo directo, todavía. Así el elemento referente a los días, contiene la palabra “days” con la cual no podremos trabajar. Es por ello que se hace necesario efectuar una división entre “576” y la palabra “days”.
Como se ve, con “sep2” hemos realizado una segunda separación (esta vez, usando como criterio separador, el espacio en blanco) dentro del primer elemento (posición 0 del la lista “sep1”), entre la parte numérica (aún en formato “string”) y la palabra “days”. Ahora solo nos falta “traducir” la parte numérica al formato de número entero:
Así hemos, finalmente, creado una variable (“num_dias”) la cual consiste en la conversión a entero (con “int”) del elemento ubicado en la posición 0, de la lista “sep2”. Con este conjunto de 4 pasos hemos logrado extraer la parte del output proporcionado por “datetime.datetime”, que nos interesa para hacer nuestro cálculo final del número de horas entre nuestros instantes. Este mismo proceso tendremos que hacer con la parte referente a las horas, minutos y segundos (de donde extraeremos el entero del número de horas).
Con estos cuatro pasos hemos desglosado la lógica empleada para la obtención del número de días y horas, a partir de la diferencia original. Ahora aplicaremos este método a nuestro programa. Sin embargo para no hacerlo excesivamente largo, vamos a resumir un poco el modo de hacerlo (incluyendo más de un paso en una misma línea).
Aquí tenemos como la variable (a la que he dado el nombre de “difer”) es igual a la separación, mediante la coma y en una lista, de las dos partes (la referente a los días y la referente a las horas, minutos y segundos) de las que consta el resultado obtenido de restar los dos instantes obtenidos a su vez, mediante “datetime.datetime”.
Finalmente hemos creado la variable “numero_dias”, la cual es igual a la separación (esta vez, mediante el espacio en blanco) del elemento, (ubicado en la posición 0), de la lista “difer” (recordemos que con esta última separación estábamos separando la parte numérica del string “days”) quedando, únicamente la parte numérica. En el caso de la variable “numero_horas” hemos “extraído” el elemento ubicado en la posición 0 (las horas) del elemento ubicado en la posición 1 (dedicado a las horas, minutos y segundos) de la lista “difer”.
Con este largo (y algo tortuoso) proceso hemos, finalmente, logrado obtener el número de días (almacenado en la variable “numero_dias”) y las horas “sobrantes” que no se habían integrado en el resultado correspondiente a los días, al ser menos de 24 (almacenadas en la variable “numero_horas”). Ahora ya solo nos queda hacer los cálculos finales del número total de horas, minutos y segundos, haciendo uso del mismo tipo de operaciones matemáticas y de corrección que vimos en el artículo anterior (cuando calculamos dichos parámetros para el caso en que nuestros instantes estuviesen ubicados en el mismo día). De ese modo tenemos:
Aquí hacemos, primero un cálculo “provisional” (al que llamamos “Total_horasProv”) del número total de horas (lo cual hacemos multiplicando el número de días por 24 y sumándole las horas “sobrantes” no incluidas en el resultado de los días). Sin embargo, este cálculo es aún insuficiente, ya que no tiene en cuenta los minutos y segundos de inicio y final, los cuales, quedaron establecidos en los datos de entrada “minuto1”-“segundo1” del primer instante y “minuto2”-“segundo2” del segundo instante. De ese modo, para calcular los minutos totales (“Total_minutos”), hemos multiplicado el número “provisional” de horas por 60, calculo al que a continuación le hemos restado el “minuto1” del instante primero, y le sumamos el minuto2, correspondiente al instante segundo (explico el motivo de estas operaciones, de forma más detallada, en mi anterior articulo). El mismo tipo de operación (solo que usando segundos) es el que realizamos para calcular los segundos totales (“Total_segundos”). Finalmente haremos el cálculo definitivo de las horas totales (“Total_horas”) utilizando, esta vez desde el principio, la cifra total de segundos, la cual, dividiremos por 3600 (los segundos de los que consta una hora) para obtener un resultado más exacto.
Hasta aquí hemos explicado el modo de calcular la cantidad total de horas, minutos y segundos, existentes entre dos instantes de tiempo, habiendo explicado, dicho calculo, para el caso de instantes pertenecientes a un mismo día (lo cual hicimos en el artículo anterior), y para el caso de instantes ubicados en días distintos (lo cual hemos hecho en el presente artículo).
Sin embargo aún nos queda un pequeño detalle por tratar, para ello vamos a volver a la parte del código referente a la introducción de los datos de partida, por parte del usuario de nuestro programa.

Como recordaremos, dividíamos, la introducción de datos en dos bloques (el relativo al primer suceso y el relativo a segundo suceso). De modo que el usuario tenía que introducir, primero, los datos relativos al año, mes, día, hora, minuto y segundo del suceso desde el cual se tenía que iniciar el cómputo del tiempo, y luego, debía introducir los datos del suceso en el que tenía que finalizar dicho cómputo (hasta aquí todo bien). Pero cabe preguntarnos ¿qué sucedería si el usuario (por el motivo que fuera) invirtiese dicho orden a la hora de introducir los datos (de modo que introdujese, para el primer instante, los datos correspondientes al segundo y viceversa)?
Para ilustrar el problema, vamos a realizar una pequeña simulación, ejecutando nuestro programa, en primer lugar, en el caso de que no hubiéramos previsto esa posible eventualidad.
Así empezaremos viendo el supuesto en el que el usuario introdujese los datos en el orden correcto. Así tenemos:



En nuestra simulación, hemos hecho que nuestro hipotético usuario introduzca los datos, referentes a los dos instantes, en el orden “correcto”, dándonos unos resultados coherentes y acertados para el cálculo de la distancia entre ambos instantes. A continuación, siguiendo con nuestra simulación, vamos a volver a introducir los mismos datos, pero de modo invertido.
Vemos que en esta ocasión, hemos obtenido unos resultados ligeramente distintos a los anteriores (lo cual no tiene sentido, debido a que el periodo de tiempo comprendido entre ambos instantes es siempre el mismo, con independencia del orden en el que se introduzcan los datos). Este fallo se ha debido, principalmente a los pares de datos, “minuto1”-“minuto2” por una parte y “segundo1”-“segundo2” por otra (esto es así, en la medida que en el momento de efectuarse el computo de los minutos y segundos totales, lo que se tenía que restar se ha sumado y lo que se tenía que sumar se ha restado). Por ello va a ser necesario que elaboremos un procedimiento que, ante tal caso, “reordene” los datos de tal modo que el resultado sea siendo el correcto.
Para ello, lo primero que tenemos que hacer es definir el tipo de situación en la que queremos que se produzca tal reordenación. Así, debajo del código orientado a la introducción de datos escribiremos lo siguiente:
Con esta larga condición, estamos contemplando todos los casos en los que el instante introducido en primer lugar, pueda ser posterior al introducido en segundo lugar (cuando, para que se calcule correctamente, debe ser al revés). Concretamente, estamos diciendo “si el -año2 es menor que el año1 o, siendo ambos años iguales, el mes1 es mayor que el mes2 o, siendo ambos meses iguales, el dia1 es mayor que el dia2  o, siendo ambos días iguales, la hora1 es mayor que la hora2 o siendo ambas horas iguales, el minuto1 es mayor que el minuto2 o, siendo ambos minutos iguales, el segundo1 es mayor que el segundo2, ejecutar este bloque de código”. Con esta larga sentencia hemos intentado prever todos los casos en los que el instante primero puede llegar a ser mayor que el instante segundo.
Como ya hemos dicho, en estos casos en los que los datos del instante primero son posteriores a los del instante segundo, lo que vamos a hacer es reordenar los datos introducidos por el usuario. Es decir, vamos a realizar un intercambio, de los datos referentes al año, mes, día, hora, minuto y segundo, entre los dos instantes. Para ello hemos de buscar un modo de hacerlo.
En la foto se muestra un intento fallido de realizar un intercambio de tales características, entre dos variables: “a” (cuyo valor es 123) y “b” (cuyo valor es 345). Una vez intentado el intercambio vemos que ambas variables han adoptado el valor de 345 (lo que supone que no hemos logrado el intercambio, ya que queríamos que “a” fuese 345 y “b” fuese 123). El fallo ha sido debido a que primero hemos hecho “a=b”, con lo que “a” ha dejado de ser 123 (para ser 345) y a continuación, cuando hemos querido que “b” fuese 123 (haciendo “b=a”) la operación ha salido mal ya “a” ya se había convertido en 345. Con lo que al final “a” y “b” han acabado siendo iguales a 345.
Así tenemos que lo que nos ha pasado, ha sido que se ha producido una “interferencia” en la asignación de valores, entre las dos variables. Por lo que se nos ha ocurrido otro modo de hacerlo:
En este segundo intento de intercambio, hemos creado dos variables “a1” y “b1” las cuales hemos hecho igual a “a” y “b”, respectivamente. Con ello lo que hemos logrado es dejar a salvo los dos valores, durante la acción de intercambio (se podría decir que las variables “a1” y “b1” han actuado como “puentes” entre “a” y “b”, evitando el problema de la interferencia de datos). Esta solución es la que nos ha permitido intercambiar con éxito (como puede verse) los valores de ambas variables.
Una vez que hemos ensayado este método (con éxito), es hora de aplicarlo al caso de nuestro programa.
De ese modo, conseguimos que se efectúe el intercambio de datos entre los dos instantes de tiempo. De modo que cuando se introduzcan los datos de forma que se asignen al instante primero los datos correspondientes al instante segundo (y viceversa) esta situación sea “detectada” por el programa el cual se encargará (por sí mismo) de corregir tal error.
Una vez aplicada la solución a nuestro programa, vamos a hacer el mismo simulacro que empleamos al principio y utilizando los mismos datos de entrada pero de modo invertido (poniendo la fecha posterior primero):

Ahora si comparamos estos resultados con los que obtuvimos en el primer simulacro, vemos que ahora si coinciden a la perfección. De este modo hemos conseguido que nuestro programa nos proporcione el mismo resultado con total independencia del orden en el que introduzcamos los instantes de tiempo entre los que queremos efectuar el cálculo.
Y aquí concluye mi exposición (que ha abarcado tres artículos) acerca del modo en que he creado esta calculadora capaz de calcular la distancia (en horas, minutos y segundos) entre dos instantes de tiempo cualesquiera y que espero que haya sido de vuestro agrado.
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

No hay comentarios :
Write comentarios

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

Powered by Blogger .