Introducción: ¿Por qué necesitamos el «tiempo relativo»?
\n\n¿Alguna vez te has preguntado por qué las redes sociales y los sitios de noticias escriben «hace 2 horas» o «ayer», y no «25.04.2025 14:32»? Porque nuestro cerebro procesa el tiempo relativo mucho más rápido. Una fecha absoluta requiere análisis: «Bien, hoy es 25, entonces eso fue…». Una fecha relativa da la respuesta al instante.
\n\nSi programas en C# — ya sea una aplicación web en ASP.NET, una aplicación de escritorio o un bot de Telegram — tarde o temprano necesitarás mostrar al usuario la hora en formato «justo ahora», «hace 3 minutos» o «hace aproximadamente un año». En Stack Overflow, la pregunta «Calculate relative time in C#» ha acumulado miles de visitas, lo que significa que es un verdadero dolor de cabeza para los desarrolladores.
\n\nEn este artículo, analizaremos la forma más correcta y flexible de calcular el tiempo relativo en C#. Obtendrás código listo para usar que puedes insertar directamente en tu proyecto, entenderás cómo funciona y aprenderás a personalizarlo para cualquier necesidad.
\n\nEl problema: ¿qué tiene de malo la simple resta de fechas?
\n\nLo primero que viene a la mente es restar una fecha de otra y obtener un TimeSpan. Sí, funciona. Pero el diablo está en los detalles:
- \n
- Valores umbral. ¿Cuándo termina un «minuto» y comienza una «hora»? ¿Y cuándo una «hora» pasa a ser un «día»? \n
- Fechas futuras. ¿Qué pasa si la fecha está en el futuro? ¿Mostrar «dentro de 5 minutos» o «dentro de 1 día»? \n
- Legibilidad del código. Si dispersas la lógica por todo el proyecto, en un mes te confundirás tú mismo. \n
Por lo tanto, no necesitamos solo un fragmento de código, sino un método único y reutilizable.
\n\nSolución: método universal para calcular el tiempo relativo
\n\nLa mejor manera es escribir un único método estático que tome un DateTime (o DateTimeOffset) y devuelva una cadena formateada. Aquí tienes una implementación básica pero ya muy potente, que se puede encontrar en discusiones de Stack Overflow y adaptar a tus necesidades:
using System;\n\npublic static class TimeHelper\n{\n public static string GetRelativeTime(DateTime dateTime)\n {\n var now = DateTime.Now;\n var utcNow = DateTime.UtcNow;\n\n // Importante: ¡comparar en la misma zona horaria!\n var timeSpan = now - dateTime;\n\n // Caso 1: fecha futura\n if (timeSpan.TotalSeconds < 0)\n {\n // Calculamos el valor absoluto para el futuro\n timeSpan = timeSpan.Duration();\n if (timeSpan.TotalMinutes < 1)\n return "dentro de unos segundos";\n if (timeSpan.TotalMinutes < 60)\n return $"dentro de {timeSpan.Minutes} min.";\n if (timeSpan.TotalHours < 24)\n return $"dentro de {timeSpan.Hours} h.";\n if (timeSpan.TotalDays < 30)\n return $"dentro de {timeSpan.Days} d.";\n if (timeSpan.TotalDays < 365)\n return $"dentro de {timeSpan.Days / 30} meses.";\n return $"dentro de {timeSpan.Days / 365} años.";\n }\n\n // Caso 2: pasado\n if (timeSpan.TotalMinutes < 1)\n return "justo ahora";\n if (timeSpan.TotalMinutes < 60)\n return $"{timeSpan.Minutes} min. atrás";\n if (timeSpan.TotalHours < 24)\n return $"{timeSpan.Hours} h. atrás";\n if (timeSpan.TotalDays < 7)\n return $"{timeSpan.Days} d. atrás";\n\n // Para fechas de más de una semana, usamos días\n if (timeSpan.TotalDays < 30)\n return $"{(int)timeSpan.TotalDays} d. atrás";\n if (timeSpan.TotalDays < 365)\n {\n int months = (int)(timeSpan.TotalDays / 30);\n return $"{months} meses atrás";\n }\n\n int years = (int)(timeSpan.TotalDays / 365);\n return $"{years} años atrás";