Skip to content

Commit 9c98ce8

Browse files
committedJun 7, 2021
Add support for DateOnly/TimeOnly .Humanize()
1 parent 5ac8201 commit 9c98ce8

14 files changed

+514
-1
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#if NET6_0_OR_GREATER
2+
3+
using System;
4+
using Humanizer.Configuration;
5+
using Humanizer.DateTimeHumanizeStrategy;
6+
using Xunit;
7+
8+
namespace Humanizer.Tests
9+
{
10+
[UseCulture("en-US")]
11+
public class DateOnlyHumanizeTests
12+
{
13+
14+
[Fact]
15+
public void DefaultStrategy_SameDate()
16+
{
17+
Configurator.DateOnlyHumanizeStrategy = new DefaultDateOnlyHumanizeStrategy();
18+
19+
var inputTime = new DateOnly(2015, 07, 05);
20+
var baseTime = new DateOnly(2015, 07, 05);
21+
22+
const string expectedResult = "now";
23+
var actualResult = inputTime.Humanize(baseTime);
24+
25+
Assert.Equal(expectedResult, actualResult);
26+
}
27+
28+
[Fact]
29+
public void DefaultStrategy_MonthApart()
30+
{
31+
Configurator.DateOnlyHumanizeStrategy = new DefaultDateOnlyHumanizeStrategy();
32+
33+
var inputTime = new DateOnly(2015, 08, 05);
34+
var baseTime = new DateOnly(2015, 07, 05);
35+
36+
const string expectedResult = "one month from now";
37+
var actualResult = inputTime.Humanize(baseTime);
38+
39+
Assert.Equal(expectedResult, actualResult);
40+
}
41+
42+
[Fact]
43+
public void DefaultStrategy_DaysAgo()
44+
{
45+
Configurator.DateOnlyHumanizeStrategy = new DefaultDateOnlyHumanizeStrategy();
46+
47+
var inputTime = new DateOnly(2015, 07, 02);
48+
var baseTime = new DateOnly(2015, 07, 05);
49+
50+
const string expectedResult = "3 days ago";
51+
var actualResult = inputTime.Humanize(baseTime);
52+
53+
Assert.Equal(expectedResult, actualResult);
54+
}
55+
56+
[Fact]
57+
public void PrecisionStrategy_NextDay()
58+
{
59+
Configurator.DateOnlyHumanizeStrategy = new PrecisionDateOnlyHumanizeStrategy(0.75);
60+
61+
var inputTime = new DateOnly(2015, 07, 05);
62+
var baseTime = new DateOnly(2015, 07, 04);
63+
64+
const string expectedResult = "tomorrow";
65+
var actualResult = inputTime.Humanize(baseTime);
66+
67+
Assert.Equal(expectedResult, actualResult);
68+
}
69+
70+
71+
[Fact]
72+
public void Never()
73+
{
74+
DateOnly? never = null;
75+
Assert.Equal("never", never.Humanize());
76+
}
77+
78+
[Fact]
79+
public void Nullable_ExpectSame()
80+
{
81+
DateOnly? never = new DateOnly(2015, 12, 7);
82+
83+
Assert.Equal(never.Value.Humanize(), never.Humanize());
84+
}
85+
}
86+
}
87+
88+
#endif

‎src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems

