Skip to content

Commit 6ee4874

Browse files
committed
Refactoring Session #1. This is an incomplete work. It seems like a big refactor & I think it requires approval for the idea before proceeding.
1 parent 7c3b2c8 commit 6ee4874

16 files changed

+741
-6
lines changed

src/Humanizer.Tests/DateHumanizeTests.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ public class DateHumanizeTests
88
{
99
static void VerifyWithCurrentDate(string expectedString, TimeSpan deltaFromNow)
1010
{
11-
var utcNow = DateTime.UtcNow;
12-
var localNow = DateTime.Now;
13-
14-
Assert.Equal(expectedString, utcNow.Add(deltaFromNow).Humanize());
15-
Assert.Equal(expectedString, localNow.Add(deltaFromNow).Humanize(false));
11+
Assert.Equal(expectedString, DateTime.UtcNow.Add(deltaFromNow).Humanize());
12+
Assert.Equal(expectedString, DateTime.Now.Add(deltaFromNow).Humanize(false));
1613
}
1714

1815
static void VerifyWithDateInjection(string expectedString, TimeSpan deltaFromNow)

src/Humanizer.Tests/Humanizer.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
</ItemGroup>
7272
<ItemGroup>
7373
<Compile Include="CasingTests.cs" />
74+
<Compile Include="Localisation\DynamicResourceKeys\DateHumanizeTests.cs" />
7475
<Compile Include="Localisation\ar\DateHumanizeTests.cs" />
7576
<Compile Include="Localisation\ar\NumberToWordsTests.cs" />
7677
<Compile Include="Localisation\ar\TimeSpanHumanizeTests.cs" />
@@ -79,6 +80,7 @@
7980
<Compile Include="Localisation\es\TimeSpanHumanizeTests.cs" />
8081
<Compile Include="Localisation\pt-BR\DateHumanizeTests.cs" />
8182
<Compile Include="Localisation\pt-BR\TimeSpanHumanizeTests.cs" />
83+
<Compile Include="Localisation\DynamicResourceKeys\ResourceKeyTests.cs" />
8284
<Compile Include="RomanNumeralTests.cs" />
8385
<Compile Include="RunnableInDebugModeOnlyAttribute.cs" />
8486
<Compile Include="ToQuantityTests.cs" />
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Humanizer.Localisation.DynamicResourceKeys;
4+
using Xunit;
5+
using Xunit.Extensions;
6+
using Resources = Humanizer.Localisation.Resources;
7+
using ResourceKeys = Humanizer.Localisation.DynamicResourceKeys.ResourceKeys;
8+
using Dyna = Humanizer.DynamicResourceKeys.DateHumanizeExtensions;
9+
10+
namespace Humanizer.Tests.Localisation.DynamicResourceKeys
11+
{
12+
public class DateHumanizeWithResourceKeysTests
13+
{
14+
static void VerifyWithCurrentDate(string expectedString, TimeSpan deltaFromNow)
15+
{
16+
Assert.Equal(expectedString, Dyna.Humanize(DateTime.UtcNow.Add(deltaFromNow)));
17+
Assert.Equal(expectedString, Dyna.Humanize(DateTime.Now.Add(deltaFromNow), false));
18+
}
19+
20+
static void VerifyWithDateInjection(string expectedString, TimeSpan deltaFromNow)
21+
{
22+
var utcNow = new DateTime(2013, 6, 20, 9, 58, 22, DateTimeKind.Utc);
23+
var now = new DateTime(2013, 6, 20, 11, 58, 22, DateTimeKind.Local);
24+
25+
Assert.Equal(expectedString, Dyna.Humanize(utcNow.Add(deltaFromNow), true, utcNow));
26+
Assert.Equal(expectedString, Dyna.Humanize(now.Add(deltaFromNow), false, now));
27+
}
28+
29+
static void Verify(string expectedString, TimeSpan deltaFromNow)
30+
{
31+
VerifyWithCurrentDate(expectedString, deltaFromNow);
32+
VerifyWithDateInjection(expectedString, deltaFromNow);
33+
}
34+
35+
public static IEnumerable<object[]> OneTimeUnitAgoTestsSource
36+
{
37+
get
38+
{
39+
return new[] {
40+
new object[]{ TimeUnit.Second, TimeSpan.FromSeconds(-1) },
41+
new object[]{ TimeUnit.Minute, TimeSpan.FromMinutes(-1) },
42+
new object[]{ TimeUnit.Hour, TimeSpan.FromHours(-1) },
43+
new object[]{ TimeUnit.Day, TimeSpan.FromDays(-1) },
44+
new object[]{ TimeUnit.Month, TimeSpan.FromDays(-30) },
45+
new object[]{ TimeUnit.Year, TimeSpan.FromDays(-365) },
46+
};
47+
}
48+
}
49+
50+
[Theory]
51+
[PropertyData("OneTimeUnitAgoTestsSource")]
52+
public void OneTimeUnitAgo(TimeUnit unit, TimeSpan timeSpan)
53+
{
54+
Verify(Resources.GetResource(ResourceKeys.DateHumanize.GetResourceKey(unit, 1)), timeSpan);
55+
}
56+
}
57+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System.Collections.Generic;
2+
using Humanizer.Localisation.DynamicResourceKeys;
3+
using Xunit;
4+
using Xunit.Extensions;
5+
using Resources = Humanizer.Localisation.Resources;
6+
7+
namespace Humanizer.Tests.Localisation.DynamicResourceKeys
8+
{
9+
public class ResourceKeyTests
10+
{
11+
[Theory]
12+
[PropertyData("DateHumanizeResourceKeys")]
13+
public void DateHumanizeKeysGeneration(string expected, string actual)
14+
{
15+
Assert.Equal(expected, actual);
16+
}
17+
18+
[Theory]
19+
[PropertyData("TimeSpanHumanizeResourceKeys")]
20+
public void TimeSpanHumanizeKeysGeneration(string expected, string actual)
21+
{
22+
Assert.Equal(expected, actual);
23+
}
24+
25+
[Theory]
26+
[PropertyData("DateHumanizeResourceKeys")]
27+
public void DateHumanizeKeysExistence(string expectedResourceKey, string generatedResourceKey)
28+
{
29+
Assert.NotNull(Resources.GetResource(generatedResourceKey));
30+
}
31+
32+
[Theory]
33+
[PropertyData("TimeSpanHumanizeResourceKeys")]
34+
public void TimeSpanHumanizeKeysExistence(string expectedResourceKey, string generatedResourceKey)
35+
{
36+
Assert.NotNull(Resources.GetResource(generatedResourceKey));
37+
}
38+
39+
public static IEnumerable<object[]> DateHumanizeResourceKeys
40+
{
41+
get
42+
{
43+
return new[] {
44+
new object[]{ "DateHumanize_SingleSecondAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Second, 1) },
45+
new object[]{ "DateHumanize_SingleMinuteAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Minute, 1) },
46+
new object[]{ "DateHumanize_SingleHourAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Hour, 1) },
47+
new object[]{ "DateHumanize_SingleDayAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Day, 1) },
48+
new object[]{ "DateHumanize_SingleMonthAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Month, 1) },
49+
new object[]{ "DateHumanize_SingleYearAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Year, 1) },
50+
new object[]{ "DateHumanize_MultipleSecondsAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Second, 10) },
51+
new object[]{ "DateHumanize_MultipleMinutesAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Minute, 10) },
52+
new object[]{ "DateHumanize_MultipleHoursAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Hour, 10) },
53+
new object[]{ "DateHumanize_MultipleDaysAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Day, 10) },
54+
new object[]{ "DateHumanize_MultipleMonthsAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Month, 10) },
55+
new object[]{ "DateHumanize_MultipleYearsAgo", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Year, 10) },
56+
57+
new object[]{ "DateHumanize_SingleSecondFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Second, 1, true) },
58+
new object[]{ "DateHumanize_SingleMinuteFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Minute, 1, true) },
59+
new object[]{ "DateHumanize_SingleHourFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Hour, 1, true) },
60+
new object[]{ "DateHumanize_SingleDayFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Day, 1, true) },
61+
new object[]{ "DateHumanize_SingleMonthFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Month, 1, true) },
62+
new object[]{ "DateHumanize_SingleYearFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Year, 1, true) },
63+
new object[]{ "DateHumanize_MultipleSecondsFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Second, 10, true) },
64+
new object[]{ "DateHumanize_MultipleMinutesFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Minute, 10, true) },
65+
new object[]{ "DateHumanize_MultipleHoursFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Hour, 10, true) },
66+
new object[]{ "DateHumanize_MultipleDaysFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Day, 10, true) },
67+
new object[]{ "DateHumanize_MultipleMonthsFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Month, 10, true) },
68+
new object[]{ "DateHumanize_MultipleYearsFromNow", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Year, 10, true) },
69+
70+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Millisecond, 0) },
71+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Second, 0) },
72+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Minute, 0) },
73+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Hour, 0) },
74+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Day, 0) },
75+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Week, 0) },
76+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Month, 0) },
77+
new object[]{ "DateHumanize_Now", ResourceKeys.DateHumanize.GetResourceKey(TimeUnit.Year, 0) }
78+
};
79+
}
80+
}
81+
82+
public static IEnumerable<object[]> TimeSpanHumanizeResourceKeys
83+
{
84+
get
85+
{
86+
return new[] {
87+
new object[]{ "TimeSpanHumanize_SingleSecond", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Second, 1) },
88+
new object[]{ "TimeSpanHumanize_SingleMinute", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Minute, 1) },
89+
new object[]{ "TimeSpanHumanize_SingleHour", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Hour, 1) },
90+
new object[]{ "TimeSpanHumanize_SingleDay", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Day, 1) },
91+
new object[]{ "TimeSpanHumanize_SingleWeek", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Week, 1) },
92+
new object[]{ "TimeSpanHumanize_MultipleSeconds", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Second, 10) },
93+
new object[]{ "TimeSpanHumanize_MultipleMinutes", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Minute, 10) },
94+
new object[]{ "TimeSpanHumanize_MultipleHours", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Hour, 10) },
95+
new object[]{ "TimeSpanHumanize_MultipleDays", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Day, 10) },
96+
new object[]{ "TimeSpanHumanize_MultipleWeeks", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Week, 10) },
97+
98+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Millisecond, 0) },
99+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Second, 0) },
100+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Minute, 0) },
101+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Hour, 0) },
102+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Day, 0) },
103+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Week, 0) },
104+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Month, 0) },
105+
new object[]{ "TimeSpanHumanize_Zero", ResourceKeys.TimeSpanHumanize.GetResourceKey(TimeUnit.Year, 0) }
106+
};
107+
}
108+
}
109+
}
110+
}

