Lidiar con fechas es una constante en el dia a dia de programación, logs, registros, fechas de creación de información eventos ocurridos,  reuniones (estas fechas son las más difíciles)  y todas estas fechas en diferentes formatos y colores Ver Figura 1, por ello en el artículo de hoy me enfocare en hablar de algunas librerías que nos pueden ser útiles para trabajar con operaciones y formateo entre fechas

Figura 1: Diferentes formatos de fechas tomado de https://xkcd.com/1179/

Timeago

Es una librería enfocada a trabajar con formateo de fechas en texto usando el concepto "** time ago" y "in **" es bastante sencilla, simple y muy puntual en lo que hace, de hecho tiene su versión en javascript, bien veamos el ejemplo de esta libreria para instalarla ejecutare el siguiente comando

pip install timeago
import timeago
from datetime import datetime, timedelta, date


def show_format_date_future(date_future:datetime):
    return timeago.format(date_future)

def show_format_two_dates(date_one:datetime, date_two:date):
    return timeago.format(date_one, date_two)

date_future = datetime.now() + timedelta(seconds = 60 * 3.4)
date_one = datetime.now()

print(show_format_date_future(date_future=date_future))
print(show_format_two_dates(date_one, date_future))
print(show_format_two_dates(date_future, date_one))

Ejecuto y obtengo el siguiente resultado

in 3 minutes
3 minutes ago
in 3 minutes

la función .format() de timeago es la que se encarga de recibir las fechas, recibe como parámetro dos fecha y devuelve un texto con sus diferencias tanto en pasado si la primera fecha es menor que la segunda o en futuro si la primera fecha es mayor a la segunda devuelve un texto en futuro, la librería acepta date y datimes  sin problema, a los dates les pone en el tiempo todo en 0, y también acepta cadenas de texto y el formato de salida permite cambiar el lenguaje como se ve a continuación

import timeago
from datetime import datetime, timedelta, date
from typing import Union


def show_format_date_future(date_future:datetime):
    return timeago.format(date_future,locale="es")

def show_format_two_dates(date_one:datetime, date_two:Union[date,str]):
    return timeago.format(date_one, date_two, locale="es")

date_future = datetime.now() + timedelta(seconds = 60 * 3.4)
date_one = datetime.now()

print(show_format_date_future(date_future=date_future))
print(show_format_two_dates(date_one, date_future))
print(show_format_two_dates(date_future, '2016-05-27 12:12:12'))

las fechas que se pasen como str deben cumplir con el requisito de que sea str formateables por datetime si no les generara error

Moment

Esta librería  es una librería para gestionar el manejo de fechas, no solo permite hacer conversiones si no también operaciones como sumar semanas, días y años para instalarla usare el siguiente comando

pip install moment
from typing import Union
import moment
from datetime import date, datetime

def add_weeks(deadline:Union[str,date,datetime],weeks:int=1):
    return moment.date(deadline).add(weeks=weeks).date

def add_years(deadline:Union[str,date,datetime],years:int=1):
    return moment.date(deadline).add(years=years).date

def substract_months(deadline:Union[str,date,datetime],months:int=1):
    return moment.date(dead_line).subtract(months=months)

dead_line = datetime.now()
print(add_weeks(deadline=dead_line, weeks=3))
print(add_years(deadline="2021-09-11",years=2))
print(substract_months(deadline="2021-11-13 11:21:09", months=12))
print(add_weeks(deadline="October 23, 2021", weeks=3))

Ejecuto y obtengo el siguiente resultado

2021-11-13 11:27:55.597719
2023-09-11 00:00:00
2020-10-23T11:27:55-05.00
2021-11-13 00:00:00

en el código anterior se utiliza el moment.date() para retornar un objeto de tipo moment el cual permite realizar varias operaciones entre ellas add y subtract las cuales permiten sumar o restar cualquier valor perteneciente a la fecha, desde años hasta horas minutos y segundos y cuando la operación está realizada simplemente se devuelve el objeto date que es un objeto nativo del date de python, aquí ya se podría guardar en la base de datos o realizar cualquier operación, otro agregado de esta librería es la capacidad de crear fechas utilizando cadenas de texto con diferentes formatos y adicionalmente permite actualizar el time zone de una fecha como se muestra a continuación

def substract_months(deadline:Union[str,date,datetime],months:int=1):
    return moment.date(dead_line).subtract(months=months).locale("US/Eastern").date

Pytime

El fuerte de esta librería es trabajar con nombres de fechas y pasarlos a date/time, ademas tiene varias cosas interesantes para probar, para instalarla

pip install pytime
from datetime import date
from typing import List, Union
from pytime import pytime

def date_parser(data_to_parser:str)->date:
    return pytime.parse(data_to_parser)

def date_before(deadline:Union[date,str],diff:str)->date:
    return pytime.before(deadline,diff )

def date_range(date_one:Union[date,str], date_two:Union[date,str])->List[date]:
    return  pytime.days_range(date_one, date_two)