+2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
<Compile Include="$(MSBuildThisFileDirectory)CollectionHumanizeTests.cs" />
2424
<Compile Include="$(MSBuildThisFileDirectory)DateHumanize.cs" />
2525
<Compile Include="$(MSBuildThisFileDirectory)DateHumanizeDefaultStrategyTests.cs" />
26+
<Compile Include="$(MSBuildThisFileDirectory)TimeOnlyHumanizeTests.cs" />
2627
<Compile Include="$(MSBuildThisFileDirectory)DateTimeHumanizePrecisionStrategyTests.cs" />
28+
<Compile Include="$(MSBuildThisFileDirectory)DateOnlyHumanizeTests.cs" />
2729
<Compile Include="$(MSBuildThisFileDirectory)DateTimeOffsetHumanizeTests.cs" />
2830
<Compile Include="$(MSBuildThisFileDirectory)DehumanizeToEnumTests.cs" />
2931
<Compile Include="$(MSBuildThisFileDirectory)EnumHumanizeTests.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#if NET6_0_OR_GREATER
2+
3+
using System;
4+
using Humanizer.Configuration;
5+
using Humanizer.DateTimeHumanizeStrategy;
6+
using Xunit;
7+
8+
namespace Humanizer.Tests
9+
{
10+
[UseCulture("en-US")]
11+
public class TimeOnlyHumanizeTests
12+
{
13+
14+
[Fact]
15+
public void DefaultStrategy_SameTime()
16+
{
17+
Configurator.TimeOnlyHumanizeStrategy = new DefaultTimeOnlyHumanizeStrategy();
18+
19+
var inputTime = new TimeOnly(13, 07, 05);
20+
var baseTime = new TimeOnly(13, 07, 05);
21+
22+
const string expectedResult = "now";
23+
var actualResult = inputTime.Humanize(baseTime);
24+
25+
Assert.Equal(expectedResult, actualResult);
26+
}
27+
28+
[Fact]
29+
public void DefaultStrategy_HoursApart()
30+
{
31+
Configurator.TimeOnlyHumanizeStrategy = new DefaultTimeOnlyHumanizeStrategy();
32+
33+
var inputTime = new TimeOnly(13, 08, 05);
34+
var baseTime = new TimeOnly(1, 08, 05);
35+
36+
const string expectedResult = "12 hours from now";
37+
var actualResult = inputTime.Humanize(baseTime);
38+
39+
Assert.Equal(expectedResult, actualResult);
40+
}
41+
42+
[Fact]
43+
public void DefaultStrategy_HoursAgo()
44+
{
45+
Configurator.TimeOnlyHumanizeStrategy = new DefaultTimeOnlyHumanizeStrategy();
46+
47+
var inputTime = new TimeOnly(13, 07, 02);
48+
var baseTime = new TimeOnly(17, 07, 05);
49+
50+
const string expectedResult = "4 hours ago";
51+
var actualResult = inputTime.Humanize(baseTime);
52+
53+
Assert.Equal(expectedResult, actualResult);
54+
}
55+
56+
[Fact]
57+
public void PrecisionStrategy_NextDay()
58+
{
59+
Configurator.TimeOnlyHumanizeStrategy = new PrecisionTimeOnlyHumanizeStrategy(0.75);
60+
61+
var inputTime = new TimeOnly(18, 10, 49);
62+
var baseTime = new TimeOnly(13, 07, 04);
63+
64+
const string expectedResult = "5 hours from now";
65+
var actualResult = inputTime.Humanize(baseTime);
66+
67+
Assert.Equal(expectedResult, actualResult);
68+
}
69+
70+
71+
[Fact]
72+
public void Never()
73+
{
74+
TimeOnly? never = null;
75+
Assert.Equal("never", never.Humanize());
76+
}
77+
78+
[Fact]
79+
public void Nullable_ExpectSame()
80+
{
81+
TimeOnly? never = new TimeOnly(23, 12, 7);
82+
83+
Assert.Equal(never.Value.Humanize(), never.Humanize());
84+
}
85+
}
86+
}
87+
88+
#endif

‎src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt

+34
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ namespace Humanizer
7575
public static string Humanize(this System.Nullable<System.DateTime> input, System.Nullable<bool> utcDate = null, System.Nullable<System.DateTime> dateToCompareAgainst = null, System.Globalization.CultureInfo culture = null) { }
7676
public static string Humanize(this System.DateTimeOffset input, System.Nullable<System.DateTimeOffset> dateToCompareAgainst = null, System.Globalization.CultureInfo culture = null) { }
7777
public static string Humanize(this System.Nullable<System.DateTimeOffset> input, System.Nullable<System.DateTimeOffset> dateToCompareAgainst = null, System.Globalization.CultureInfo culture = null) { }
78+
public static string Humanize(this System.DateOnly input, System.Nullable<System.DateOnly> dateToCompareAgainst = null, System.Globalization.CultureInfo culture = null) { }
79+
public static string Humanize(this System.Nullable<System.DateOnly> input, System.Nullable<System.DateOnly> dateToCompareAgainst = null, System.Globalization.CultureInfo culture = null) { }
80+
public static string Humanize(this System.TimeOnly input, System.Nullable<System.TimeOnly> timeToCompareAgainst = null, bool useUtc = True, System.Globalization.CultureInfo culture = null) { }
81+
public static string Humanize(this System.Nullable<System.TimeOnly> input, System.Nullable<System.TimeOnly> timeToCompareAgainst = null, bool useUtc = True, System.Globalization.CultureInfo culture = null) { }
7882
}
7983
public class static DateToOrdinalWordsExtensions
8084
{
@@ -1103,13 +1107,15 @@ namespace Humanizer.Configuration
11031107
public class static Configurator
11041108
{
11051109
public static Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.CollectionFormatters.ICollectionFormatter> CollectionFormatters { get; }
1110+
public static Humanizer.DateTimeHumanizeStrategy.IDateOnlyHumanizeStrategy DateOnlyHumanizeStrategy { get; set; }
11061111
public static Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; }
11071112
public static Humanizer.DateTimeHumanizeStrategy.IDateTimeOffsetHumanizeStrategy DateTimeOffsetHumanizeStrategy { get; set; }
11081113
public static Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.DateToOrdinalWords.IDateToOrdinalWordConverter> DateToOrdinalWordsConverters { get; }
11091114
public static System.Func<System.Reflection.PropertyInfo, bool> EnumDescriptionPropertyLocator { get; set; }
11101115
public static Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.Formatters.IFormatter> Formatters { get; }
11111116
public static Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.NumberToWords.INumberToWordsConverter> NumberToWordsConverters { get; }
11121117
public static Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.Ordinalizers.IOrdinalizer> Ordinalizers { get; }
1118+
public static Humanizer.DateTimeHumanizeStrategy.ITimeOnlyHumanizeStrategy TimeOnlyHumanizeStrategy { get; set; }
11131119
}
11141120
public class LocaliserRegistry<TLocaliser>
11151121
where TLocaliser : class
@@ -1124,6 +1130,11 @@ namespace Humanizer.Configuration
11241130
}
11251131
namespace Humanizer.DateTimeHumanizeStrategy
11261132
{
1133+
public class DefaultDateOnlyHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateOnlyHumanizeStrategy
1134+
{
1135+
public DefaultDateOnlyHumanizeStrategy() { }
1136+
public string Humanize(System.DateOnly input, System.DateOnly comparisonBase, System.Globalization.CultureInfo culture) { }
1137+
}
11271138
public class DefaultDateTimeHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy
11281139
{
11291140
public DefaultDateTimeHumanizeStrategy() { }
@@ -1134,6 +1145,15 @@ namespace Humanizer.DateTimeHumanizeStrategy
11341145
public DefaultDateTimeOffsetHumanizeStrategy() { }
11351146
public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { }
11361147
}
1148+
public class DefaultTimeOnlyHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.ITimeOnlyHumanizeStrategy
1149+
{
1150+
public DefaultTimeOnlyHumanizeStrategy() { }
1151+
public string Humanize(System.TimeOnly input, System.TimeOnly comparisonBase, System.Globalization.CultureInfo culture) { }
1152+
}
1153+
public interface IDateOnlyHumanizeStrategy
1154+
{
1155+
string Humanize(System.DateOnly input, System.DateOnly comparisonBase, System.Globalization.CultureInfo culture);
1156+
}
11371157
public interface IDateTimeHumanizeStrategy
11381158
{
11391159
string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture);
@@ -1142,6 +1162,15 @@ namespace Humanizer.DateTimeHumanizeStrategy
11421162
{
11431163
string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture);
11441164
}
1165+
public interface ITimeOnlyHumanizeStrategy
1166+
{
1167+
string Humanize(System.TimeOnly input, System.TimeOnly comparisonBase, System.Globalization.CultureInfo culture);
1168+
}
1169+
public class PrecisionDateOnlyHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateOnlyHumanizeStrategy
1170+
{
1171+
public PrecisionDateOnlyHumanizeStrategy(double precision = 0.75) { }
1172+
public string Humanize(System.DateOnly input, System.DateOnly comparisonBase, System.Globalization.CultureInfo culture) { }
1173+
}
11451174
public class PrecisionDateTimeHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy
11461175
{
11471176
public PrecisionDateTimeHumanizeStrategy(double precision = 0.75) { }
@@ -1152,6 +1181,11 @@ namespace Humanizer.DateTimeHumanizeStrategy
11521181
public PrecisionDateTimeOffsetHumanizeStrategy(double precision = 0.75) { }
11531182
public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { }
11541183
}
1184+
public class PrecisionTimeOnlyHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.ITimeOnlyHumanizeStrategy
1185+
{
1186+
public PrecisionTimeOnlyHumanizeStrategy(double precision = 0.75) { }
1187+
public string Humanize(System.TimeOnly input, System.TimeOnly comparisonBase, System.Globalization.CultureInfo culture) { }
1188+
}
11551189
}
11561190
namespace Humanizer.Inflections
11571191
{

‎src/Humanizer/Configuration/Configurator.cs

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Globalization;
33
using System.Reflection;
44
using Humanizer.DateTimeHumanizeStrategy;
@@ -129,6 +129,28 @@ public static IDateTimeOffsetHumanizeStrategy DateTimeOffsetHumanizeStrategy
129129
set { _dateTimeOffsetHumanizeStrategy = value; }
130130
}
131131