src/Humanizer/Configuration/Configurator.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Globalization;
44
using Humanizer.Localisation;
5+
using Dyna = Humanizer.Localisation.DynamicResourceKeys;
56

67
namespace Humanizer.Configuration
78
{
@@ -33,5 +34,16 @@ public static IFormatter Formatter
3334
return new DefaultFormatter();
3435
}
3536
}
37+
38+
/// <summary>
39+
/// Providers similar functionality for the DefaultFormatter except the localization, at the moment.
40+
/// </summary>
41+
public static Dyna.IFormatter DynamicFormatter
42+
{
43+
get
44+
{
45+
return new Dyna.DefaultFormatter();
46+
}
47+
}
3648
}
3749
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using Humanizer.Configuration;
3+
4+
namespace Humanizer.DynamicResourceKeys
5+
{
6+
/// <summary>
7+
/// Humanizes DateTime into human readable sentence
8+
/// </summary>
9+
public static class DateHumanizeExtensions
10+
{
11+
// http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time
12+
/// <summary>
13+
/// Turns the current or provided date into a human readable sentence
14+
/// </summary>
15+
/// <param name="input">The date to be humanized</param>
16+
/// <param name="utcDate">Boolean value indicating whether the date is in UTC or local</param>
17+
/// <param name="dateToCompareAgainst">Date to compare the input against. If null, current date is used as base</param>
18+
/// <returns></returns>
19+
public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null)
20+
{
21+
if (dateToCompareAgainst == null)
22+
dateToCompareAgainst = DateTime.UtcNow;
23+
24+
var formatter = Configurator.DynamicFormatter;
25+
var comparisonBase = dateToCompareAgainst.Value;
26+
27+
if (!utcDate)
28+
comparisonBase = comparisonBase.ToLocalTime();
29+
30+
if (input <= comparisonBase && comparisonBase.Subtract(input) < TimeSpan.FromMilliseconds(500))
31+
return formatter.DateHumanize_Now();
32+
33+
var isFuture = input > comparisonBase;
34+
var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks));
35+
36+
if (ts.TotalSeconds < 60)
37+
return formatter.DateHumanize_Seconds(ts.Seconds, isFuture);
38+
39+
if (ts.TotalSeconds < 120)
40+
return formatter.DateHumanize_Minutes(1, isFuture);
41+
42+
if (ts.TotalMinutes < 45)
43+
return formatter.DateHumanize_Minutes(ts.Minutes, isFuture);
44+
45+
if (ts.TotalMinutes < 90)
46+
return formatter.DateHumanize_Hours(1, isFuture);
47+
48+
if (ts.TotalHours < 24)
49+
return formatter.DateHumanize_Hours(ts.Hours, isFuture);
50+
51+
if (ts.TotalHours < 48)
52+
return formatter.DateHumanize_Days(1, isFuture);
53+
54+
if (ts.TotalDays < 28)
55+
return formatter.DateHumanize_Days(ts.Days, isFuture);
56+
57+
if (ts.TotalDays >= 28 && ts.TotalDays < 30)
58+
{
59+
if (comparisonBase.Date.AddMonths(isFuture ? 1 : -1) == input.Date)
60+
return formatter.DateHumanize_Months(1, isFuture);
61+
62+
return formatter.DateHumanize_Days(ts.Days, isFuture);
63+
}
64+
65+
if (ts.TotalDays < 345)
66+
{
67+
int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5));
68+
return formatter.DateHumanize_Months(months, isFuture);
69+
}
70+
71+
int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365));
72+
if (years == 0) years = 1;
73+
return formatter.DateHumanize_Years(years, isFuture);
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)