Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix inability to get weeks from Timespan.Humanize #884

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 21 additions & 11 deletions src/Humanizer.Tests/TimeSpanHumanizeTests.cs
Expand Up @@ -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
Expand All @@ -17,15 +17,15 @@ public void AllTimeSpansMustBeUniqueForASequenceOfDays()
}

[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)
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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);
}
}
39 changes: 31 additions & 8 deletions src/Humanizer/TimeSpanHumanizeExtensions.cs
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand Down