Introduction: Why Do We Need "Relative Time"?
\n\nHave you ever wondered why social networks and news sites write "2 hours ago" or "yesterday" instead of "04/25/2025 14:32"? Because our brain perceives relative time much faster. An absolute date requires analysis: "Okay, today is the 25th, so that was...". A relative date gives an answer instantly.
\n\nIf you are writing in C# — whether it's an ASP.NET web application, a desktop application, or a Telegram bot — sooner or later you will need to show the user time in the format "just now", "3 minutes ago", or "about a year ago". On Stack Overflow, the question "Calculate relative time in C#" has garnered thousands of views, meaning it's a real pain point for developers.
\n\nIn this article, we will break down the most correct and flexible way to calculate relative time in C#. You will get ready-to-use code that you can immediately insert into your project, understand how it works, and learn how to customize it for any need.
\n\nThe Problem: What's Wrong with Simple Date Subtraction?
\n\nThe first thing that comes to mind is subtracting one date from another and getting a TimeSpan. Yes, it works. But the devil is in the details:
- \n
- Threshold values. When does a "minute" end and an "hour" begin? And when does an "hour" transition into a "day"? \n
- Future dates. What if the date is in the future? Should it show "in 5 minutes" or "in 1 day"? \n
- Code readability. If you scatter the logic throughout the project, you'll confuse yourself in a month. \n
Therefore, we need not just a piece of code, but a single, reusable method.
\n\nThe Solution: A Universal Method for Calculating Relative Time
\n\nThe best way is to write one static method that takes a DateTime (or DateTimeOffset) and returns a nice string. Here is a basic, yet already very powerful implementation that can be found in discussions on Stack Overflow and adapted to your needs:
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 // Important: compare in the same time zone!\n var timeSpan = now - dateTime;\n\n // Case 1: date in the future\n if (timeSpan.TotalSeconds < 0)\n {\n // Calculate absolute value for the future\n timeSpan = timeSpan.Duration();\n if (timeSpan.TotalMinutes < 1)\n return "in a few seconds";\n if (timeSpan.TotalMinutes < 60)\n return $"in {timeSpan.Minutes} min.";\n if (timeSpan.TotalHours < 24)\n return $"in {timeSpan.Hours} h.";\n if (timeSpan.TotalDays < 30)\n return $"in {timeSpan.Days} d.";\n if (timeSpan.TotalDays < 365)\n return $"in {timeSpan.Days / 30} mo.";\n return $"in {timeSpan.Days / 365} y.";\n }\n\n // Case 2: past\n if (timeSpan.TotalMinutes < 1)\n return "just now";\n if (timeSpan.TotalMinutes < 60)\n return $"{timeSpan.Minutes} min. ago";\n if (timeSpan.TotalHours < 24)\n return $"{timeSpan.Hours} h. ago";\n if (timeSpan.TotalDays < 7)\n return $"{timeSpan.Days} d. ago";\n\n // For dates older than a week, use days\n if (timeSpan.TotalDays < 30)\n return $"{(int)timeSpan.TotalDays} d. ago";\n if (timeSpan.TotalDays < 365)\n {\n int months = (int)(timeSpan.TotalDays / 30);\n return $"{months} mo. ago";\n }\n\n int years = (int)(timeSpan.TotalDays / 365);\n return $"{years} y