Skip to content

Instantly share code, notes, and snippets.

@michaelvolz
Last active November 6, 2018 13:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michaelvolz/eaaf4033e6167aa42182f964bbd52574 to your computer and use it in GitHub Desktop.
Save michaelvolz/eaaf4033e6167aa42182f964bbd52574 to your computer and use it in GitHub Desktop.
public static class DateTimeExtensions
{
private const int Today = 0;
private const int Yesterday = 1;
private const int Week = 7;
private const int Month = 31;
private const int OneMinute = 60;
private const int OneHour = 3600;
private const int OneDay = 86400;
public static string ToFriendlyDate(this DateTime? date)
{
return date.HasValue ? date.Value.ToFriendlyDate() : string.Empty;
}
public static string ToFriendlyDate(this DateTime date)
{
var elapsedTime = DateTime.UtcNow.Subtract(date);
var totalDaysElapsed = (int) elapsedTime.TotalDays;
var totalSecondsElapsed = (int) elapsedTime.TotalSeconds;
switch (totalDaysElapsed)
{
case int _ when totalSecondsElapsed < 0 : return "n/a";
case Today when totalSecondsElapsed < OneMinute: return "just now";
case Today when totalSecondsElapsed < OneHour:
return string.Format(new PluralFormatProvider(), "{0:minute;minutes} ago",
totalSecondsElapsed.FloorBy(OneMinute));
case Today when totalSecondsElapsed < OneDay:
return string.Format(new PluralFormatProvider(), "{0:hour;hours} ago",
totalSecondsElapsed.FloorBy(OneHour));
case Yesterday: return "yesterday";
case int days when days < Week: return $"{totalDaysElapsed} days ago";
case int days when days < Month:
return string.Format(new PluralFormatProvider(), "{0:week;weeks} ago",
totalDaysElapsed.CeilingBy(Week));
default:
return date.ToString(CultureInfo.InvariantCulture);
}
}
private static double CeilingBy(this int dividend, int divisor) => Math.Ceiling((double) dividend / divisor);
private static double FloorBy(this int dividend, int divisor) => Math.Floor((double) dividend / divisor);
private class PluralFormatProvider : IFormatProvider, ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider formatProvider) =>
$"{Convert.ToInt32(arg)} {format.Split(';')[Convert.ToInt32(arg) == 1 ? 0 : 1]}";
public object GetFormat(Type formatType) => this;
}
}
---
using System;
using FluentAssertions;
using Xunit;
public class DateTimeExtensionsTests
{
public static object[][] TestData => new[]
{
new object[] {DateTime.UtcNow, "just now"},
new object[] {DateTime.UtcNow.AddMinutes(-1), "1 minute ago"},
new object[] {DateTime.UtcNow.AddMinutes(-2), "2 minutes ago"},
new object[] {DateTime.UtcNow.AddMinutes(-60), "1 hour ago"},
new object[] {DateTime.UtcNow.AddMinutes(-120), "2 hours ago"},
new object[] {DateTime.UtcNow.AddDays(-1), "yesterday"},
new object[] {DateTime.UtcNow.AddDays(-2), "2 days ago"},
new object[] {DateTime.UtcNow.AddDays(-7), "1 week ago"},
new object[] {DateTime.UtcNow.AddDays(-30), "5 weeks ago"}
};
[Theory]
[MemberData(nameof(TestData))]
public void ToFriendlyDate_ValidDates(DateTime date, string expected)
{
var result = date.ToFriendlyDate();
result.Should().Be(expected);
}
[Fact]
public void ToFriendlyDate_FutureDate_Invalid()
{
var result = DateTime.UtcNow.AddMinutes(5).ToFriendlyDate();
result.Should().Be("n/a");
}
[Fact]
public void ToFriendlyDate_OldDate()
{
var date = DateTime.UtcNow.AddMonths(-3);
var result = date.ToFriendlyDate();
result.Should().Be(date.ToString(CultureInfo.InvariantCulture));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment