Как вычислить относительное время в C# (типа "5 минут назад", "вчера")?
Решение: универсальный метод для вычисления относительного времени на C#
Ваша интуиция верна — писать кучу if-else не нужно. В C# можно создать компактный и гибкий метод, который учитывает склонения слов на русском языке и правильно обрабатывает граничные случаи (только что, вчера, неделю назад и т.д.). Ниже представлено готовое решение с подробными комментариями.
public static class TimeHelper
{
public static string GetRelativeTime(DateTime eventTime)
{
var now = DateTime.Now;
var diff = now - eventTime;
// Меньше минуты
if (diff.TotalSeconds < 60)
{
return "только что";
}
// Меньше часа
if (diff.TotalMinutes < 60)
{
int minutes = (int)diff.TotalMinutes;
return $"{minutes} {DeclineWord(minutes, new[] { "минуту", "минуты", "минут" })} назад";
}
// Меньше суток
if (diff.TotalHours < 24)
{
int hours = (int)diff.TotalHours;
return $"{hours} {DeclineWord(hours, new[] { "час", "часа", "часов" })} назад";
}
// Меньше двух дней — используем "вчера"
if (diff.TotalDays < 2)
{
return "вчера";
}
// Меньше 30 дней
if (diff.TotalDays < 30)
{
int days = (int)diff.TotalDays;
return $"{days} {DeclineWord(days, new[] { "день", "дня", "дней" })} назад";
}
// Меньше 12 месяцев
if (diff.TotalDays < 365)
{
int months = (int)(diff.TotalDays / 30);
return $"{months} {DeclineWord(months, new[] { "месяц", "месяца", "месяцев" })} назад";
}
// Больше года — показываем полную дату
return eventTime.ToString("dd MMMM yyyy");
}
// Вспомогательный метод для склонения слов (1 минута, 2 минуты, 5 минут)
private static string DeclineWord(int number, string[] forms)
{
// forms[0] — единственное число (1 минута)
// forms[1] — множественное для 2-4 (2 минуты)
// forms[2] — множественное для 5+ (5 минут)
int n = Math.Abs(number) % 100;
int n1 = n % 10;
if (n > 10 && n < 20) return forms[2];
if (n1 > 1 && n1 < 5) return forms[1];
if (n1 == 1) return forms[0];
return forms[2];
}
}
Пример использования
DateTime testTime = DateTime.Now.AddMinutes(-5);
string result = TimeHelper.GetRelativeTime(testTime);
Console.WriteLine(result); // Выведет: "5 минут назад"
Почему это решение хорошее?
- Компактность: весь код помещается в один статический класс.
- Правильные склонения: метод
DeclineWordкорректно обрабатывает русские числительные (1 минута, 2 минуты, 5 минут, 21 минута). - Граничные случаи: учтены "только что", "вчера", а для дат старше года показывается полная дата.
- Производительность: нет циклов или сложных вычислений, только простые арифметические операции.
- Гибкость: вы легко можете изменить пороговые значения (например, заменить 30 дней на 31) или добавить новые варианты (например, "на прошлой неделе").
Важные замечания
- Метод использует
DateTime.Now, что даёт локальное время. Если нужно UTC — замените наDateTime.UtcNow. - Для точности в промышленных приложениях рекомендуется использовать
DateTimeOffsetвместоDateTime, чтобы избежать проблем с часовыми поясами. - Вы можете адаптировать метод для английского языка, просто заменив массив форм и строки-константы.
Альтернативный подход: использование сторонней библиотеки Humanizer
Если вы не хотите писать свою реализацию склонений и обработки границ, рекомендую воспользоваться популярной NuGet-библиотекой Humanizer. Она специализируется именно на "очеловечивании" данных, включая относительное время.
// Установите пакет: Install-Package Humanizer
using Humanizer;
using Humanizer.Localisation;
DateTime eventTime = DateTime.Now.AddHours(-3);
string result = eventTime.Humanize(); // "3 часа назад"
Преимущества Humanizer
- Поддержка русского языка: библиотека имеет встроенные локализации, включая русскую.
- Гибкость: можно указать точность (например, только до минут) и стиль (краткий или полный).
- Дополнительные функции: Humanizer умеет "очеловечивать" не только даты, но и числа, байты, перечисления и многое другое.
Пример с настройками
// Только до минут, без секунд
string result = eventTime.Humanize(precision: Humanizer.Localisation.TimeUnit.Minute, culture: new CultureInfo("ru-RU"));
Недостаток: библиотека добавляет зависимость в проект. Если ваше приложение небольшое и не требует других функций Humanizer, то проще написать свой метод, как в первом ответе. Но для крупных проектов с множеством локализаций Humanizer — отличный выбор.