132+
#if NET6_0_OR_GREATER
133+
private static IDateOnlyHumanizeStrategy _dateOnlyHumanizeStrategy = new DefaultDateOnlyHumanizeStrategy();
134+
/// <summary>
135+
/// The strategy to be used for DateOnly.Humanize
136+
/// </summary>
137+
public static IDateOnlyHumanizeStrategy DateOnlyHumanizeStrategy
138+
{
139+
get { return _dateOnlyHumanizeStrategy; }
140+
set { _dateOnlyHumanizeStrategy = value; }
141+
}
142+
143+
private static ITimeOnlyHumanizeStrategy _timeOnlyHumanizeStrategy = new DefaultTimeOnlyHumanizeStrategy();
144+
/// <summary>
145+
/// The strategy to be used for TimeOnly.Humanize
146+
/// </summary>
147+
public static ITimeOnlyHumanizeStrategy TimeOnlyHumanizeStrategy
148+
{
149+
get { return _timeOnlyHumanizeStrategy; }
150+
set { _timeOnlyHumanizeStrategy = value; }
151+
}
152+
#endif
153+
132154
private static readonly Func<PropertyInfo, bool> DefaultEnumDescriptionPropertyLocator = p => p.Name == "Description";
133155
private static Func<PropertyInfo, bool> _enumDescriptionPropertyLocator = DefaultEnumDescriptionPropertyLocator;
134156
/// <summary>

‎src/Humanizer/DateHumanizeExtensions.cs