print(date_parser("April 3rd 2015"))
print(date_parser("Oct, 1st. 2015"))
print(date_parser("92-11-2"))
print(date_before(deadline=date.today(), diff="3years 3months"))
print(date_before(deadline="Oct, 1st. 2015", diff="3years 3days"))
print(date_range("2021-01-01", "2021-01-10"))

ejecuto y obtengo el siguiente resultado

2015-04-03
2015-10-01
1992-11-02
2018-07-23 00:00:00
2012-09-28 00:00:00
[datetime.date(2021, 1, 10), datetime.date(2021, 1, 9), datetime.date(2021, 1, 8), datetime.date(2021, 1, 7), datetime.date(2021, 1, 6), datetime.date(2021, 1, 5), datetime.date(2021, 1, 4), datetime.date(2021, 1, 3), datetime.date(2021, 1, 2), datetime.date(2021, 1, 1)]

en el código se puede apreciar que el fuerte es la conversión de textos a fechas utilizando diferentes expresiones regulares, de hecho tienen muchas más en su repertorio, pero una de las funciones más interesantes es el before esté recibe dos parámetros una fecha que puede ser cualquier expresión y un diff que es un texto que indicare que tiempo hacia atrás se quiere devolver pero usando expresiones regulares, en este ejemplo yo le indico 3 años y 3 meses y en el siguiente le indico 3 años y 3 días, esto me retornara un date restando esa cantidad de tiempo, otra función interesante es el days_range() esta recibe como parámetros dos fechas date_one y date_two en este ejemplo, la primera debe ser menor a la segunda y apartir de ahi devuelve una lista de fechas  con los días que faltan para llegar de la date_one a la date_two está feature en específico es bastante interesante el caso de uso puede enfocarse a muchas aplicaciones, lo bueno aquí es que el formato devuelve siempre date que es el dato nativo de python y de ahí ya se integra con cualquier otra librería o base de datos que se requiera

Dateutil

Es una librería muy cercana al datetime podría decirse que es más un extensión permite hacer varias operaciones más fácil que de manera nativa sin embargo al ser algunas operaciones muy manuales algunos devs la consideran un poco pesada por otro lado es la librería que más  stars en github tiene en comparación con las anteriores (incluso alguna de las librerías anteriores la usan como base), para instalarla ejecuto el siguiente comando

pip install python-dateutil
from datetime import date, datetime
from dateutil.relativedelta import relativedelta, WE, MO, SA


def add(
    deadline: datetime, months: int = 0, days: int = 0, years: int = 0, hours: int = 0
) -> datetime:
    return deadline + relativedelta(
        months=+months, years=+years, days=+days, hours=+hours
    )


def substract(
    deadline: datetime, months: int = 0, days: int = 0, years: int = 0, hours: int = 0
) -> datetime:
    return deadline + relativedelta(
        months=-months, years=-years, days=-days, hours=-hours
    )


def next_wednesday(deadline: datetime) -> datetime:
    return deadline + relativedelta(weekday=WE(+1))


def next_saturday(deadline: datetime) -> datetime:
    return deadline + relativedelta(days=+1, weekday=SA(+1))


def how_old_is(birthday: date) -> int:
    return relativedelta(datetime.now(), birthday).years


deadline = datetime.now()
print(add(deadline=deadline, years=3, days=3, hours=2))
print(substract(deadline=deadline, months=7))
print(next_wednesday(deadline=deadline))
print(next_saturday(deadline=deadline))
birthday = datetime(2000, 11, 2)
print(how_old_is(birthday=birthday))

ejecuto el codigo y obtengo el siguiente resultado

2024-10-26 14:58:33.011097
2021-03-23 12:58:33.011097
2021-10-27 12:58:33.011097
2021-10-30 12:58:33.011097
20

esta librería realmente es bastante completa algunos funciones requieren un poco mas de trabajo que  las anteriores, pero tiene bastantes cosas para utilizar una de ellas es la diferencia de tiempo entre dos fechas utilizando relativedelta() y pasando como parámetros datetime.now() y la fecha de nacimiento de una persona aquí se puede calcular la diferencia y retorna los days, años meses , horas minutos y segundos lo cual tiene un amplio espectro en donde se puede aplicar. Sumar y restar fechas también se vuelve bastante sencillo utilizando el realtivedelta() y pasando como parámetros months, days years hours o seconds si se pasa en positivo suma es decir  "+1" y si se pasa negativo es decir "-1" le resta ese parámetro, esto devuelve un datetime y adicionalmente tiene las variables WE,MO,SA para poder calcular la fecha donde sea por ejemplo el próximo miércoles o el próximo sábado, esta librería tiene muchísimas mas cosas y elementos que te pueden servir en su documentación pueden encontrar mas material

Conclusiones

El manejo de fechas siempre es necesario y a veces se puede volver un dolor de cabeza por diferentes razones, aquí tenemos una amplia gama de librerías que resuelven diferentes problemas de trabajar con fechas en donde pueden escoger la que más se adapte a su contexto, yo he podido utilizar la mayoría todas tienen sus ventajas y desventajas lo mejor es entender que se requiere y optar por la que ofrece la solución más sólida a tu problema en específico, la idea de este post era mostrar que trabajar con fechas puede llegar a ser bastante manejable