Trabajando con fechas y horas
El manejo de fechas y horas es una necesidad fundamental en la mayoría de aplicaciones de software. Ya sea para registrar cuándo ocurrió un evento, calcular períodos de tiempo, programar tareas futuras, o simplemente mostrar la fecha actual al usuario, necesitamos herramientas robustas y precisas para trabajar con información temporal.
C# proporciona varios tipos especializados para el trabajo con fechas y horas, siendo los más importantes DateTime
, DateOnly
, TimeOnly
, TimeSpan
y DateTimeOffset
. Cada uno está diseñado para casos de uso específicos, desde fechas simples hasta marcas de tiempo precisas con información de zona horaria.
En este artículo aprenderemos a crear, manipular y formatear fechas y horas, realizar cálculos temporales, y manejar las complejidades que surgen al trabajar con diferentes formatos, zonas horarias y culturas.
Tipos principales para fechas y horas
C# ofrece varios tipos para manejar información temporal, cada uno optimizado para diferentes escenarios:
Tipo | Descripción | Uso recomendado |
---|---|---|
DateTime | Fecha y hora completas | Marcas de tiempo generales |
DateOnly | Solo fecha (sin hora) | Fechas de nacimiento, eventos |
TimeOnly | Solo hora (sin fecha) | Horarios, duraciones del día |
TimeSpan | Intervalo de tiempo | Duraciones, diferencias temporales |
DateTimeOffset | Fecha/hora con zona horaria | Aplicaciones globales |
Trabajando con DateTime
DateTime
es el tipo más utilizado para representar fechas y horas. Puede representar valores desde el año 1 hasta el año 9999 con precisión de 100 nanosegundos.
Creación de objetos DateTime
using System;
class Program
{
static void Main()
{
// Diferentes formas de crear un DateTime
// Fecha y hora actuales
DateTime ahora = DateTime.Now;
DateTime ahoraUtc = DateTime.UtcNow;
DateTime hoy = DateTime.Today; // Solo fecha, hora a 00:00:00
// Constructores específicos
DateTime fechaEspecifica = new DateTime(2024, 12, 25); // 25 de diciembre de 2024
DateTime fechaConHora = new DateTime(2024, 12, 25, 9, 30, 0); // 25/12/2024 9:30:00
DateTime fechaCompleta = new DateTime(2024, 12, 25, 9, 30, 45, 500); // Con milisegundos
// Parsing desde strings
DateTime fechaParseada = DateTime.Parse("2024-03-15 14:30:00");
Console.WriteLine($"Ahora: {ahora}");
Console.WriteLine($"Ahora UTC: {ahoraUtc}");
Console.WriteLine($"Hoy: {hoy}");
Console.WriteLine($"Fecha específica: {fechaEspecifica}");
Console.WriteLine($"Fecha con hora: {fechaConHora}");
Console.WriteLine($"Fecha parseada: {fechaParseada}");
}
}
Propiedades principales de DateTime
using System;
class Program
{
static void Main()
{
DateTime fecha = new DateTime(2024, 6, 15, 14, 30, 45);
// Componentes individuales
Console.WriteLine($"Año: {fecha.Year}");
Console.WriteLine($"Mes: {fecha.Month}");
Console.WriteLine($"Día: {fecha.Day}");
Console.WriteLine($"Hora: {fecha.Hour}");
Console.WriteLine($"Minuto: {fecha.Minute}");
Console.WriteLine($"Segundo: {fecha.Second}");
Console.WriteLine($"Milisegundo: {fecha.Millisecond}");
// Información adicional
Console.WriteLine($"Día de la semana: {fecha.DayOfWeek}");
Console.WriteLine($"Día del año: {fecha.DayOfYear}");
// Solo fecha o solo hora
Console.WriteLine($"Solo fecha: {fecha.Date}");
Console.WriteLine($"Solo hora: {fecha.TimeOfDay}");
// Verificaciones
Console.WriteLine($"¿Es año bisiesto? {DateTime.IsLeapYear(fecha.Year)}");
}
}
Operaciones con fechas
Suma y resta de tiempo
using System;
class Program
{
static void Main()
{
DateTime fechaBase = new DateTime(2024, 6, 15, 10, 0, 0);
// Sumar diferentes unidades de tiempo
DateTime sumarDias = fechaBase.AddDays(7);
DateTime sumarHoras = fechaBase.AddHours(3.5);
DateTime sumarMinutos = fechaBase.AddMinutes(45);
DateTime sumarMeses = fechaBase.AddMonths(2);
DateTime sumarAños = fechaBase.AddYears(1);
Console.WriteLine($"Fecha base: {fechaBase}");
Console.WriteLine($"+ 7 días: {sumarDias}");
Console.WriteLine($"+ 3.5 horas: {sumarHoras}");
Console.WriteLine($"+ 45 minutos: {sumarMinutos}");
Console.WriteLine($"+ 2 meses: {sumarMeses}");
Console.WriteLine($"+ 1 año: {sumarAños}");
// También podemos restar (números negativos)
DateTime restarDias = fechaBase.AddDays(-10);
Console.WriteLine($"- 10 días: {restarDias}");
// Calcular diferencias entre fechas
DateTime fecha1 = new DateTime(2024, 1, 1);
DateTime fecha2 = new DateTime(2024, 12, 31);
TimeSpan diferencia = fecha2 - fecha1;
Console.WriteLine($"\nDiferencia entre {fecha2:yyyy-MM-dd} y {fecha1:yyyy-MM-dd}:");
Console.WriteLine($"Días: {diferencia.Days}");
Console.WriteLine($"Horas totales: {diferencia.TotalHours:F0}");
Console.WriteLine($"Minutos totales: {diferencia.TotalMinutes:F0}");
}
}
Comparaciones de fechas
using System;
class Program
{
static void Main()
{
DateTime fecha1 = new DateTime(2024, 6, 15);
DateTime fecha2 = new DateTime(2024, 6, 20);
DateTime fecha3 = new DateTime(2024, 6, 15);
// Operadores de comparación
Console.WriteLine($"fecha1 < fecha2: {fecha1 < fecha2}");
Console.WriteLine($"fecha1 > fecha2: {fecha1 > fecha2}");
Console.WriteLine($"fecha1 == fecha3: {fecha1 == fecha3}");
// Métodos de comparación
Console.WriteLine($"fecha1.CompareTo(fecha2): {fecha1.CompareTo(fecha2)}");
// Encontrar la fecha más reciente o más antigua
DateTime masReciente = (fecha1 > fecha2) ? fecha1 : fecha2;
DateTime masAntigua = (fecha1 < fecha2) ? fecha1 : fecha2;
Console.WriteLine($"Más reciente: {masReciente:yyyy-MM-dd}");
Console.WriteLine($"Más antigua: {masAntigua:yyyy-MM-dd}");
// Verificar si una fecha está en un rango
DateTime fechaPrueba = new DateTime(2024, 6, 18);
bool estaEnRango = fechaPrueba >= fecha1 && fechaPrueba <= fecha2;
Console.WriteLine($"¿{fechaPrueba:yyyy-MM-dd} está entre las otras fechas? {estaEnRango}");
}
}
Formato de fechas y horas
Formatos estándar
using System;
using System.Globalization;
class Program
{
static void Main()
{
DateTime fecha = new DateTime(2024, 6, 15, 14, 30, 45);
// Formatos estándar más comunes
Console.WriteLine($"Formato corto de fecha (d): {fecha:d}");
Console.WriteLine($"Formato largo de fecha (D): {fecha:D}");
Console.WriteLine($"Formato corto de hora (t): {fecha:t}");
Console.WriteLine($"Formato largo de hora (T): {fecha:T}");
Console.WriteLine($"Formato completo (F): {fecha:F}");
Console.WriteLine($"Formato general (G): {fecha:G}");
Console.WriteLine($"ISO 8601 (s): {fecha:s}");
Console.WriteLine($"Universal (u): {fecha:u}");
// Formatos personalizados
Console.WriteLine($"\nFormatos personalizados:");
Console.WriteLine($"dd/MM/yyyy: {fecha:dd/MM/yyyy}");
Console.WriteLine($"yyyy-MM-dd HH:mm:ss: {fecha:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"dddd, dd 'de' MMMM 'de' yyyy: {fecha:dddd, dd 'de' MMMM 'de' yyyy}");
Console.WriteLine($"HH:mm: {fecha:HH:mm}");
}
}
Formatos específicos por cultura
using System;
using System.Globalization;
class Program
{
static void Main()
{
DateTime fecha = new DateTime(2024, 6, 15, 14, 30, 45);
// Diferentes culturas
CultureInfo culturaEspañola = new CultureInfo("es-ES");
CultureInfo culturaInglesa = new CultureInfo("en-US");
CultureInfo culturaFrancesa = new CultureInfo("fr-FR");
Console.WriteLine($"Español: {fecha.ToString("F", culturaEspañola)}");
Console.WriteLine($"Inglés (US): {fecha.ToString("F", culturaInglesa)}");
Console.WriteLine($"Francés: {fecha.ToString("F", culturaFrancesa)}");
// Nombres de días y meses en diferentes idiomas
Console.WriteLine($"\nNombres de días:");
Console.WriteLine($"Español: {fecha.ToString("dddd", culturaEspañola)}");
Console.WriteLine($"Inglés: {fecha.ToString("dddd", culturaInglesa)}");
Console.WriteLine($"Francés: {fecha.ToString("dddd", culturaFrancesa)}");
Console.WriteLine($"\nNombres de meses:");
Console.WriteLine($"Español: {fecha.ToString("MMMM", culturaEspañola)}");
Console.WriteLine($"Inglés: {fecha.ToString("MMMM", culturaInglesa)}");
Console.WriteLine($"Francés: {fecha.ToString("MMMM", culturaFrancesa)}");
}
}
Parsing y conversión de strings
Conversión segura de strings a DateTime
using System;
using System.Globalization;
class Program
{
static void Main()
{
// Strings de ejemplo con diferentes formatos
string[] fechasString = {
"2024-06-15",
"15/06/2024",
"June 15, 2024",
"15 de junio de 2024",
"2024-06-15 14:30:00",
"formato_invalido"
};
foreach (string fechaStr in fechasString)
{
Console.WriteLine($"\nIntentando parsear: '{fechaStr}'");
// Método Parse (puede lanzar excepción)
try
{
DateTime fechaParseada = DateTime.Parse(fechaStr);
Console.WriteLine($"Parse exitoso: {fechaParseada}");
}
catch (FormatException)
{
Console.WriteLine("Parse falló con Parse()");
}
// Método TryParse (más seguro)
if (DateTime.TryParse(fechaStr, out DateTime fechaSafe))
{
Console.WriteLine($"TryParse exitoso: {fechaSafe}");
}
else
{
Console.WriteLine("TryParse falló");
}
}
// ParseExact para formatos específicos
string fechaPersonalizada = "15-06-2024 14:30";
string formato = "dd-MM-yyyy HH:mm";
if (DateTime.TryParseExact(fechaPersonalizada, formato, null,
DateTimeStyles.None, out DateTime fechaExacta))
{
Console.WriteLine($"\nParseExact exitoso: {fechaExacta}");
}
}
}
Trabajando con TimeSpan
TimeSpan
representa un intervalo de tiempo y es útil para medir duraciones y realizar cálculos temporales:
using System;
class Program
{
static void Main()
{
// Diferentes formas de crear TimeSpan
TimeSpan duracion1 = new TimeSpan(2, 30, 0); // 2 horas, 30 minutos
TimeSpan duracion2 = TimeSpan.FromHours(1.5); // 1.5 horas
TimeSpan duracion3 = TimeSpan.FromMinutes(90); // 90 minutos
TimeSpan duracion4 = TimeSpan.FromDays(7); // 7 días
Console.WriteLine($"Duración 1: {duracion1}");
Console.WriteLine($"Duración 2: {duracion2}");
Console.WriteLine($"Duración 3: {duracion3}");
Console.WriteLine($"Duración 4: {duracion4}");
// Propiedades de TimeSpan
Console.WriteLine($"\nDetalles de duración1 ({duracion1}):");
Console.WriteLine($"Días: {duracion1.Days}");
Console.WriteLine($"Horas: {duracion1.Hours}");
Console.WriteLine($"Minutos: {duracion1.Minutes}");
Console.WriteLine($"Total en horas: {duracion1.TotalHours}");
Console.WriteLine($"Total en minutos: {duracion1.TotalMinutes}");
// Operaciones con TimeSpan
TimeSpan suma = duracion1 + duracion2;
TimeSpan resta = duracion4 - duracion1;
Console.WriteLine($"\nSuma de duraciones: {suma}");
Console.WriteLine($"Resta de duraciones: {resta}");
// Ejemplo práctico: calcular tiempo trabajado
DateTime inicioTrabajo = new DateTime(2024, 6, 15, 9, 0, 0);
DateTime finTrabajo = new DateTime(2024, 6, 15, 17, 30, 0);
TimeSpan tiempoTrabajado = finTrabajo - inicioTrabajo;
Console.WriteLine($"\nTiempo trabajado: {tiempoTrabajado}");
Console.WriteLine($"Horas trabajadas: {tiempoTrabajado.TotalHours:F1}");
}
}
DateOnly y TimeOnly (.NET 6+)
Los tipos DateOnly
y TimeOnly
introducidos en .NET 6 proporcionan representaciones más específicas:
using System;
class Program
{
static void Main()
{
// DateOnly - solo fechas
DateOnly fechaSolo = new DateOnly(2024, 6, 15);
DateOnly hoy = DateOnly.FromDateTime(DateTime.Now);
Console.WriteLine($"Fecha específica: {fechaSolo}");
Console.WriteLine($"Hoy: {hoy}");
// Operaciones con DateOnly
DateOnly enUnaSeamana = fechaSolo.AddDays(7);
DateOnly enUnMes = fechaSolo.AddMonths(1);
Console.WriteLine($"En una semana: {enUnaSeamana}");
Console.WriteLine($"En un mes: {enUnMes}");
// TimeOnly - solo horas
TimeOnly horaSolo = new TimeOnly(14, 30, 0);
TimeOnly ahoraHora = TimeOnly.FromDateTime(DateTime.Now);
Console.WriteLine($"\nHora específica: {horaSolo}");
Console.WriteLine($"Hora actual: {ahoraHora}");
// Operaciones con TimeOnly
TimeOnly enDosHoras = horaSolo.AddHours(2);
TimeOnly en30Minutos = horaSolo.AddMinutes(30);
Console.WriteLine($"En dos horas: {enDosHoras}");
Console.WriteLine($"En 30 minutos: {en30Minutos}");
// Combinar DateOnly y TimeOnly
DateTime fechaCompleta = fechaSolo.ToDateTime(horaSolo);
Console.WriteLine($"\nFecha completa combinada: {fechaCompleta}");
}
}
Ejemplo práctico: calculadora de edad
using System;
class CalculadoraEdad
{
public static void CalcularEdadCompleta(DateTime fechaNacimiento)
{
DateTime hoy = DateTime.Today;
// Calcular años
int años = hoy.Year - fechaNacimiento.Year;
if (hoy.DayOfYear < fechaNacimiento.DayOfYear)
años--;
// Calcular próximo cumpleaños
DateTime proximoCumpleaños = new DateTime(hoy.Year, fechaNacimiento.Month, fechaNacimiento.Day);
if (proximoCumpleaños < hoy)
proximoCumpleaños = proximoCumpleaños.AddYears(1);
TimeSpan tiempoParaCumpleaños = proximoCumpleaños - hoy;
// Calcular días vividos
TimeSpan diasVividos = hoy - fechaNacimiento;
Console.WriteLine($"Fecha de nacimiento: {fechaNacimiento:dd/MM/yyyy}");
Console.WriteLine($"Fecha actual: {hoy:dd/MM/yyyy}");
Console.WriteLine($"Edad: {años} años");
Console.WriteLine($"Días vividos: {diasVividos.Days:N0}");
Console.WriteLine($"Próximo cumpleaños: {proximoCumpleaños:dd/MM/yyyy}");
Console.WriteLine($"Días para el próximo cumpleaños: {tiempoParaCumpleaños.Days}");
// Información adicional
int semanas = (int)(diasVividos.Days / 7);
int mesesAprox = años * 12 + (hoy.Month - fechaNacimiento.Month);
Console.WriteLine($"Semanas vividas aproximadamente: {semanas:N0}");
Console.WriteLine($"Meses vividos aproximadamente: {mesesAprox}");
}
}
class Program
{
static void Main()
{
// Ejemplo de uso
DateTime fechaNacimiento = new DateTime(1990, 3, 15);
CalculadoraEdad.CalcularEdadCompleta(fechaNacimiento);
Console.WriteLine("\n" + new string('-', 50));
// Probar con otra fecha
DateTime otraFecha = new DateTime(2000, 12, 25);
CalculadoraEdad.CalcularEdadCompleta(otraFecha);
}
}
Ejemplo práctico: sistema de horarios
using System;
using System.Collections.Generic;
class SistemaHorarios
{
private Dictionary<DayOfWeek, (TimeOnly inicio, TimeOnly fin)> horarios;
public SistemaHorarios()
{
horarios = new Dictionary<DayOfWeek, (TimeOnly, TimeOnly)>();
}
public void EstablecerHorario(DayOfWeek dia, TimeOnly inicio, TimeOnly fin)
{
horarios[dia] = (inicio, fin);
}
public bool EstaAbierto(DateTime momento)
{
DayOfWeek dia = momento.DayOfWeek;
TimeOnly hora = TimeOnly.FromDateTime(momento);
if (!horarios.ContainsKey(dia))
return false;
var (inicio, fin) = horarios[dia];
return hora >= inicio && hora <= fin;
}
public void MostrarHorarios()
{
Console.WriteLine("Horarios de atención:");
string[] nombresDeias = { "Domingo", "Lunes", "Martes", "Miércoles",
"Jueves", "Viernes", "Sábado" };
for (int i = 0; i < 7; i++)
{
DayOfWeek dia = (DayOfWeek)i;
Console.Write($"{nombresDeias[i]}: ");
if (horarios.ContainsKey(dia))
{
var (inicio, fin) = horarios[dia];
Console.WriteLine($"{inicio:HH:mm} - {fin:HH:mm}");
}
else
{
Console.WriteLine("Cerrado");
}
}
}
public TimeSpan TiempoParaApertura(DateTime momento)
{
if (EstaAbierto(momento))
return TimeSpan.Zero;
// Buscar el próximo día de apertura
DateTime proximaApertura = momento.Date.AddDays(1);
for (int i = 1; i <= 7; i++)
{
DateTime diaConsiderado = momento.Date.AddDays(i);
DayOfWeek dia = diaConsiderado.DayOfWeek;
if (horarios.ContainsKey(dia))
{
var (inicio, fin) = horarios[dia];
proximaApertura = diaConsiderado.Add(inicio.ToTimeSpan());
break;
}
}
return proximaApertura - momento;
}
}
class Program
{
static void Main()
{
SistemaHorarios tienda = new SistemaHorarios();
// Establecer horarios de lunes a viernes
for (DayOfWeek dia = DayOfWeek.Monday; dia <= DayOfWeek.Friday; dia++)
{
tienda.EstablecerHorario(dia, new TimeOnly(9, 0), new TimeOnly(18, 0));
}
// Sábado horario reducido
tienda.EstablecerHorario(DayOfWeek.Saturday, new TimeOnly(10, 0), new TimeOnly(14, 0));
tienda.MostrarHorarios();
// Probar diferentes momentos
DateTime[] momentosPrueba = {
new DateTime(2024, 6, 17, 10, 30, 0), // Lunes 10:30
new DateTime(2024, 6, 22, 15, 0, 0), // Sábado 15:00
new DateTime(2024, 6, 23, 12, 0, 0) // Domingo 12:00
};
Console.WriteLine("\nVerificaciones de apertura:");
foreach (DateTime momento in momentosPrueba)
{
bool abierto = tienda.EstaAbierto(momento);
Console.WriteLine($"{momento:dddd dd/MM/yyyy HH:mm}: {(abierto ? "ABIERTO" : "CERRADO")}");
if (!abierto)
{
TimeSpan tiempoParaApertura = tienda.TiempoParaApertura(momento);
Console.WriteLine($" Abre en: {tiempoParaApertura.Days} días, {tiempoParaApertura.Hours} horas, {tiempoParaApertura.Minutes} minutos");
}
}
}
}
Resumen
El trabajo con fechas y horas en C# es fundamental para la mayoría de aplicaciones. Hemos explorado los tipos principales como DateTime
, DateOnly
, TimeOnly
y TimeSpan
, cada uno optimizado para diferentes necesidades. Aprendimos a crear, manipular, comparar y formatear fechas y horas, así como a realizar cálculos temporales y manejar diferentes culturas y formatos.
Los tipos de fecha y hora en C# proporcionan una base sólida para manejar información temporal de manera precisa y eficiente. Desde simples cálculos de edad hasta sistemas complejos de horarios, estas herramientas nos permiten crear aplicaciones que manejan el tiempo de forma robusta y confiable. En el siguiente artículo profundizaremos en las operaciones avanzadas con cadenas, complementando nuestro conocimiento de los tipos de datos fundamentales en C#.