+71
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,76 @@ public static string Humanize(this DateTimeOffset? input, DateTimeOffset? dateTo
7878
return Configurator.GetFormatter(culture).DateHumanize_Never();
7979
}
8080
}
81+
82+
#if NET6_0_OR_GREATER
83+
/// <summary>
84+
/// Turns the current or provided date into a human readable sentence
85+
/// </summary>
86+
/// <param name="input">The date to be humanized</param>
87+
/// <param name="dateToCompareAgainst">Date to compare the input against. If null, current date is used as base</param>
88+
/// <param name="culture">Culture to use. If null, current thread's UI culture is used.</param>
89+
/// <returns>distance of time in words</returns>
90+
public static string Humanize(this DateOnly input, DateOnly? dateToCompareAgainst = null, CultureInfo culture = null)
91+
{
92+
var comparisonBase = dateToCompareAgainst.HasValue ? dateToCompareAgainst.Value : DateOnly.FromDateTime(DateTime.UtcNow);
93+
94+
return Configurator.DateOnlyHumanizeStrategy.Humanize(input, comparisonBase, culture);
95+
}
96+
97+
/// <summary>
98+
/// Turns the current or provided date into a human readable sentence, overload for the nullable DateTime, returning 'never' in case null
99+
/// </summary>
100+
/// <param name="input">The date to be humanized</param>
101+
/// <param name="dateToCompareAgainst">Date to compare the input against. If null, current date is used as base</param>
102+
/// <param name="culture">Culture to use. If null, current thread's UI culture is used.</param>
103+
/// <returns>distance of time in words</returns>
104+
public static string Humanize(this DateOnly? input, DateOnly? dateToCompareAgainst = null, CultureInfo culture = null)
105+
{
106+
if (input.HasValue)
107+
{
108+
return Humanize(input.Value, dateToCompareAgainst, culture);
109+
}
110+
else
111+
{
112+
return Configurator.GetFormatter(culture).DateHumanize_Never();
113+
}
114+
}
115+
116+
/// <summary>
117+
/// Turns the current or provided time into a human readable sentence
118+
/// </summary>
119+
/// <param name="input">The date to be humanized</param>
120+
/// <param name="useUtc">If <paramref name="dateToCompareAgainst"/> is null, used to determine if the current time is UTC or local. Defaults to UTC.</param>
121+
/// <param name="timeToCompareAgainst">Date to compare the input against. If null, current date is used as base</param>
122+
/// <param name="culture">Culture to use. If null, current thread's UI culture is used.</param>
123+
/// <returns>distance of time in words</returns>
124+
public static string Humanize(this TimeOnly input, TimeOnly? timeToCompareAgainst = null, bool useUtc = true, CultureInfo culture = null)
125+
{
126+
var comparisonBase = timeToCompareAgainst.HasValue ? timeToCompareAgainst.Value : TimeOnly.FromDateTime(useUtc ? DateTime.UtcNow : DateTime.Now);
127+
128+
return Configurator.TimeOnlyHumanizeStrategy.Humanize(input, comparisonBase, culture);
129+
}
130+
131+
/// <summary>
132+
/// Turns the current or provided time into a human readable sentence, overload for the nullable TimeOnly, returning 'never' in case null
133+
/// </summary>
134+
/// <param name="input">The date to be humanized</param>
135+
/// <param name="useUtc">If <paramref name="dateToCompareAgainst"/> is null, used to determine if the current time is UTC or local. Defaults to UTC.</param>
136+
/// <param name="timeToCompareAgainst">Time to compare the input against. If null, current date is used as base</param>
137+
/// <param name="culture">Culture to use. If null, current thread's UI culture is used.</param>
138+
/// <returns>distance of time in words</returns>
139+
public static string Humanize(this TimeOnly? input, TimeOnly? timeToCompareAgainst = null, bool useUtc = true, CultureInfo culture = null)
140+
{
141+
if (input.HasValue)
142+
{
143+
return Humanize(input.Value, timeToCompareAgainst, useUtc, culture);
144+
}
145+
else
146+
{
147+
return Configurator.GetFormatter(culture).DateHumanize_Never();
148+
}
149+
}
150+
151+
#endif
81152
}
82153
}

0 commit comments

Comments
 (0)