Python Datetime: Guía Completa de Fechas y Horas en Python
Updated on
Trabajar con fechas y horas en Python debería ser sencillo, pero rara vez lo es. Necesitas parsear una cadena de fecha de un archivo CSV, pero "01/02/2026" podría significar 2 de enero o 1 de febrero dependiendo de la configuración regional. Quieres calcular la diferencia entre dos marcas de tiempo, pero una es una cadena y la otra es una época Unix. Confundes strftime y strptime por décima vez este mes. Las operaciones de fecha y hora son una fuente constante de bugs, errores de desplazamiento y dolores de cabeza con zonas horarias en código de producción.
Las consecuencias van más allá de la molestia. Un formato de fecha incorrecto en un informe financiero significa cálculos erróneos. Una comparación de datetime ingenua que ignora las zonas horarias causa trabajos cron duplicados. Un cálculo de timedelta fallido cobra a un cliente 31 días en lugar de 30.
El módulo integrado datetime de Python resuelve estos problemas con una API limpia y consistente. Proporciona clases para fechas, horas, marcas de tiempo y duraciones. Una vez que aprendas sus patrones -- y especialmente la diferencia entre strftime y strptime -- podrás manejar cualquier operación de fecha sin bibliotecas de terceros.
Obtener la Fecha y Hora Actual
El punto de partida más común es obtener la fecha y hora actual. El módulo datetime proporciona varias formas de hacerlo.
from datetime import datetime, date
# Fecha y hora actual
now = datetime.now()
print(now) # 2026-02-10 14:30:45.123456
# Solo fecha actual
today = date.today()
print(today) # 2026-02-10
# datetime.today() es similar a datetime.now() pero sin soporte de zona horaria
now_alt = datetime.today()
print(now_alt) # 2026-02-10 14:30:45.123456La diferencia entre datetime.now() y datetime.today() es sutil pero importante. datetime.now() acepta un parámetro opcional tz para datetimes con zona horaria. datetime.today() no. Para la mayoría del código, prefiere datetime.now().
from datetime import datetime
from zoneinfo import ZoneInfo
# Hora actual con zona horaria
utc_now = datetime.now(tz=ZoneInfo("UTC"))
print(utc_now) # 2026-02-10 14:30:45.123456+00:00
tokyo_now = datetime.now(tz=ZoneInfo("Asia/Tokyo"))
print(tokyo_now) # 2026-02-10 23:30:45.123456+09:00Crear Objetos Datetime
Puedes crear objetos datetime a partir de componentes individuales o de datos existentes.
from datetime import datetime, date, time
# Desde año, mes, día, hora, minuto, segundo
dt = datetime(2026, 3, 15, 9, 30, 0)
print(dt) # 2026-03-15 09:30:00
# Solo fecha
d = date(2026, 12, 25)
print(d) # 2026-12-25
# Solo hora
t = time(14, 30, 0)
print(t) # 14:30:00
# Combinar fecha y hora
combined = datetime.combine(d, t)
print(combined) # 2026-12-25 14:30:00También puedes extraer componentes de un datetime existente.
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 30, 45)
print(dt.year) # 2026
print(dt.month) # 3
print(dt.day) # 15
print(dt.hour) # 9
print(dt.minute) # 30
print(dt.second) # 45
print(dt.weekday()) # 6 (Domingo, Lunes=0)
print(dt.isoformat()) # 2026-03-15T09:30:45Formatear Fechas con strftime
strftime significa "string format time" (formatear cadena de tiempo). Convierte un objeto datetime en una cadena formateada. Le pasas una cadena de formato con directivas que se reemplazan por componentes de fecha.
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 5, 7)
# Formatos comunes
print(dt.strftime("%Y-%m-%d")) # 2026-03-15
print(dt.strftime("%d/%m/%Y")) # 15/03/2026
print(dt.strftime("%B %d, %Y")) # March 15, 2026
print(dt.strftime("%Y-%m-%d %H:%M:%S")) # 2026-03-15 09:05:07
print(dt.strftime("%I:%M %p")) # 09:05 AM
print(dt.strftime("%A, %B %d, %Y")) # Sunday, March 15, 2026Una forma rápida de recordar: strftime = string from time (datetime a cadena).
Referencia de Códigos de Formato strftime
| Código | Significado | Ejemplo |
|---|---|---|
%Y | Año de 4 dígitos | 2026 |
%y | Año de 2 dígitos | 26 |
%m | Mes como número con cero | 03 |
%B | Nombre completo del mes | March |
%b | Nombre abreviado del mes | Mar |
%d | Día del mes (con cero) | 15 |
%A | Nombre completo del día de la semana | Sunday |
%a | Nombre abreviado del día de la semana | Sun |
%H | Hora (24 horas, con cero) | 09 |
%I | Hora (12 horas, con cero) | 09 |
%M | Minuto (con cero) | 05 |
%S | Segundo (con cero) | 07 |
%p | AM/PM | AM |
%f | Microsegundo (con cero a 6 dígitos) | 000000 |
%z | Desplazamiento UTC (+HHMM o -HHMM) | +0000 |
%Z | Nombre de zona horaria | UTC |
%j | Día del año (001-366) | 074 |
%% | Carácter literal % | % |
Patrones de Formato Más Usados
| Patrón | Cadena de Formato | Salida |
|---|---|---|
| ISO 8601 | %Y-%m-%dT%H:%M:%S | 2026-03-15T09:05:07 |
| Fecha EE.UU. | %m/%d/%Y | 03/15/2026 |
| Fecha europea | %d/%m/%Y | 15/03/2026 |
| Fecha legible | %B %d, %Y | March 15, 2026 |
| Marca de tiempo de log | %Y-%m-%d %H:%M:%S | 2026-03-15 09:05:07 |
| Hora de 12 horas | %I:%M:%S %p | 09:05:07 AM |
| Fecha compacta | %Y%m%d | 20260315 |
| Marca de tiempo segura para archivos | %Y%m%d_%H%M%S | 20260315_090507 |
Parsear Cadenas con strptime
strptime significa "string parse time" (parsear cadena de tiempo). Es lo inverso de strftime -- convierte una cadena en un objeto datetime. Proporcionas la cadena y el formato que sigue.
from datetime import datetime
# Parsear varios formatos de cadena de fecha
dt1 = datetime.strptime("2026-03-15", "%Y-%m-%d")
print(dt1) # 2026-03-15 00:00:00
dt2 = datetime.strptime("15/03/2026", "%d/%m/%Y")
print(dt2) # 2026-03-15 00:00:00
dt3 = datetime.strptime("March 15, 2026 09:30 AM", "%B %d, %Y %I:%M %p")
print(dt3) # 2026-03-15 09:30:00
dt4 = datetime.strptime("2026-03-15T09:30:00", "%Y-%m-%dT%H:%M:%S")
print(dt4) # 2026-03-15 09:30:00Recuerda: strptime = string parse time (cadena a datetime).
Manejar Errores de Parseo
Si la cadena no coincide con el formato, Python lanza un ValueError. Siempre envuelve las llamadas a strptime en manejo de errores cuando parsees entrada de usuario o datos externos.
from datetime import datetime
def safe_parse_date(date_string, fmt="%Y-%m-%d"):
"""Parse a date string safely, returning None on failure."""
try:
return datetime.strptime(date_string, fmt)
except ValueError as e:
print(f"Could not parse '{date_string}': {e}")
return None
# Valid input
print(safe_parse_date("2026-03-15")) # 2026-03-15 00:00:00
# Invalid input
print(safe_parse_date("15-03-2026")) # Could not parse '15-03-2026': ...
print(safe_parse_date("not a date")) # Could not parse 'not a date': ...Parsear Múltiples Formatos
Cuando recibes fechas en formatos impredecibles, prueba múltiples patrones.
from datetime import datetime
def parse_flexible_date(date_string):
"""Try multiple date formats and return the first match."""
formats = [
"%Y-%m-%d",
"%d/%m/%Y",
"%m/%d/%Y",
"%B %d, %Y",
"%b %d, %Y",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d %H:%M:%S",
]
for fmt in formats:
try:
return datetime.strptime(date_string, fmt)
except ValueError:
continue
raise ValueError(f"No matching format found for '{date_string}'")
print(parse_flexible_date("2026-03-15")) # 2026-03-15 00:00:00
print(parse_flexible_date("March 15, 2026")) # 2026-03-15 00:00:00
print(parse_flexible_date("15/03/2026")) # 2026-03-15 00:00:00Aritmética de Fechas con timedelta
La clase timedelta representa una duración -- la diferencia entre dos fechas o horas. Puedes sumar o restar objetos timedelta para mover fechas hacia adelante o hacia atrás.
from datetime import datetime, timedelta
now = datetime(2026, 2, 10, 12, 0, 0)
# Sumar días
tomorrow = now + timedelta(days=1)
print(tomorrow) # 2026-02-11 12:00:00
# Restar días
last_week = now - timedelta(weeks=1)
print(last_week) # 2026-02-03 12:00:00
# Sumar horas y minutos
later = now + timedelta(hours=5, minutes=30)
print(later) # 2026-02-10 17:30:00
# Combinar múltiples unidades
future = now + timedelta(weeks=2, days=3, hours=6)
print(future) # 2026-02-27 18:00:00Calcular Diferencias de Tiempo
Restar un datetime de otro devuelve un timedelta.
from datetime import datetime
start = datetime(2026, 1, 1)
end = datetime(2026, 12, 31)
diff = end - start
print(diff) # 364 days, 0:00:00
print(diff.days) # 364
print(diff.total_seconds()) # 31449600.0Parámetros del Constructor timedelta
| Parámetro | Descripción | Ejemplo |
|---|---|---|
weeks | Número de semanas | timedelta(weeks=2) = 14 días |
days | Número de días | timedelta(days=30) |
hours | Número de horas | timedelta(hours=12) |
minutes | Número de minutos | timedelta(minutes=45) |
seconds | Número de segundos | timedelta(seconds=120) |
milliseconds | Número de milisegundos | timedelta(milliseconds=500) |
microseconds | Número de microsegundos | timedelta(microseconds=1000) |
Todos los parámetros se pueden combinar. Internamente, timedelta solo almacena days, seconds y microseconds. Todo lo demás se convierte.
from datetime import timedelta
delta = timedelta(weeks=1, days=2, hours=3, minutes=30, seconds=45)
print(delta) # 9 days, 3:30:45
print(delta.days) # 9
print(delta.seconds) # 12645 (3*3600 + 30*60 + 45)
print(delta.total_seconds()) # 790245.0Comparar Fechas
Los objetos datetime soportan todos los operadores de comparación estándar. Esto hace que ordenar y filtrar fechas sea sencillo.
from datetime import datetime
dt1 = datetime(2026, 1, 1)
dt2 = datetime(2026, 6, 15)
dt3 = datetime(2026, 12, 31)
print(dt1 < dt2) # True
print(dt3 > dt2) # True
print(dt1 == dt2) # False
print(dt1 != dt2) # TrueOrdenar Fechas
from datetime import datetime
dates = [
datetime(2026, 12, 25),
datetime(2026, 1, 1),
datetime(2026, 7, 4),
datetime(2026, 2, 14),
]
sorted_dates = sorted(dates)
for d in sorted_dates:
print(d.strftime("%B %d, %Y"))
# January 01, 2026
# February 14, 2026
# July 04, 2026
# December 25, 2026Trabajar con Marcas de Tiempo
Las marcas de tiempo Unix representan segundos desde el 1 de enero de 1970 (la época Unix). El módulo datetime puede convertir entre marcas de tiempo y objetos datetime.
from datetime import datetime, timezone
# Datetime a marca de tiempo
dt = datetime(2026, 3, 15, 9, 30, 0)
ts = dt.timestamp()
print(ts) # 1773814200.0 (depende de la zona horaria local)
# Marca de tiempo a datetime (con zona horaria, recomendado)
dt_aware = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_aware) # 2026-03-15 01:30:00+00:00Importante: datetime.fromtimestamp() sin zona horaria devuelve la hora local. Para UTC, siempre pasa tz=timezone.utc.
Manejo de Zonas Horarias
Los datetimes ingenuos (sin información de zona horaria) son una fuente común de bugs. Python proporciona dos enfoques integrados para datetimes con zona horaria.
Usando datetime.timezone (Integrado)
La clase timezone maneja desplazamientos UTC fijos.
from datetime import datetime, timezone, timedelta
# UTC
utc_now = datetime.now(timezone.utc)
print(utc_now) # 2026-02-10 14:30:00+00:00
# Desplazamiento fijo (ej., UTC+5:30 para India)
ist = timezone(timedelta(hours=5, minutes=30))
india_time = datetime.now(ist)
print(india_time) # 2026-02-10 20:00:00+05:30
# Convertir entre zonas horarias
utc_time = datetime(2026, 3, 15, 12, 0, 0, tzinfo=timezone.utc)
eastern = timezone(timedelta(hours=-5))
eastern_time = utc_time.astimezone(eastern)
print(eastern_time) # 2026-03-15 07:00:00-05:00Usando zoneinfo (Python 3.9+)
Para zonas horarias con nombre con manejo correcto del horario de verano, usa el módulo zoneinfo.
from datetime import datetime
from zoneinfo import ZoneInfo
# Zonas horarias con nombre
utc = ZoneInfo("UTC")
eastern = ZoneInfo("America/New_York")
tokyo = ZoneInfo("Asia/Tokyo")
# Crear datetime con zona horaria
dt = datetime(2026, 7, 15, 12, 0, 0, tzinfo=utc)
print(dt) # 2026-07-15 12:00:00+00:00
# Convertir a otras zonas horarias
print(dt.astimezone(eastern)) # 2026-07-15 08:00:00-04:00 (EDT)
print(dt.astimezone(tokyo)) # 2026-07-15 21:00:00+09:00Datetimes Ingenuos vs Conscientes
| Característica | Ingenuo | Consciente |
|---|---|---|
| Tiene información de zona horaria | No | Sí |
| Seguro para comparaciones entre zonas | No | Sí |
Creado por datetime.now() | Sí | No (a menos que se pase tz) |
| Se puede mezclar en aritmética | Solo con otros ingenuos | Solo con otros conscientes |
| Recomendado para producción | No | Sí |
No puedes comparar o restar un datetime ingenuo de uno consciente. Python lanza un TypeError.
Ejemplos Prácticos
Calcular la Edad de una Persona
from datetime import date
def calculate_age(birth_date):
"""Calculate age in years from a birth date."""
today = date.today()
age = today.year - birth_date.year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age
birthday = date(1995, 8, 20)
print(f"Age: {calculate_age(birthday)} years") # Age: 30 years (as of Feb 2026)Generar un Rango de Fechas
from datetime import date, timedelta
def date_range(start, end, step_days=1):
"""Generate dates from start to end (inclusive)."""
current = start
while current <= end:
yield current
current += timedelta(days=step_days)
start = date(2026, 2, 1)
end = date(2026, 2, 7)
for d in date_range(start, end):
print(d.strftime("%A, %B %d"))Contar Días Laborables Entre Dos Fechas
from datetime import date, timedelta
def business_days_between(start, end):
"""Count weekdays (Mon-Fri) between two dates, excluding endpoints."""
count = 0
current = start + timedelta(days=1)
while current < end:
if current.weekday() < 5: # 0=Mon, 4=Fri
count += 1
current += timedelta(days=1)
return count
start = date(2026, 2, 1)
end = date(2026, 2, 28)
print(f"Business days: {business_days_between(start, end)}") # Business days: 19Experimentar con Datetime en Jupyter
Las operaciones de fecha y hora se benefician de la experimentación interactiva. Cuando estás parseando formatos de fecha inconsistentes de un CSV o depurando conversiones de zona horaria, poder probar cada paso en una celda de notebook ahorra tiempo significativo.
RunCell (opens in a new tab) es un agente de IA que trabaja directamente dentro de notebooks Jupyter. Puede inspeccionar tus objetos datetime, sugerir los códigos de formato strftime/strptime correctos para tus datos y ayudar a depurar problemas de conversión de zona horaria en tiempo real.
strftime vs strptime: Comparación Rápida
| strftime | strptime | |
|---|---|---|
| Nombre completo | String Format Time | String Parse Time |
| Dirección | datetime -> cadena | cadena -> datetime |
| Se llama en | Un objeto datetime | La clase datetime |
| Sintaxis | dt.strftime("%Y-%m-%d") | datetime.strptime(s, "%Y-%m-%d") |
| Devuelve | Una cadena formateada | Un objeto datetime |
| Lanza | Nunca | ValueError si el formato no coincide |
| Caso de uso | Visualización, logging, nombres de archivo | Parsear CSV, respuestas API, entrada de usuario |
from datetime import datetime
# strftime: datetime -> string
dt = datetime(2026, 3, 15, 9, 30)
formatted = dt.strftime("%B %d, %Y at %I:%M %p")
print(formatted) # March 15, 2026 at 09:30 AM
# strptime: string -> datetime
parsed = datetime.strptime("March 15, 2026 at 09:30 AM", "%B %d, %Y at %I:%M %p")
print(parsed) # 2026-03-15 09:30:00FAQ
¿Cómo obtengo la fecha y hora actual en Python?
Usa datetime.now() del módulo datetime. Para solo la fecha, usa date.today(). Para la hora actual con zona horaria, pasa una zona horaria: datetime.now(tz=timezone.utc). Estos son los enfoques estándar y no requieren bibliotecas de terceros.
¿Cuál es la diferencia entre strftime y strptime en Python?
strftime convierte un objeto datetime a una cadena formateada (cadena desde tiempo). strptime parsea una cadena y la convierte en un objeto datetime (parsear cadena de tiempo). Piénsalo así: strftime produce cadenas, strptime consume cadenas. Ambos usan los mismos códigos de formato como %Y, %m, %d.
¿Cómo agrego días a una fecha en Python?
Usa la clase timedelta. Impórtala de datetime, luego súmala a tu fecha: new_date = old_date + timedelta(days=7). También puedes usar weeks, hours, minutes y seconds como parámetros. La resta funciona igual: past_date = today - timedelta(days=30).
¿Cómo convierto una cadena a un datetime en Python?
Usa datetime.strptime(string, format). Necesitas proporcionar una cadena de formato que coincida con tu entrada. Por ejemplo, datetime.strptime("2026-03-15", "%Y-%m-%d") parsea una fecha ISO. Si la cadena no coincide con el formato, Python lanza un ValueError, así que envuelve la llamada en un bloque try/except para datos externos.
¿Cómo manejo las zonas horarias en Python datetime?
Para Python 3.9+, usa el módulo integrado zoneinfo: from zoneinfo import ZoneInfo. Crea datetimes con zona horaria con datetime.now(tz=ZoneInfo("UTC")) y convierte entre zonas con dt.astimezone(ZoneInfo("America/New_York")). Para desplazamientos UTC fijos, usa datetime.timezone(timedelta(hours=N)). Evita datetimes ingenuos en código de producción.
Conclusión
El módulo datetime de Python proporciona todo lo que necesitas para operaciones de fecha y hora: crear fechas, formatearlas para visualización, parsearlas de cadenas, realizar aritmética y manejar zonas horarias. Los patrones clave son consistentes y predecibles una vez que los aprendes.
Recuerda la distinción central: strftime formatea un datetime en una cadena, strptime parsea una cadena en un datetime. Usa timedelta para aritmética de fechas. Usa zoneinfo (Python 3.9+) para manejo correcto de zonas horarias. Mantén los datetimes con zona horaria en código de producción para evitar bugs sutiles de comparación.
Para la mayoría de proyectos, el módulo integrado datetime es suficiente. No necesitas pytz en Python moderno -- zoneinfo maneja zonas horarias con nombre de forma nativa. Comienza con datetime.now(tz=timezone.utc) como tu punto de referencia y convierte a zonas horarias locales solo cuando muestres información a usuarios.