From 647e38250370ef8c815e0940e5b896ca38ad7227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pa=C5=BEourek?= Date: Fri, 27 Mar 2015 12:57:42 +0100 Subject: [PATCH 1/4] Initial implementation for humanizing DateTimeOffset. --- readme.md | 10 ++- ...provalTest.approve_public_api.approved.txt | 9 +++ .../DateTimeOffsetHumanizeTests.cs | 73 +++++++++++++++++++ src/Humanizer.Tests/Humanizer.Tests.csproj | 1 + src/Humanizer/Configuration/Configurator.cs | 10 +++ src/Humanizer/DateHumanizeExtensions.cs | 14 ++++ .../DefaultDateTimeHumanizeStrategy.cs | 14 +++- .../IDateTimeOffsetHumanizeStrategy.cs | 16 ++++ .../PrecisionDateTimeHumanizeStrategy.cs | 14 +++- src/Humanizer/Humanizer.csproj | 1 + 10 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs create mode 100644 src/Humanizer/DateTimeHumanizeStrategy/IDateTimeOffsetHumanizeStrategy.cs diff --git a/readme.md b/readme.md index e96a64159..e4fc4a661 100644 --- a/readme.md +++ b/readme.md @@ -263,7 +263,7 @@ By default both methods throw a `NoMatchFoundException` when they cannot match t In the non-generic method you can also ask the method to return null by setting the second optional parameter to `NoMatch.ReturnsNull`. ###Humanize DateTime -You can `Humanize` an instance of `DateTime` and get back a string telling how far back or forward in time that is: +You can `Humanize` an instance of `DateTime` or `DateTimeOffset` and get back a string telling how far back or forward in time that is: ```C# DateTime.UtcNow.AddHours(-30).Humanize() => "yesterday" @@ -273,12 +273,13 @@ DateTime.UtcNow.AddHours(30).Humanize() => "tomorrow" DateTime.UtcNow.AddHours(2).Humanize() => "2 hours from now" ``` -Humanizer supports local as well as UTC dates. You could also provide the date you want the input date to be compared against. If null, it will use the current date as comparison base. +Humanizer supports both local and UTC dates as well as dates with offset (`DateTimeOffset`). You could also provide the date you want the input date to be compared against. If null, it will use the current date as comparison base. Also, culture to use can be specified explicitly. If it is not, current thread's current UI culture is used. Here is the API signature: ```C# public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) +public static string Humanize(this DateTimeOffset input, DateTimeOffset? dateToCompareAgainst = null, CultureInfo culture = null) ``` Many localizations are available for this method. Here is a few examples: @@ -302,7 +303,10 @@ DateTime.UtcNow.AddMinutes(-40).Humanize() => "40 минут назад" There are two strategies for `DateTime.Humanize`: the default one as seen above and a precision based one. To use the precision based strategy you need to configure it: -`Configurator.DateTimeHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision = .75)`. +```C# +Configurator.DateTimeHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision = .75); +Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision = .75); // configure when humanizing DateTimeOffset +``` The default precision is set to .75 but you can pass your desired precision too. With precision set to 0.75: diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt index 17307cfd5..b6ec8d2b9 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -125,6 +125,7 @@ public class Configurator { public Humanizer.Configuration.LocaliserRegistry CollectionFormatters { get; } public Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; } + public Humanizer.DateTimeHumanizeStrategy.IDateTimeOffsetHumanizeStrategy DateTimeOffsetHumanizeStrategy { get; set; } public System.Func EnumDescriptionPropertyLocator { get; set; } public Humanizer.Configuration.LocaliserRegistry Formatters { get; } public Humanizer.Configuration.LocaliserRegistry NumberToWordsConverters { get; } @@ -144,12 +145,14 @@ public class LocaliserRegistry`1 public class DateHumanizeExtensions { public string Humanize(System.DateTime input, bool utcDate, System.Nullable dateToCompareAgainst, System.Globalization.CultureInfo culture) { } + public string Humanize(System.DateTimeOffset input, System.Nullable dateToCompareAgainst, System.Globalization.CultureInfo culture) { } } public class DefaultDateTimeHumanizeStrategy { public DefaultDateTimeHumanizeStrategy() { } public string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture) { } + public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { } } public interface IDateTimeHumanizeStrategy @@ -157,10 +160,16 @@ public interface IDateTimeHumanizeStrategy string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture); } +public interface IDateTimeOffsetHumanizeStrategy +{ + string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture); +} + public class PrecisionDateTimeHumanizeStrategy { public PrecisionDateTimeHumanizeStrategy(double precision) { } public string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture) { } + public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { } } public class EnumDehumanizeExtensions diff --git a/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs b/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs new file mode 100644 index 000000000..839a759a5 --- /dev/null +++ b/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Humanizer.Configuration; +using Humanizer.DateTimeHumanizeStrategy; +using Xunit; + +namespace Humanizer.Tests +{ + public class DateTimeOffsetHumanizeTests : AmbientCulture + { + public DateTimeOffsetHumanizeTests() : base("en-US") + { + } + + [Fact] + public void DefaultStrategy_SameOffset() + { + Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + + var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); + var baseTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, TimeSpan.Zero); + + const string expectedResult = "an hour from now"; + var actualResult = inputTime.Humanize(baseTime); + + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void DefaultStrategy_DifferentOffsets() + { + Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + + var inputTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, new TimeSpan(2, 0, 0)); + var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(1, 0, 0)); + + const string expectedResult = "30 minutes ago"; + var actualResult = inputTime.Humanize(baseTime); + + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void PrecisionStrategy_SameOffset() + { + Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(0.75); + + var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); + var baseTime = new DateTimeOffset(2015, 07, 04, 05, 0, 0, TimeSpan.Zero); + + const string expectedResult = "tomorrow"; + var actualResult = inputTime.Humanize(baseTime); + + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void PrecisionStrategy_DifferentOffsets() + { + Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(0.75); + + var inputTime = new DateTimeOffset(2015, 07, 05, 03, 45, 0, new TimeSpan(2, 0, 0)); + var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(-5, 0, 0)); + + const string expectedResult = "6 hours ago"; + var actualResult = inputTime.Humanize(baseTime); + + Assert.Equal(expectedResult, actualResult); + } + } +} \ No newline at end of file diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 556419e99..2e9aa81c0 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -67,6 +67,7 @@ + diff --git a/src/Humanizer/Configuration/Configurator.cs b/src/Humanizer/Configuration/Configurator.cs index c3605537b..a72e4a969 100644 --- a/src/Humanizer/Configuration/Configurator.cs +++ b/src/Humanizer/Configuration/Configurator.cs @@ -99,6 +99,16 @@ public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy set { _dateTimeHumanizeStrategy = value; } } + private static IDateTimeOffsetHumanizeStrategy _dateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + /// + /// The strategy to be used for DateTimeOffset.Humanize + /// + public static IDateTimeOffsetHumanizeStrategy DateTimeOffsetHumanizeStrategy + { + get { return _dateTimeOffsetHumanizeStrategy; } + set { _dateTimeOffsetHumanizeStrategy = value; } + } + private static readonly Func DefaultEnumDescriptionPropertyLocator = p => p.Name == "Description"; private static Func _enumDescriptionPropertyLocator = DefaultEnumDescriptionPropertyLocator; /// diff --git a/src/Humanizer/DateHumanizeExtensions.cs b/src/Humanizer/DateHumanizeExtensions.cs index 70874e073..9a6b0b0c0 100644 --- a/src/Humanizer/DateHumanizeExtensions.cs +++ b/src/Humanizer/DateHumanizeExtensions.cs @@ -26,5 +26,19 @@ public static string Humanize(this DateTime input, bool utcDate = true, DateTime return Configurator.DateTimeHumanizeStrategy.Humanize(input, comparisonBase, culture); } + + /// + /// Turns the current or provided date into a human readable sentence + /// + /// The date to be humanized + /// Date to compare the input against. If null, current date is used as base + /// Culture to use. If null, current thread's UI culture is used. + /// distance of time in words + public static string Humanize(this DateTimeOffset input, DateTimeOffset? dateToCompareAgainst = null, CultureInfo culture = null) + { + var comparisonBase = dateToCompareAgainst ?? DateTimeOffset.UtcNow; + + return Configurator.DateTimeOffsetHumanizeStrategy.Humanize(input, comparisonBase, culture); + } } } \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs index 876cf7f9b..78c23d584 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs @@ -8,7 +8,7 @@ namespace Humanizer.DateTimeHumanizeStrategy /// /// The default 'distance of time' -> words calculator. /// - public class DefaultDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy + public class DefaultDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy, IDateTimeOffsetHumanizeStrategy { // http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time /// @@ -70,5 +70,17 @@ public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo cult return formatter.DateHumanize(TimeUnit.Year, tense, years); } + + /// + /// Calculates the distance of time in words between two provided dates + /// + /// + /// + /// + /// + public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) + { + return Humanize(input.UtcDateTime, comparisonBase.UtcDateTime, culture); + } } } \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeOffsetHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeOffsetHumanizeStrategy.cs new file mode 100644 index 000000000..0cde54186 --- /dev/null +++ b/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeOffsetHumanizeStrategy.cs @@ -0,0 +1,16 @@ +using System; +using System.Globalization; + +namespace Humanizer.DateTimeHumanizeStrategy +{ + /// + /// Implement this interface to create a new strategy for DateTime.Humanize and hook it in the Configurator.DateTimeOffsetHumanizeStrategy + /// + public interface IDateTimeOffsetHumanizeStrategy + { + /// + /// Calculates the distance of time in words between two provided dates used for DateTimeOffset.Humanize + /// + string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture); + } +} \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs index 64e9ba567..a1b56df61 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs @@ -8,7 +8,7 @@ namespace Humanizer.DateTimeHumanizeStrategy /// /// /// - public class PrecisionDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy + public class PrecisionDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy, IDateTimeOffsetHumanizeStrategy { private readonly double _precision; @@ -70,5 +70,17 @@ public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo cult if (seconds > 0) return formatter.DateHumanize(TimeUnit.Second, tense, seconds); return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); } + + /// + /// Calculates the distance of time in words between two provided dates + /// + /// + /// + /// + /// + public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) + { + return Humanize(input.UtcDateTime, comparisonBase.UtcDateTime, culture); + } } } \ No newline at end of file diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 78d0e49b4..bacfec1e7 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -52,6 +52,7 @@ + From 2c09e49ba61a1e8acab618ae735a8beaf9ec7364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pa=C5=BEourek?= Date: Sat, 28 Mar 2015 20:37:07 +0100 Subject: [PATCH 2/4] Separated humanizing time strategies for DateTime and DateTimeOffset. --- readme.md | 4 +- ...provalTest.approve_public_api.approved.txt | 10 ++ .../DateTimeOffsetHumanizeTests.cs | 8 +- src/Humanizer/Configuration/Configurator.cs | 2 +- .../DateTimeHumanizeAlgorithms.cs | 116 ++++++++++++++++++ .../DefaultDateTimeHumanizeStrategy.cs | 73 +---------- .../DefaultDateTimeOffsetHumanizeStrategy.cs | 19 +++ .../PrecisionDateTimeHumanizeStrategy.cs | 62 +--------- ...PrecisionDateTimeOffsetHumanizeStrategy.cs | 30 +++++ src/Humanizer/Humanizer.csproj | 5 +- 10 files changed, 193 insertions(+), 136 deletions(-) create mode 100644 src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs create mode 100644 src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeOffsetHumanizeStrategy.cs create mode 100644 src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeOffsetHumanizeStrategy.cs diff --git a/readme.md b/readme.md index e4fc4a661..1839ab43a 100644 --- a/readme.md +++ b/readme.md @@ -271,6 +271,8 @@ DateTime.UtcNow.AddHours(-2).Humanize() => "2 hours ago" DateTime.UtcNow.AddHours(30).Humanize() => "tomorrow" DateTime.UtcNow.AddHours(2).Humanize() => "2 hours from now" + +DateTimeOffset.AddHours(1).Humanize() => "an hour from now" ``` Humanizer supports both local and UTC dates as well as dates with offset (`DateTimeOffset`). You could also provide the date you want the input date to be compared against. If null, it will use the current date as comparison base. @@ -305,7 +307,7 @@ To use the precision based strategy you need to configure it: ```C# Configurator.DateTimeHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision = .75); -Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision = .75); // configure when humanizing DateTimeOffset +Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(precision = .75); // configure when humanizing DateTimeOffset ``` The default precision is set to .75 but you can pass your desired precision too. With precision set to 0.75: diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt index b6ec8d2b9..46ef0744c 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -152,6 +152,11 @@ public class DefaultDateTimeHumanizeStrategy { public DefaultDateTimeHumanizeStrategy() { } public string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture) { } +} + +public class DefaultDateTimeOffsetHumanizeStrategy +{ + public DefaultDateTimeOffsetHumanizeStrategy() { } public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { } } @@ -169,6 +174,11 @@ public class PrecisionDateTimeHumanizeStrategy { public PrecisionDateTimeHumanizeStrategy(double precision) { } public string Humanize(System.DateTime input, System.DateTime comparisonBase, System.Globalization.CultureInfo culture) { } +} + +public class PrecisionDateTimeOffsetHumanizeStrategy +{ + public PrecisionDateTimeOffsetHumanizeStrategy(double precision) { } public string Humanize(System.DateTimeOffset input, System.DateTimeOffset comparisonBase, System.Globalization.CultureInfo culture) { } } diff --git a/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs b/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs index 839a759a5..c77357f41 100644 --- a/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs +++ b/src/Humanizer.Tests/DateTimeOffsetHumanizeTests.cs @@ -17,7 +17,7 @@ public DateTimeOffsetHumanizeTests() : base("en-US") [Fact] public void DefaultStrategy_SameOffset() { - Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeOffsetHumanizeStrategy(); var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); var baseTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, TimeSpan.Zero); @@ -31,7 +31,7 @@ public void DefaultStrategy_SameOffset() [Fact] public void DefaultStrategy_DifferentOffsets() { - Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeOffsetHumanizeStrategy(); var inputTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, new TimeSpan(2, 0, 0)); var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(1, 0, 0)); @@ -45,7 +45,7 @@ public void DefaultStrategy_DifferentOffsets() [Fact] public void PrecisionStrategy_SameOffset() { - Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(0.75); + Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(0.75); var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); var baseTime = new DateTimeOffset(2015, 07, 04, 05, 0, 0, TimeSpan.Zero); @@ -59,7 +59,7 @@ public void PrecisionStrategy_SameOffset() [Fact] public void PrecisionStrategy_DifferentOffsets() { - Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(0.75); + Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(0.75); var inputTime = new DateTimeOffset(2015, 07, 05, 03, 45, 0, new TimeSpan(2, 0, 0)); var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(-5, 0, 0)); diff --git a/src/Humanizer/Configuration/Configurator.cs b/src/Humanizer/Configuration/Configurator.cs index a72e4a969..facdd033b 100644 --- a/src/Humanizer/Configuration/Configurator.cs +++ b/src/Humanizer/Configuration/Configurator.cs @@ -99,7 +99,7 @@ public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy set { _dateTimeHumanizeStrategy = value; } } - private static IDateTimeOffsetHumanizeStrategy _dateTimeOffsetHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); + private static IDateTimeOffsetHumanizeStrategy _dateTimeOffsetHumanizeStrategy = new DefaultDateTimeOffsetHumanizeStrategy(); /// /// The strategy to be used for DateTimeOffset.Humanize /// diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs b/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs new file mode 100644 index 000000000..1f0eaf500 --- /dev/null +++ b/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs @@ -0,0 +1,116 @@ +using System; +using System.Globalization; +using Humanizer.Configuration; +using Humanizer.Localisation; + +namespace Humanizer.DateTimeHumanizeStrategy +{ + /// + /// A precision-based calculator for distance of time, used for both + /// + internal static class DateTimeHumanizeAlgorithms + { + /// + /// Returns localized & humanized distance of time between two dates; given a specific precision. + /// + public static string PrecisionHumanize(DateTime input, DateTime comparisonBase, double precision, CultureInfo culture) + { + var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); + var tense = input > comparisonBase ? Tense.Future : Tense.Past; + + int seconds = ts.Seconds, minutes = ts.Minutes, hours = ts.Hours, days = ts.Days; + int years = 0, months = 0; + + // start approximate from smaller units towards bigger ones + if (ts.Milliseconds >= 999 * precision) seconds += 1; + if (seconds >= 59 * precision) minutes += 1; + if (minutes >= 59 * precision) hours += 1; + if (hours >= 23 * precision) days += 1; + + // month calculation + if (days >= 30 * precision & days <= 31) months = 1; + if (days > 31 && days < 365 * precision) + { + int factor = Convert.ToInt32(Math.Floor((double)days / 30)); + int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 30)); + months = (days >= 30 * (factor + precision)) ? maxMonths : maxMonths - 1; + } + + // year calculation + if (days >= 365 * precision && days <= 366) years = 1; + if (days > 365) + { + int factor = Convert.ToInt32(Math.Floor((double)days / 365)); + int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 365)); + years = (days >= 365 * (factor + precision)) ? maxMonths : maxMonths - 1; + } + + // start computing result from larger units to smaller ones + var formatter = Configurator.GetFormatter(culture); + if (years > 0) return formatter.DateHumanize(TimeUnit.Year, tense, years); + if (months > 0) return formatter.DateHumanize(TimeUnit.Month, tense, months); + if (days > 0) return formatter.DateHumanize(TimeUnit.Day, tense, days); + if (hours > 0) return formatter.DateHumanize(TimeUnit.Hour, tense, hours); + if (minutes > 0) return formatter.DateHumanize(TimeUnit.Minute, tense, minutes); + if (seconds > 0) return formatter.DateHumanize(TimeUnit.Second, tense, seconds); + return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); + } + + // http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time + /// + /// Calculates the distance of time in words between two provided dates + /// + public static string DefaultHumanize(DateTime input, DateTime comparisonBase, CultureInfo culture) + { + var tense = input > comparisonBase ? Tense.Future : Tense.Past; + var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); + + var formatter = Configurator.GetFormatter(culture); + + if (ts.TotalMilliseconds < 500) + return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); + + if (ts.TotalSeconds < 60) + return formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); + + if (ts.TotalSeconds < 120) + return formatter.DateHumanize(TimeUnit.Minute, tense, 1); + + if (ts.TotalMinutes < 60) + return formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); + + if (ts.TotalMinutes < 90) + return formatter.DateHumanize(TimeUnit.Hour, tense, 1); + + if (ts.TotalHours < 24) + return formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); + + if (ts.TotalHours < 48) + { + var days = Math.Abs((input.Date - comparisonBase.Date).Days); + return formatter.DateHumanize(TimeUnit.Day, tense, days); + } + + if (ts.TotalDays < 28) + return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); + + if (ts.TotalDays >= 28 && ts.TotalDays < 30) + { + if (comparisonBase.Date.AddMonths(tense == Tense.Future ? 1 : -1) == input.Date) + return formatter.DateHumanize(TimeUnit.Month, tense, 1); + return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); + } + + if (ts.TotalDays < 345) + { + int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); + return formatter.DateHumanize(TimeUnit.Month, tense, months); + } + + int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); + if (years == 0) years = 1; + + return formatter.DateHumanize(TimeUnit.Year, tense, years); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs index 78c23d584..e8c97fdb0 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs @@ -1,86 +1,19 @@ using System; using System.Globalization; -using Humanizer.Configuration; -using Humanizer.Localisation; namespace Humanizer.DateTimeHumanizeStrategy { /// /// The default 'distance of time' -> words calculator. /// - public class DefaultDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy, IDateTimeOffsetHumanizeStrategy + public class DefaultDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy { - // http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time - /// - /// Calculates the distance of time in words between two provided dates - /// - /// - /// - /// - /// - public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo culture) - { - var tense = input > comparisonBase ? Tense.Future : Tense.Past; - var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); - - var formatter = Configurator.GetFormatter(culture); - - if (ts.TotalMilliseconds < 500) - return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); - - if (ts.TotalSeconds < 60) - return formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); - - if (ts.TotalSeconds < 120) - return formatter.DateHumanize(TimeUnit.Minute, tense, 1); - - if (ts.TotalMinutes < 60) - return formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); - - if (ts.TotalMinutes < 90) - return formatter.DateHumanize(TimeUnit.Hour, tense, 1); - - if (ts.TotalHours < 24) - return formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); - - if (ts.TotalHours < 48) - { - var days = Math.Abs((input.Date - comparisonBase.Date).Days); - return formatter.DateHumanize(TimeUnit.Day, tense, days); - } - - if (ts.TotalDays < 28) - return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); - - if (ts.TotalDays >= 28 && ts.TotalDays < 30) - { - if (comparisonBase.Date.AddMonths(tense == Tense.Future ? 1 : -1) == input.Date) - return formatter.DateHumanize(TimeUnit.Month, tense, 1); - return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); - } - - if (ts.TotalDays < 345) - { - int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); - return formatter.DateHumanize(TimeUnit.Month, tense, months); - } - - int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); - if (years == 0) years = 1; - - return formatter.DateHumanize(TimeUnit.Year, tense, years); - } - /// /// Calculates the distance of time in words between two provided dates /// - /// - /// - /// - /// - public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) + public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo culture) { - return Humanize(input.UtcDateTime, comparisonBase.UtcDateTime, culture); + return DateTimeHumanizeAlgorithms.DefaultHumanize(input, comparisonBase, culture); } } } \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeOffsetHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeOffsetHumanizeStrategy.cs new file mode 100644 index 000000000..7f0ffc45c --- /dev/null +++ b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeOffsetHumanizeStrategy.cs @@ -0,0 +1,19 @@ +using System; +using System.Globalization; + +namespace Humanizer.DateTimeHumanizeStrategy +{ + /// + /// The default 'distance of time' -> words calculator. + /// + public class DefaultDateTimeOffsetHumanizeStrategy : IDateTimeOffsetHumanizeStrategy + { + /// + /// Calculates the distance of time in words between two provided dates + /// + public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) + { + return DateTimeHumanizeAlgorithms.DefaultHumanize(input.UtcDateTime, comparisonBase.UtcDateTime, culture); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs index a1b56df61..37bef4b9e 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeHumanizeStrategy.cs @@ -1,14 +1,12 @@ using System; using System.Globalization; -using Humanizer.Configuration; -using Humanizer.Localisation; namespace Humanizer.DateTimeHumanizeStrategy { /// - /// + /// Precision-based calculator for distance between two times /// - public class PrecisionDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy, IDateTimeOffsetHumanizeStrategy + public class PrecisionDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy { private readonly double _precision; @@ -24,63 +22,9 @@ public PrecisionDateTimeHumanizeStrategy(double precision = .75) /// /// Returns localized & humanized distance of time between two dates; given a specific precision. /// - /// - /// - /// - /// public string Humanize(DateTime input, DateTime comparisonBase, CultureInfo culture) { - var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); - var tense = input > comparisonBase ? Tense.Future : Tense.Past; - - int seconds = ts.Seconds, minutes = ts.Minutes, hours = ts.Hours, days = ts.Days; - int years = 0, months = 0; - - // start approximate from smaller units towards bigger ones - if (ts.Milliseconds >= 999 * _precision) seconds += 1; - if (seconds >= 59 * _precision) minutes += 1; - if (minutes >= 59 * _precision) hours += 1; - if (hours >= 23 * _precision) days += 1; - - // month calculation - if (days >= 30 * _precision & days <= 31) months = 1; - if (days > 31 && days < 365 * _precision) - { - int factor = Convert.ToInt32(Math.Floor((double)days / 30)); - int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 30)); - months = (days >= 30 * (factor + _precision)) ? maxMonths : maxMonths - 1; - } - - // year calculation - if (days >= 365 * _precision && days <= 366) years = 1; - if (days > 365) - { - int factor = Convert.ToInt32(Math.Floor((double)days / 365)); - int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 365)); - years = (days >= 365 * (factor + _precision)) ? maxMonths : maxMonths - 1; - } - - // start computing result from larger units to smaller ones - var formatter = Configurator.GetFormatter(culture); - if (years > 0) return formatter.DateHumanize(TimeUnit.Year, tense, years); - if (months > 0) return formatter.DateHumanize(TimeUnit.Month, tense, months); - if (days > 0) return formatter.DateHumanize(TimeUnit.Day, tense, days); - if (hours > 0) return formatter.DateHumanize(TimeUnit.Hour, tense, hours); - if (minutes > 0) return formatter.DateHumanize(TimeUnit.Minute, tense, minutes); - if (seconds > 0) return formatter.DateHumanize(TimeUnit.Second, tense, seconds); - return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); - } - - /// - /// Calculates the distance of time in words between two provided dates - /// - /// - /// - /// - /// - public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) - { - return Humanize(input.UtcDateTime, comparisonBase.UtcDateTime, culture); + return DateTimeHumanizeAlgorithms.PrecisionHumanize(input, comparisonBase, _precision, culture); } } } \ No newline at end of file diff --git a/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeOffsetHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeOffsetHumanizeStrategy.cs new file mode 100644 index 000000000..5b1738b5f --- /dev/null +++ b/src/Humanizer/DateTimeHumanizeStrategy/PrecisionDateTimeOffsetHumanizeStrategy.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; + +namespace Humanizer.DateTimeHumanizeStrategy +{ + /// + /// Precision-based calculator for distance between two times + /// + public class PrecisionDateTimeOffsetHumanizeStrategy : IDateTimeOffsetHumanizeStrategy + { + private readonly double _precision; + + /// + /// Constructs a precision-based calculator for distance of time with default precision 0.75. + /// + /// precision of approximation, if not provided 0.75 will be used as a default precision. + public PrecisionDateTimeOffsetHumanizeStrategy(double precision = .75) + { + _precision = precision; + } + + /// + /// Returns localized & humanized distance of time between two dates; given a specific precision. + /// + public string Humanize(DateTimeOffset input, DateTimeOffset comparisonBase, CultureInfo culture) + { + return DateTimeHumanizeAlgorithms.PrecisionHumanize(input.UtcDateTime, comparisonBase.UtcDateTime, _precision, culture); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index bacfec1e7..e05415c86 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -52,7 +52,11 @@ + + + + @@ -89,7 +93,6 @@ - From eb78493fe06384c8248531812eb2451acd792cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pa=C5=BEourek?= Date: Sat, 28 Mar 2015 20:39:26 +0100 Subject: [PATCH 3/4] Added pull request #399 to release notes. --- release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release_notes.md b/release_notes.md index 5577ff553..99a6374d1 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,4 +1,5 @@ ###In Development + - [#399](https://github.com/MehdiK/Humanizer/pull/399): Support for humanizing DateTimeOffset [Commits](https://github.com/MehdiK/Humanizer/compare/v1.34.0...master) From 134d1cd7f193734299efe2f5558a0d3f40b881b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pa=C5=BEourek?= Date: Sat, 28 Mar 2015 20:44:43 +0100 Subject: [PATCH 4/4] Fixed DateTimeHumanizeAlgorithms documentation comment. --- .../DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs b/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs index 1f0eaf500..ad1646c6c 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs @@ -6,7 +6,7 @@ namespace Humanizer.DateTimeHumanizeStrategy { /// - /// A precision-based calculator for distance of time, used for both + /// Algorithms used to convert distance between two dates into words. /// internal static class DateTimeHumanizeAlgorithms {