From 87cbe1af91bfa4c59910218bbd67b101930ea82e Mon Sep 17 00:00:00 2001 From: Alex Knight Date: Sat, 9 Mar 2024 00:04:50 +0000 Subject: [PATCH] Update to latest version of main --- src/Humanizer.Tests/TimeSpanHumanizeTests.cs | 32 ++++++++++------ src/Humanizer/TimeSpanHumanizeExtensions.cs | 39 ++++++++++++++++---- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/Humanizer.Tests/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests/TimeSpanHumanizeTests.cs index 0ce694092..89f864220 100644 --- a/src/Humanizer.Tests/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests/TimeSpanHumanizeTests.cs @@ -7,7 +7,7 @@ public void AllTimeSpansMustBeUniqueForASequenceOfDays() var culture = new CultureInfo("en-US"); var qry = from i in Enumerable.Range(0, 100000) let ts = TimeSpan.FromDays(i) - let text = ts.Humanize(precision: 3, culture: culture, maxUnit: TimeUnit.Year) + let text = ts.Humanize(precision: 4, culture: culture, maxUnit: TimeUnit.Year) select text; var grouping = from t in qry group t by t into g @@ -17,15 +17,15 @@ group t by t into g } [Theory] - [InlineData(365, "11 months, 30 days")] + [InlineData(365, "11 months, 4 weeks, 2 days")] [InlineData(365 + 1, "1 year")] - [InlineData(365 + 365, "1 year, 11 months, 29 days")] + [InlineData(365 + 365, "1 year, 11 months, 4 weeks, 1 day")] [InlineData(365 + 365 + 1, "2 years")] - [InlineData(365 + 365 + 365, "2 years, 11 months, 29 days")] + [InlineData(365 + 365 + 365, "2 years, 11 months, 4 weeks, 1 day")] [InlineData(365 + 365 + 365 + 1, "3 years")] - [InlineData(365 + 365 + 365 + 365, "3 years, 11 months, 29 days")] + [InlineData(365 + 365 + 365 + 365, "3 years, 11 months, 4 weeks, 1 day")] [InlineData(365 + 365 + 365 + 365 + 1, "4 years")] - [InlineData(365 + 365 + 365 + 365 + 366, "4 years, 11 months, 30 days")] + [InlineData(365 + 365 + 365 + 365 + 366, "4 years, 11 months, 4 weeks, 2 days")] [InlineData(365 + 365 + 365 + 365 + 366 + 1, "5 years")] public void Years(int days, string expected) { @@ -36,15 +36,15 @@ public void Years(int days, string expected) [Theory] [InlineData(30, "4 weeks, 2 days")] [InlineData(30 + 1, "1 month")] - [InlineData(30 + 30, "1 month, 29 days")] + [InlineData(30 + 30, "1 month, 4 weeks, 1 day")] [InlineData(30 + 30 + 1, "2 months")] - [InlineData(30 + 30 + 31, "2 months, 30 days")] + [InlineData(30 + 30 + 31, "2 months, 4 weeks, 2 days")] [InlineData(30 + 30 + 31 + 1, "3 months")] - [InlineData(30 + 30 + 31 + 30, "3 months, 29 days")] + [InlineData(30 + 30 + 31 + 30, "3 months, 4 weeks, 1 day")] [InlineData(30 + 30 + 31 + 30 + 1, "4 months")] - [InlineData(30 + 30 + 31 + 30 + 31, "4 months, 30 days")] + [InlineData(30 + 30 + 31 + 30 + 31, "4 months, 4 weeks, 2 days")] [InlineData(30 + 30 + 31 + 30 + 31 + 1, "5 months")] - [InlineData(365, "11 months, 30 days")] + [InlineData(365, "11 months, 4 weeks, 2 days")] [InlineData(366, "1 year")] public void Months(int days, string expected) { @@ -457,4 +457,14 @@ public void CanSpecifyCultureExplicitlyToWords(int days, int precision, string c var actual = timeSpan.Humanize(precision: precision, culture: new(culture), maxUnit: TimeUnit.Year, toWords: true); Assert.Equal(expected: expected, actual); } + + [Fact] + //Fixes https://stackoverflow.com/questions/56550059/humanizer-months-weeks-days-hours + public void MonthsAndWeeks() + { + var ts = new TimeSpan(109, 4, 0, 0, 0); + var humanized = ts.Humanize(4, maxUnit: TimeUnit.Month); + + Assert.Equal("3 months, 2 weeks, 3 days, 4 hours", humanized); + } } \ No newline at end of file diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index f77dac0df..40a794ee9 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -110,7 +110,7 @@ static int GetTimeUnitNumericalValue(TimeUnit timeUnitToGet, TimeSpan timespan, TimeUnit.Minute => GetNormalCaseTimeAsInteger(timespan.Minutes, timespan.TotalMinutes, isTimeUnitToGetTheMaximumTimeUnit), TimeUnit.Hour => GetNormalCaseTimeAsInteger(timespan.Hours, timespan.TotalHours, isTimeUnitToGetTheMaximumTimeUnit), TimeUnit.Day => GetSpecialCaseDaysAsInteger(timespan, maximumTimeUnit), - TimeUnit.Week => GetSpecialCaseWeeksAsInteger(timespan, isTimeUnitToGetTheMaximumTimeUnit), + TimeUnit.Week => GetSpecialCaseWeeksAsInteger(timespan, maximumTimeUnit), TimeUnit.Month => GetSpecialCaseMonthAsInteger(timespan, isTimeUnitToGetTheMaximumTimeUnit), TimeUnit.Year => GetSpecialCaseYearAsInteger(timespan), _ => 0 @@ -131,14 +131,25 @@ static int GetSpecialCaseMonthAsInteger(TimeSpan timespan, bool isTimeUnitToGetT static int GetSpecialCaseYearAsInteger(TimeSpan timespan) => (int) (timespan.Days / _daysInAYear); - static int GetSpecialCaseWeeksAsInteger(TimeSpan timespan, bool isTimeUnitToGetTheMaximumTimeUnit) + static int GetSpecialCaseWeeksAsInteger(TimeSpan timespan, TimeUnit maximumTimeUnit) { - if (isTimeUnitToGetTheMaximumTimeUnit || timespan.Days < _daysInAMonth) + if (maximumTimeUnit == TimeUnit.Week || timespan.Days < _daysInAMonth) { return timespan.Days / _daysInAWeek; } + var timespanRemaining = timespan; - return 0; + if (maximumTimeUnit == TimeUnit.Year) + { + timespanRemaining -= TimeSpan.FromDays(GetSpecialCaseYearAsInteger(timespan) * _daysInAYear); + } + + if (maximumTimeUnit >= TimeUnit.Month) + { + timespanRemaining -= TimeSpan.FromDays(GetSpecialCaseMonthAsInteger(timespan, false) * _daysInAMonth); + } + + return timespanRemaining.Days / _daysInAWeek; } static int GetSpecialCaseDaysAsInteger(TimeSpan timespan, TimeUnit maximumTimeUnit) @@ -148,13 +159,25 @@ static int GetSpecialCaseDaysAsInteger(TimeSpan timespan, TimeUnit maximumTimeUn return timespan.Days; } - if (timespan.Days < _daysInAMonth || maximumTimeUnit == TimeUnit.Week) + var timespanRemaining = timespan; + + if (maximumTimeUnit == TimeUnit.Year) + { + timespanRemaining -= TimeSpan.FromDays(GetSpecialCaseYearAsInteger(timespan) * _daysInAYear); + } + + if (maximumTimeUnit >= TimeUnit.Month) + { + timespanRemaining -= TimeSpan.FromDays(GetSpecialCaseMonthAsInteger(timespan, false) * _daysInAMonth); + } + + if (maximumTimeUnit >= TimeUnit.Week) { - var remainingDays = timespan.Days % _daysInAWeek; - return remainingDays; + timespanRemaining -= TimeSpan.FromDays(GetSpecialCaseWeeksAsInteger(timespan, maximumTimeUnit) * _daysInAWeek); } - return (int) (timespan.Days % _daysInAMonth); + var remainingDays = timespanRemaining.Days; + return remainingDays; } static int GetNormalCaseTimeAsInteger(int timeNumberOfUnits, double totalTimeNumberOfUnits, bool isTimeUnitToGetTheMaximumTimeUnit)