From f6b75eb447b8d5bb129f0b9def2d16451064fe74 Mon Sep 17 00:00:00 2001 From: MehdiK Date: Mon, 7 Apr 2014 16:10:38 +0430 Subject: [PATCH 1/5] implemented DateTimeStrategy to cater for precisions --- ...provalTest.approve_public_api.approved.txt | 18 ++ src/Humanizer.Tests/DateHumanize.cs | 9 +- ...cs => DateHumanizeDefaultStrategyTests.cs} | 4 +- .../DateTimeHumanizePrecisionStrategyTests.cs | 177 ++++++++++++++++++ src/Humanizer.Tests/Humanizer.Tests.csproj | 3 +- src/Humanizer/Configuration/Configurator.cs | 3 + src/Humanizer/DateHumanizeExtensions.cs | 55 +----- .../DefaultDateTimeStrategy.cs | 67 +++++++ .../IDateTimeHumanizeStrategy.cs | 9 + .../PrecisionDateTimeStrategy.cs | 61 ++++++ src/Humanizer/Humanizer.csproj | 3 + 11 files changed, 353 insertions(+), 56 deletions(-) rename src/Humanizer.Tests/{DateHumanizeTests.cs => DateHumanizeDefaultStrategyTests.cs} (98%) create mode 100644 src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs create mode 100644 src/Humanizer/DateTimeStrategy/DefaultDateTimeStrategy.cs create mode 100644 src/Humanizer/DateTimeStrategy/IDateTimeHumanizeStrategy.cs create mode 100644 src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs 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 482b10d6d..334b9ff06 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -65,6 +65,7 @@ public class CasingExtensions public class Configurator { + public Humanizer.DateTimeStrategy.IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; } public Humanizer.Localisation.IFormatter Formatter { get; } } @@ -73,6 +74,23 @@ public class DateHumanizeExtensions public string Humanize(System.DateTime input, bool utcDate, System.Nullable dateToCompareAgainst) { } } +public class DefaultDateTimeStrategy +{ + public DefaultDateTimeStrategy() { } + public string Humanize(System.DateTime input, System.DateTime comparisonBase) { } +} + +public interface IDateTimeHumanizeStrategy +{ + string Humanize(System.DateTime input, System.DateTime comparisonBase); +} + +public class PrecisionDateTimeStrategy +{ + public PrecisionDateTimeStrategy(double precision) { } + public string Humanize(System.DateTime input, System.DateTime comparisonBase) { } +} + public class EnumDehumanizeExtensions { public TTargetEnum DehumanizeTo(string input) { } diff --git a/src/Humanizer.Tests/DateHumanize.cs b/src/Humanizer.Tests/DateHumanize.cs index 871b561c5..1af72f945 100644 --- a/src/Humanizer.Tests/DateHumanize.cs +++ b/src/Humanizer.Tests/DateHumanize.cs @@ -1,4 +1,6 @@ using System; +using Humanizer.Configuration; +using Humanizer.DateTimeStrategy; using Humanizer.Localisation; using Xunit; @@ -25,8 +27,13 @@ static void VerifyWithDateInjection(string expectedString, TimeSpan deltaFromNow Assert.Equal(expectedString, now.Add(deltaFromNow).Humanize(false, now)); } - public static void Verify(string expectedString, int unit, TimeUnit timeUnit, Tense tense) + public static void Verify(string expectedString, int unit, TimeUnit timeUnit, Tense tense, double? precision = null) { + if (precision.HasValue) + Configurator.DateTimeHumanizeStrategy = new PrecisionDateTimeStrategy(precision.Value); + else + Configurator.DateTimeHumanizeStrategy = new DefaultDateTimeStrategy(); + var deltaFromNow = new TimeSpan(); unit = Math.Abs(unit); diff --git a/src/Humanizer.Tests/DateHumanizeTests.cs b/src/Humanizer.Tests/DateHumanizeDefaultStrategyTests.cs similarity index 98% rename from src/Humanizer.Tests/DateHumanizeTests.cs rename to src/Humanizer.Tests/DateHumanizeDefaultStrategyTests.cs index b5892e2b9..479564581 100644 --- a/src/Humanizer.Tests/DateHumanizeTests.cs +++ b/src/Humanizer.Tests/DateHumanizeDefaultStrategyTests.cs @@ -3,7 +3,7 @@ namespace Humanizer.Tests { - public class DateHumanizeTests + public class DateHumanizeDefaultStrategyTests { [Theory] [InlineData(1, "one second ago")] @@ -125,4 +125,4 @@ public void YearsFromNow(int years, string expected) DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); } } -} +} \ No newline at end of file diff --git a/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs new file mode 100644 index 000000000..ab46e1788 --- /dev/null +++ b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs @@ -0,0 +1,177 @@ +using Humanizer.Localisation; +using Xunit.Extensions; + +namespace Humanizer.Tests +{ + public class DateTimeHumanizePrecisionStrategyTests + { + [Theory] + [InlineData(1, "now")] + [InlineData(749, "now")] + [InlineData(750, "one second ago")] + [InlineData(1000, "one second ago")] + [InlineData(1499, "one second ago")] + [InlineData(1500, "2 seconds ago")] + public void MillisecondsAgo(int milliseconds, string expected) + { + DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "now")] + [InlineData(749, "now")] + [InlineData(750, "one second from now")] + [InlineData(1000, "one second from now")] + [InlineData(1499, "one second from now")] + [InlineData(1500, "2 seconds from now")] + public void MillisecondsFromNow(int milliseconds, string expected) + { + DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "one second ago")] + [InlineData(10, "10 seconds ago")] + [InlineData(44, "44 seconds ago")] + [InlineData(45, "a minute ago")] + [InlineData(60, "a minute ago")] + [InlineData(89, "a minute ago")] + [InlineData(90, "2 minutes ago")] + [InlineData(120, "2 minutes ago")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "one second from now")] + [InlineData(10, "10 seconds from now")] + [InlineData(44, "44 seconds from now")] + [InlineData(45, "a minute from now")] + [InlineData(60, "a minute from now")] + [InlineData(89, "a minute from now")] + [InlineData(90, "2 minutes from now")] + [InlineData(120, "2 minutes from now")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "a minute ago")] + [InlineData(10, "10 minutes ago")] + [InlineData(44, "44 minutes ago")] + [InlineData(45, "an hour ago")] + [InlineData(60, "an hour ago")] + [InlineData(89, "an hour ago")] + [InlineData(90, "2 hours ago")] + [InlineData(120, "2 hours ago")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "a minute from now")] + [InlineData(10, "10 minutes from now")] + [InlineData(44, "44 minutes from now")] + [InlineData(45, "an hour from now")] + [InlineData(60, "an hour from now")] + [InlineData(89, "an hour from now")] + [InlineData(90, "2 hours from now")] + [InlineData(120, "2 hours from now")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "an hour ago")] + [InlineData(10, "10 hours ago")] + [InlineData(17, "17 hours ago")] + [InlineData(18, "yesterday")] + [InlineData(24, "yesterday")] + [InlineData(35, "yesterday")] + [InlineData(36, "2 days ago")] + [InlineData(48, "2 days ago")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "an hour from now")] + [InlineData(10, "10 hours from now")] + [InlineData(18, "tomorrow")] + [InlineData(24, "tomorrow")] + [InlineData(41, "tomorrow")] + [InlineData(42, "2 days from now")] + [InlineData(60, "2 days from now")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "yesterday")] + [InlineData(10, "10 days ago")] + [InlineData(20, "20 days ago")] + [InlineData(25, "one month ago")] + [InlineData(32, "one month ago")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "tomorrow")] + [InlineData(10, "10 days from now")] + [InlineData(20, "20 days from now")] + [InlineData(25, "one month from now")] + [InlineData(32, "one month from now")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "one month ago")] + [InlineData(8, "8 months ago")] + [InlineData(9, "one year ago")] + [InlineData(12, "one year ago")] + [InlineData(18, "2 years ago")] + [InlineData(24, "2 years ago")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "one month from now")] + [InlineData(8, "8 months from now")] + [InlineData(9, "one year from now")] + [InlineData(12, "one year from now")] + [InlineData(18, "2 years from now")] + [InlineData(24, "2 years from now")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future, .75); + } + + [Theory] + [InlineData(1, "one year ago")] + [InlineData(2, "2 years ago")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past, .75); + } + + [Theory] + [InlineData(1, "one year from now")] + [InlineData(2, "2 years from now")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future, .75); + } + } +} diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 05addc363..963f995b0 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -97,6 +97,7 @@ + @@ -104,7 +105,7 @@ - + diff --git a/src/Humanizer/Configuration/Configurator.cs b/src/Humanizer/Configuration/Configurator.cs index 2eae49b54..6fc4f53b1 100644 --- a/src/Humanizer/Configuration/Configurator.cs +++ b/src/Humanizer/Configuration/Configurator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using Humanizer.DateTimeStrategy; using Humanizer.Localisation; namespace Humanizer.Configuration @@ -35,5 +36,7 @@ public static IFormatter Formatter return new DefaultFormatter(); } } + + public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; } } } diff --git a/src/Humanizer/DateHumanizeExtensions.cs b/src/Humanizer/DateHumanizeExtensions.cs index fdc59257f..7147e3baf 100644 --- a/src/Humanizer/DateHumanizeExtensions.cs +++ b/src/Humanizer/DateHumanizeExtensions.cs @@ -1,6 +1,5 @@ using System; using Humanizer.Configuration; -using Humanizer.Localisation; namespace Humanizer { @@ -9,69 +8,21 @@ namespace Humanizer /// public static class DateHumanizeExtensions { - // http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time /// /// Turns the current or provided date into a human readable sentence /// /// The date to be humanized /// Boolean value indicating whether the date is in UTC or local /// Date to compare the input against. If null, current date is used as base - /// + /// distance of time in words public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null) { - if (dateToCompareAgainst == null) - dateToCompareAgainst = DateTime.UtcNow; - - var formatter = Configurator.Formatter; - var comparisonBase = dateToCompareAgainst.Value; + var comparisonBase = dateToCompareAgainst ?? DateTime.UtcNow; if (!utcDate) comparisonBase = comparisonBase.ToLocalTime(); - if (input <= comparisonBase && comparisonBase.Subtract(input) < TimeSpan.FromMilliseconds(500)) - return formatter.DateHumanize_Now(); - - var timeUnitTense = input > comparisonBase ? Tense.Future : Tense.Past; - var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); - - if (ts.TotalSeconds < 60) - return formatter.DateHumanize(TimeUnit.Second, timeUnitTense, ts.Seconds); - - if (ts.TotalSeconds < 120) - return formatter.DateHumanize(TimeUnit.Minute, timeUnitTense, 1); - - if (ts.TotalMinutes < 45) - return formatter.DateHumanize(TimeUnit.Minute, timeUnitTense, ts.Minutes); - - if (ts.TotalMinutes < 90) - return formatter.DateHumanize(TimeUnit.Hour, timeUnitTense, 1); - - if (ts.TotalHours < 24) - return formatter.DateHumanize(TimeUnit.Hour, timeUnitTense, ts.Hours); - - if (ts.TotalHours < 48) - return formatter.DateHumanize(TimeUnit.Day, timeUnitTense, 1); - - if (ts.TotalDays < 28) - return formatter.DateHumanize(TimeUnit.Day, timeUnitTense, ts.Days); - - if (ts.TotalDays >= 28 && ts.TotalDays < 30) - { - if (comparisonBase.Date.AddMonths(timeUnitTense == Tense.Future ? 1 : -1) == input.Date) - return formatter.DateHumanize(TimeUnit.Month, timeUnitTense, 1); - - return formatter.DateHumanize(TimeUnit.Day, timeUnitTense, ts.Days); - } - - if (ts.TotalDays < 345) - { - int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); - return formatter.DateHumanize(TimeUnit.Month, timeUnitTense, months); - } - - int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); - if (years == 0) years = 1; - return formatter.DateHumanize(TimeUnit.Year, timeUnitTense, years); + return Configurator.DateTimeHumanizeStrategy.Humanize(input, comparisonBase); } } } \ No newline at end of file diff --git a/src/Humanizer/DateTimeStrategy/DefaultDateTimeStrategy.cs b/src/Humanizer/DateTimeStrategy/DefaultDateTimeStrategy.cs new file mode 100644 index 000000000..d51dde62c --- /dev/null +++ b/src/Humanizer/DateTimeStrategy/DefaultDateTimeStrategy.cs @@ -0,0 +1,67 @@ +using System; +using Humanizer.Configuration; +using Humanizer.Localisation; + +namespace Humanizer.DateTimeStrategy +{ + /// + /// The default distance of time in works calculator + /// + public class DefaultDateTimeStrategy : 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) + { + var tense = input > comparisonBase ? Tense.Future : Tense.Past; + var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); + + if (ts.TotalMilliseconds < 500) + return Configurator.Formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); + + if (ts.TotalSeconds < 60) + return Configurator.Formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); + + if (ts.TotalSeconds < 120) + return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, 1); + + if (ts.TotalMinutes < 45) + return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); + + if (ts.TotalMinutes < 90) + return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, 1); + + if (ts.TotalHours < 24) + return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); + + if (ts.TotalHours < 48) + return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, 1); + + if (ts.TotalDays < 28) + return Configurator.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 Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, 1); + return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); + } + + if (ts.TotalDays < 345) + { + int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); + return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, months); + } + + int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); + if (years == 0) years = 1; + + return Configurator.Formatter.DateHumanize(TimeUnit.Year, tense, years); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/DateTimeStrategy/IDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeStrategy/IDateTimeHumanizeStrategy.cs new file mode 100644 index 000000000..feb99d599 --- /dev/null +++ b/src/Humanizer/DateTimeStrategy/IDateTimeHumanizeStrategy.cs @@ -0,0 +1,9 @@ +using System; + +namespace Humanizer.DateTimeStrategy +{ + public interface IDateTimeHumanizeStrategy + { + string Humanize(DateTime input, DateTime comparisonBase); + } +} \ No newline at end of file diff --git a/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs b/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs new file mode 100644 index 000000000..65c197cdc --- /dev/null +++ b/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs @@ -0,0 +1,61 @@ +using System; +using Humanizer.Configuration; +using Humanizer.Localisation; + +namespace Humanizer.DateTimeStrategy +{ + public class PrecisionDateTimeStrategy : IDateTimeHumanizeStrategy + { + private readonly double _precision; + + public PrecisionDateTimeStrategy(double precision = .75) + { + _precision = precision; + } + + public string Humanize(DateTime input, DateTime comparisonBase) + { + var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); + var tense = input > comparisonBase ? Tense.Future : Tense.Past; + + if (ts.TotalMilliseconds < 1000 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); + + if (ts.TotalSeconds < 60 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); + + if (ts.TotalSeconds < 120 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, 1); + + if (ts.TotalMinutes < 60 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); + + if (ts.TotalMinutes < 120 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, 1); + + if (ts.TotalHours < 24 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); + + if (ts.TotalHours < 48 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, 1); + + if (ts.TotalDays < 30 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); + + if (ts.TotalDays < 60 * _precision) + return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, 1); + + if (ts.TotalDays < 365 * _precision) + { + int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); + return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, months); + } + + int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); + if (years == 0) + years = 1; + + return Configurator.Formatter.DateHumanize(TimeUnit.Year, tense, years); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index eeebc22c6..3ba49d240 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -70,6 +70,9 @@ + + + From 469389bdced54726c731db53dead11cb1425874c Mon Sep 17 00:00:00 2001 From: MehdiK Date: Mon, 7 Apr 2014 16:25:51 +0430 Subject: [PATCH 2/5] changed Farsi date tests to use Verify --- .../Localisation/fa/DateHumanizeTests.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Humanizer.Tests/Localisation/fa/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/fa/DateHumanizeTests.cs index 8fc105d39..6a5953960 100644 --- a/src/Humanizer.Tests/Localisation/fa/DateHumanizeTests.cs +++ b/src/Humanizer.Tests/Localisation/fa/DateHumanizeTests.cs @@ -1,5 +1,4 @@ -using System; -using Xunit; +using Humanizer.Localisation; using Xunit.Extensions; namespace Humanizer.Tests.Localisation.fa @@ -13,7 +12,7 @@ public class DateHumanizeTests : AmbientCulture [InlineData(13, "13 روز بعد")] public void DaysFromNow(int days, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddDays(days).Humanize()); + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); } [Theory] @@ -21,7 +20,7 @@ public void DaysFromNow(int days, string expected) [InlineData(-11, "11 روز پیش")] public void DaysAgo(int days, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddDays(days).Humanize()); + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); } [Theory] @@ -29,7 +28,7 @@ public void DaysAgo(int days, string expected) [InlineData(11, "11 ساعت بعد")] public void HoursFromNow(int hours, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddHours(hours).Humanize()); + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); } [Theory] @@ -37,7 +36,7 @@ public void HoursFromNow(int hours, string expected) [InlineData(-11, "11 ساعت پیش")] public void HoursAgo(int hours, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddHours(hours).Humanize()); + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); } [Theory] @@ -45,7 +44,7 @@ public void HoursAgo(int hours, string expected) [InlineData(13, "13 دقیقه بعد")] public void MinutesFromNow(int minutes, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddMinutes(minutes).Humanize()); + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); } [Theory] @@ -53,7 +52,7 @@ public void MinutesFromNow(int minutes, string expected) [InlineData(-13, "13 دقیقه پیش")] public void MinutesAgo(int minutes, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddMinutes(minutes).Humanize()); + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); } [Theory] @@ -61,7 +60,7 @@ public void MinutesAgo(int minutes, string expected) [InlineData(10, "10 ماه بعد")] public void MonthsFromNow(int months, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddMonths(months).Humanize()); + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); } [Theory] @@ -69,7 +68,7 @@ public void MonthsFromNow(int months, string expected) [InlineData(-10, "10 ماه پیش")] public void MonthsAgo(int months, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddMonths(months).Humanize()); + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); } [Theory] @@ -77,7 +76,7 @@ public void MonthsAgo(int months, string expected) [InlineData(11, "11 ثانیه بعد")] public void SecondsFromNow(int seconds, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddSeconds(seconds).Humanize()); + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); } [Theory] @@ -85,7 +84,7 @@ public void SecondsFromNow(int seconds, string expected) [InlineData(-11, "11 ثانیه پیش")] public void SecondsAgo(int seconds, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddSeconds(seconds).Humanize()); + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); } [Theory] @@ -93,7 +92,7 @@ public void SecondsAgo(int seconds, string expected) [InlineData(21, "21 سال بعد")] public void YearsFromNow(int years, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddYears(years).Humanize()); + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); } [Theory] @@ -101,7 +100,7 @@ public void YearsFromNow(int years, string expected) [InlineData(-21, "21 سال پیش")] public void YearsAgo(int years, string expected) { - Assert.Equal(expected, DateTime.UtcNow.AddYears(years).Humanize()); + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); } } } From 382ca64f034ae5a2c4f34a8ebac00af74b62b2bd Mon Sep 17 00:00:00 2001 From: MehdiK Date: Wed, 9 Apr 2014 16:13:44 +0430 Subject: [PATCH 3/5] fixed some of the tests --- src/Humanizer.Tests/DateHumanize.cs | 3 ++ .../DateTimeHumanizePrecisionStrategyTests.cs | 52 +++++++++++-------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/Humanizer.Tests/DateHumanize.cs b/src/Humanizer.Tests/DateHumanize.cs index 1af72f945..f3e0c3f38 100644 --- a/src/Humanizer.Tests/DateHumanize.cs +++ b/src/Humanizer.Tests/DateHumanize.cs @@ -42,6 +42,9 @@ public static void Verify(string expectedString, int unit, TimeUnit timeUnit, Te switch (timeUnit) { + case TimeUnit.Millisecond: + deltaFromNow = TimeSpan.FromMilliseconds(unit); + break; case TimeUnit.Second: deltaFromNow = TimeSpan.FromSeconds(unit); break; diff --git a/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs index ab46e1788..85807f9b1 100644 --- a/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs +++ b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs @@ -3,15 +3,15 @@ namespace Humanizer.Tests { - public class DateTimeHumanizePrecisionStrategyTests + public class DateTimeHumanizePrecisionStrategyTests { [Theory] [InlineData(1, "now")] [InlineData(749, "now")] [InlineData(750, "one second ago")] [InlineData(1000, "one second ago")] - [InlineData(1499, "one second ago")] - [InlineData(1500, "2 seconds ago")] + [InlineData(1749, "one second ago")] + [InlineData(1750, "2 seconds ago")] public void MillisecondsAgo(int milliseconds, string expected) { DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Past, .75); @@ -22,8 +22,8 @@ public void MillisecondsAgo(int milliseconds, string expected) [InlineData(749, "now")] [InlineData(750, "one second from now")] [InlineData(1000, "one second from now")] - [InlineData(1499, "one second from now")] - [InlineData(1500, "2 seconds from now")] + [InlineData(1749, "one second from now")] + [InlineData(1750, "2 seconds from now")] public void MillisecondsFromNow(int milliseconds, string expected) { DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Future, .75); @@ -35,8 +35,8 @@ public void MillisecondsFromNow(int milliseconds, string expected) [InlineData(44, "44 seconds ago")] [InlineData(45, "a minute ago")] [InlineData(60, "a minute ago")] - [InlineData(89, "a minute ago")] - [InlineData(90, "2 minutes ago")] + [InlineData(104, "a minute ago")] + [InlineData(105, "2 minutes ago")] [InlineData(120, "2 minutes ago")] public void SecondsAgo(int seconds, string expected) { @@ -49,8 +49,8 @@ public void SecondsAgo(int seconds, string expected) [InlineData(44, "44 seconds from now")] [InlineData(45, "a minute from now")] [InlineData(60, "a minute from now")] - [InlineData(89, "a minute from now")] - [InlineData(90, "2 minutes from now")] + [InlineData(104, "a minute from now")] + [InlineData(105, "2 minutes from now")] [InlineData(120, "2 minutes from now")] public void SecondsFromNow(int seconds, string expected) { @@ -63,8 +63,8 @@ public void SecondsFromNow(int seconds, string expected) [InlineData(44, "44 minutes ago")] [InlineData(45, "an hour ago")] [InlineData(60, "an hour ago")] - [InlineData(89, "an hour ago")] - [InlineData(90, "2 hours ago")] + [InlineData(104, "an hour ago")] + [InlineData(105, "2 hours ago")] [InlineData(120, "2 hours ago")] public void MinutesAgo(int minutes, string expected) { @@ -77,8 +77,8 @@ public void MinutesAgo(int minutes, string expected) [InlineData(44, "44 minutes from now")] [InlineData(45, "an hour from now")] [InlineData(60, "an hour from now")] - [InlineData(89, "an hour from now")] - [InlineData(90, "2 hours from now")] + [InlineData(104, "an hour from now")] + [InlineData(105, "2 hours from now")] [InlineData(120, "2 hours from now")] public void MinutesFromNow(int minutes, string expected) { @@ -91,9 +91,10 @@ public void MinutesFromNow(int minutes, string expected) [InlineData(17, "17 hours ago")] [InlineData(18, "yesterday")] [InlineData(24, "yesterday")] - [InlineData(35, "yesterday")] - [InlineData(36, "2 days ago")] + [InlineData(41, "yesterday")] + [InlineData(42, "2 days ago")] [InlineData(48, "2 days ago")] + [InlineData(60, "2 days ago")] public void HoursAgo(int hours, string expected) { DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past, .75); @@ -106,6 +107,7 @@ public void HoursAgo(int hours, string expected) [InlineData(24, "tomorrow")] [InlineData(41, "tomorrow")] [InlineData(42, "2 days from now")] + [InlineData(48, "2 days from now")] [InlineData(60, "2 days from now")] public void HoursFromNow(int hours, string expected) { @@ -116,8 +118,11 @@ public void HoursFromNow(int hours, string expected) [InlineData(1, "yesterday")] [InlineData(10, "10 days ago")] [InlineData(20, "20 days ago")] - [InlineData(25, "one month ago")] - [InlineData(32, "one month ago")] + [InlineData(22, "one month ago")] + [InlineData(23, "one month ago")] + [InlineData(31, "one month ago")] + [InlineData(43, "one month ago")] + [InlineData(44, "two months ago")] public void DaysAgo(int days, string expected) { DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past, .75); @@ -127,8 +132,11 @@ public void DaysAgo(int days, string expected) [InlineData(1, "tomorrow")] [InlineData(10, "10 days from now")] [InlineData(20, "20 days from now")] - [InlineData(25, "one month from now")] - [InlineData(32, "one month from now")] + [InlineData(22, "one month from now")] + [InlineData(23, "one month from now")] + [InlineData(31, "one month from now")] + [InlineData(43, "one month from now")] + [InlineData(44, "two months from now")] public void DaysFromNow(int days, string expected) { DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future, .75); @@ -139,7 +147,8 @@ public void DaysFromNow(int days, string expected) [InlineData(8, "8 months ago")] [InlineData(9, "one year ago")] [InlineData(12, "one year ago")] - [InlineData(18, "2 years ago")] + [InlineData(19, "one year ago")] + [InlineData(20, "2 years ago")] [InlineData(24, "2 years ago")] public void MonthsAgo(int months, string expected) { @@ -151,7 +160,8 @@ public void MonthsAgo(int months, string expected) [InlineData(8, "8 months from now")] [InlineData(9, "one year from now")] [InlineData(12, "one year from now")] - [InlineData(18, "2 years from now")] + [InlineData(19, "one year from now")] + [InlineData(20, "2 years from now")] [InlineData(24, "2 years from now")] public void MonthsFromNow(int months, string expected) { From b362c72b256c68421ec60ca6cb8a06c86aa247ed Mon Sep 17 00:00:00 2001 From: Wahid Shalaly Date: Thu, 10 Apr 2014 21:11:01 +1000 Subject: [PATCH 4/5] Precision-based algorithm from a previous (dead) branch. --- .../PrecisionDateTimeStrategy.cs | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs b/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs index 65c197cdc..d036fae6b 100644 --- a/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs +++ b/src/Humanizer/DateTimeStrategy/PrecisionDateTimeStrategy.cs @@ -4,58 +4,68 @@ namespace Humanizer.DateTimeStrategy { + /// + /// + /// public class PrecisionDateTimeStrategy : IDateTimeHumanizeStrategy { 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 PrecisionDateTimeStrategy(double precision = .75) { _precision = precision; } + /// + /// Returns localized & humanized distance of time between two dates; given a specific precision. + /// + /// + /// + /// public string Humanize(DateTime input, DateTime comparisonBase) { var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); var tense = input > comparisonBase ? Tense.Future : Tense.Past; - if (ts.TotalMilliseconds < 1000 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); + int seconds = ts.Seconds, minutes = ts.Minutes, hours = ts.Hours, days = ts.Days; + int years = 0, months = 0; - if (ts.TotalSeconds < 60 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); + // 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; - if (ts.TotalSeconds < 120 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, 1); - - if (ts.TotalMinutes < 60 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); - - if (ts.TotalMinutes < 120 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, 1); - - if (ts.TotalHours < 24 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); - - if (ts.TotalHours < 48 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, 1); - - if (ts.TotalDays < 30 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); - - if (ts.TotalDays < 60 * _precision) - return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, 1); - - if (ts.TotalDays < 365 * _precision) + // month calculation + if (days >= 30 * _precision & days <= 31) months = 1; + if (days > 31 && days < 365 * _precision) { - int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); - return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, months); + 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; } - int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); - if (years == 0) - years = 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; + } - return Configurator.Formatter.DateHumanize(TimeUnit.Year, tense, years); + // start computing result from larger units to smaller ones + if (years > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Year, tense, years); + if (months > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Month, tense, months); + if (days > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Day, tense, days); + if (hours > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Hour, tense, hours); + if (minutes > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Minute, tense, minutes); + if (seconds > 0) return Configurator.Formatter.DateHumanize(TimeUnit.Second, tense, seconds); + return Configurator.Formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); } } } \ No newline at end of file From d73a9ac9b22664d622debac76a5a8886c1d83c3d Mon Sep 17 00:00:00 2001 From: Wahid Shalaly Date: Thu, 10 Apr 2014 22:27:20 +1000 Subject: [PATCH 5/5] Fixed failing unit tests in DateTimeHumanizePrecisionStrategyTests --- .../DateTimeHumanizePrecisionStrategyTests.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs index 85807f9b1..1a6e99224 100644 --- a/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs +++ b/src/Humanizer.Tests/DateTimeHumanizePrecisionStrategyTests.cs @@ -5,6 +5,8 @@ namespace Humanizer.Tests { public class DateTimeHumanizePrecisionStrategyTests { + private const double defaultPrecision = .75; + [Theory] [InlineData(1, "now")] [InlineData(749, "now")] @@ -14,7 +16,7 @@ public class DateTimeHumanizePrecisionStrategyTests [InlineData(1750, "2 seconds ago")] public void MillisecondsAgo(int milliseconds, string expected) { - DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Past, .75); + DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Past, defaultPrecision); } [Theory] @@ -26,7 +28,7 @@ public void MillisecondsAgo(int milliseconds, string expected) [InlineData(1750, "2 seconds from now")] public void MillisecondsFromNow(int milliseconds, string expected) { - DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Future, .75); + DateHumanize.Verify(expected, milliseconds, TimeUnit.Millisecond, Tense.Future, defaultPrecision); } [Theory] @@ -40,7 +42,7 @@ public void MillisecondsFromNow(int milliseconds, string expected) [InlineData(120, "2 minutes ago")] public void SecondsAgo(int seconds, string expected) { - DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past, .75); + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past, defaultPrecision); } [Theory] @@ -54,7 +56,7 @@ public void SecondsAgo(int seconds, string expected) [InlineData(120, "2 minutes from now")] public void SecondsFromNow(int seconds, string expected) { - DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future, .75); + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future, defaultPrecision); } [Theory] @@ -68,7 +70,7 @@ public void SecondsFromNow(int seconds, string expected) [InlineData(120, "2 hours ago")] public void MinutesAgo(int minutes, string expected) { - DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past, .75); + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past, defaultPrecision); } [Theory] @@ -82,7 +84,7 @@ public void MinutesAgo(int minutes, string expected) [InlineData(120, "2 hours from now")] public void MinutesFromNow(int minutes, string expected) { - DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future, .75); + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future, defaultPrecision); } [Theory] @@ -97,7 +99,7 @@ public void MinutesFromNow(int minutes, string expected) [InlineData(60, "2 days ago")] public void HoursAgo(int hours, string expected) { - DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past, .75); + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past, defaultPrecision); } [Theory] @@ -111,35 +113,35 @@ public void HoursAgo(int hours, string expected) [InlineData(60, "2 days from now")] public void HoursFromNow(int hours, string expected) { - DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future, .75); + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future, defaultPrecision); } [Theory] [InlineData(1, "yesterday")] [InlineData(10, "10 days ago")] [InlineData(20, "20 days ago")] - [InlineData(22, "one month ago")] + [InlineData(22, "22 days ago")] [InlineData(23, "one month ago")] [InlineData(31, "one month ago")] [InlineData(43, "one month ago")] - [InlineData(44, "two months ago")] + [InlineData(53, "2 months ago")] public void DaysAgo(int days, string expected) { - DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past, .75); + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past, defaultPrecision); } [Theory] [InlineData(1, "tomorrow")] [InlineData(10, "10 days from now")] [InlineData(20, "20 days from now")] - [InlineData(22, "one month from now")] + [InlineData(22, "22 days from now")] [InlineData(23, "one month from now")] [InlineData(31, "one month from now")] [InlineData(43, "one month from now")] - [InlineData(44, "two months from now")] + [InlineData(53, "2 months from now")] public void DaysFromNow(int days, string expected) { - DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future, .75); + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future, defaultPrecision); } [Theory] @@ -148,11 +150,11 @@ public void DaysFromNow(int days, string expected) [InlineData(9, "one year ago")] [InlineData(12, "one year ago")] [InlineData(19, "one year ago")] - [InlineData(20, "2 years ago")] + [InlineData(21, "2 years ago")] [InlineData(24, "2 years ago")] public void MonthsAgo(int months, string expected) { - DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past, .75); + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past, defaultPrecision); } [Theory] @@ -161,11 +163,11 @@ public void MonthsAgo(int months, string expected) [InlineData(9, "one year from now")] [InlineData(12, "one year from now")] [InlineData(19, "one year from now")] - [InlineData(20, "2 years from now")] + [InlineData(21, "2 years from now")] [InlineData(24, "2 years from now")] public void MonthsFromNow(int months, string expected) { - DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future, .75); + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future, defaultPrecision); } [Theory] @@ -173,7 +175,7 @@ public void MonthsFromNow(int months, string expected) [InlineData(2, "2 years ago")] public void YearsAgo(int years, string expected) { - DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past, .75); + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past, defaultPrecision); } [Theory] @@ -181,7 +183,7 @@ public void YearsAgo(int years, string expected) [InlineData(2, "2 years from now")] public void YearsFromNow(int years, string expected) { - DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future, .75); + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future, defaultPrecision); } } }