Skip to content

Commit

Permalink
Added option to abreviate TimeSpan,
Browse files Browse the repository at this point in the history
Added Tests
Added Readme documentations
  • Loading branch information
Alex Boutin committed Oct 2, 2020
1 parent 685c23e commit 8373d2f
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 20 deletions.
11 changes: 11 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,17 @@ If words are preferred to numbers, a `toWords: true` parameter can be set to con
TimeSpan.FromMilliseconds(1299630020).Humanize(3, toWords: true) => "two weeks, one day, one hour"
````

If you would prefer and abbreviation to the complete Time Unit, a `abbreviate: true` parameter can be set to convert time units to the following abbreviations:
- Milliseconds: `ms`,
- Seconds: `s`,
- Minutes: `m`,
- Hours: `h`,
- Days: `d`,
- Weeks: `W`,
- Months: `M`,
- Years: `Y`,

Note that anything bigger then 'Days' will be capitalized.
### <a id="humanize-collections">Humanize Collections</a>
You can call `Humanize` on any `IEnumerable` to get a nicely formatted string representing the objects in the collection. By default `ToString()` will be called on each item to get its representation but a formatting function may be passed to `Humanize` instead. Additionally, a default separator is provided ("and" in English), but a different separator may be passed into `Humanize`.

Expand Down
9 changes: 9 additions & 0 deletions src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ public void TimeSpanWithMinTimeUnit(long ms, string expected, TimeUnit minUnit,
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(34390862500, "1 Y, 1 M, 2 d, 1 h, 1 m, 2 s, 500 ms", TimeUnit.Millisecond)]
[InlineData(604800000, "1 W", TimeUnit.Millisecond)]
public void TimeSpanWithAllUnitsAbb(long ms, string expected, TimeUnit minUnit)
{
var actual = TimeSpan.FromMilliseconds(ms).Humanize(7, maxUnit: TimeUnit.Year,toWords: false, abbreviated: true);
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(0, 3, "no time", true)]
[InlineData(0, 2, "no time", true)]
Expand Down
1 change: 1 addition & 0 deletions src/Humanizer.Tests/Humanizer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="1.3.0" PrivateAssets="all" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
Expand Down
5 changes: 4 additions & 1 deletion src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Humanizer.snk</AssemblyOriginatorKeyFile>
<DebugType Condition=" '$(BuildingForLiveUnitTesting)' != 'true' ">embedded</DebugType>
</PropertyGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Globalization" Version="4.3.0" />
</ItemGroup>
</Project>
10 changes: 5 additions & 5 deletions src/Humanizer/Localisation/Formatters/DefaultFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ public virtual string TimeSpanHumanize_Zero()
/// <param name="toWords"></param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">Is thrown when timeUnit is larger than TimeUnit.Week</exception>
public virtual string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false)
public virtual string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false, bool abbreviated = false)
{
return GetResourceForTimeSpan(timeUnit, unit, toWords);
return GetResourceForTimeSpan(timeUnit, unit, toWords, abbreviated);
}

private string GetResourceForDate(TimeUnit unit, Tense timeUnitTense, int count)
Expand All @@ -77,10 +77,10 @@ private string GetResourceForDate(TimeUnit unit, Tense timeUnitTense, int count)
return count == 1 ? Format(resourceKey) : Format(resourceKey, count);
}

private string GetResourceForTimeSpan(TimeUnit unit, int count, bool toWords = false)
private string GetResourceForTimeSpan(TimeUnit unit, int count, bool toWords = false, bool abbreviated = false)
{
var resourceKey = ResourceKeys.TimeSpanHumanize.GetResourceKey(unit, count, toWords);
return count == 1 ? Format(resourceKey + (toWords ? "_Words" : "")) : Format(resourceKey, count, toWords);
var resourceKey = ResourceKeys.TimeSpanHumanize.GetResourceKey(unit, count, toWords, abbreviated);
return count == 1 && !abbreviated ? Format(resourceKey + (toWords ? "_Words" : "")) : Format(resourceKey, count, toWords);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Humanizer/Localisation/Formatters/IFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ public interface IFormatter
/// <param name="unit"></param>
/// <param name="toWords"></param>
/// <returns></returns>
string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false);
string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false, bool abbreviate = false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public PolishNumberToWordsConverter(CultureInfo culture)
_culture = culture;
}

public override string Convert(long input, GrammaticalGender gender)
public string Convert(long input, GrammaticalGender gender)
{
if (input == 0)
{
Expand All @@ -55,7 +55,12 @@ public override string Convert(long input, GrammaticalGender gender)

return string.Join(" ", parts);
}


public override string Convert(long number, GrammaticalGender gender, bool addAnd = true)
{
return Convert(number, gender);
}

public override string ConvertToOrdinal(int number, GrammaticalGender gender)
{
return number.ToString(_culture);
Expand Down
1 change: 1 addition & 0 deletions src/Humanizer/Localisation/ResourceKeys.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public partial class ResourceKeys
{
private const string Single = "Single";
private const string Multiple = "Multiple";
private const string Abbreviate = "Abb";

private static void ValidateRange(int count)
{
Expand Down
5 changes: 4 additions & 1 deletion src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public static class TimeSpanHumanize
/// <param name="unit">Time unit, <see cref="TimeUnit"/>.</param>
/// <param name="count">Number of units, default is One.</param>
/// <param name="toWords">Result to words, default is false.</param>
/// <param name="abbreviate">Resulting units abbreviated => seconds: s. Default false</param>
/// <returns>Resource key, like TimeSpanHumanize_SingleMinute</returns>
public static string GetResourceKey(TimeUnit unit, int count = 1, bool toWords = false)
public static string GetResourceKey(TimeUnit unit, int count = 1, bool toWords = false, bool abbreviate = false)
{
ValidateRange(count);

Expand All @@ -30,6 +31,8 @@ public static string GetResourceKey(TimeUnit unit, int count = 1, bool toWords =
return Zero;
}

if (abbreviate) return TimeSpanFormat.FormatWith(Abbreviate, unit, "");

return TimeSpanFormat.FormatWith(count == 1 ? Single : Multiple, unit, count == 1 ? "" : "s");
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/Humanizer/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -675,4 +675,28 @@
<data name="NNW_Short" xml:space="preserve">
<value>NNW</value>
</data>
<data name="TimeSpanHumanize_AbbSecond" xml:space="preserve">
<value>{0} s</value>
</data>
<data name="TimeSpanHumanize_AbbDay" xml:space="preserve">
<value>{0} d</value>
</data>
<data name="TimeSpanHumanize_AbbHour" xml:space="preserve">
<value>{0} h</value>
</data>
<data name="TimeSpanHumanize_AbbMillisecond" xml:space="preserve">
<value>{0} ms</value>
</data>
<data name="TimeSpanHumanize_AbbMinute" xml:space="preserve">
<value>{0} m</value>
</data>
<data name="TimeSpanHumanize_AbbWeek" xml:space="preserve">
<value>{0} W</value>
</data>
<data name="TimeSpanHumanize_AbbMonth" xml:space="preserve">
<value>{0} M</value>
</data>
<data name="TimeSpanHumanize_AbbYear" xml:space="preserve">
<value>{0} Y</value>
</data>
</root>
20 changes: 10 additions & 10 deletions src/Humanizer/TimeSpanHumanizeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public static class TimeSpanHumanizeExtensions
/// <param name="collectionSeparator">The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used.</param>
/// <param name="toWords">Uses words instead of numbers if true. E.g. one day.</param>
/// <returns></returns>
public static string Humanize(this TimeSpan timeSpan, int precision = 1, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false)
public static string Humanize(this TimeSpan timeSpan, int precision = 1, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false, bool abbreviated = false)
{
return Humanize(timeSpan, precision, false, culture, maxUnit, minUnit, collectionSeparator, toWords);
return Humanize(timeSpan, precision, false, culture, maxUnit, minUnit, collectionSeparator, toWords, abbreviated);
}

/// <summary>
Expand All @@ -45,15 +45,15 @@ public static string Humanize(this TimeSpan timeSpan, int precision = 1, Culture
/// <param name="collectionSeparator">The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used.</param>
/// <param name="toWords">Uses words instead of numbers if true. E.g. one day.</param>
/// <returns></returns>
public static string Humanize(this TimeSpan timeSpan, int precision, bool countEmptyUnits, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false)
public static string Humanize(this TimeSpan timeSpan, int precision, bool countEmptyUnits, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false, bool abbreviated = false)
{
var timeParts = CreateTheTimePartsWithUpperAndLowerLimits(timeSpan, culture, maxUnit, minUnit, toWords);
var timeParts = CreateTheTimePartsWithUpperAndLowerLimits(timeSpan, culture, maxUnit, minUnit, toWords, abbreviated);
timeParts = SetPrecisionOfTimeSpan(timeParts, precision, countEmptyUnits);

return ConcatenateTimeSpanParts(timeParts, culture, collectionSeparator);
}

private static IEnumerable<string> CreateTheTimePartsWithUpperAndLowerLimits(TimeSpan timespan, CultureInfo culture, TimeUnit maxUnit, TimeUnit minUnit, bool toWords = false)
private static IEnumerable<string> CreateTheTimePartsWithUpperAndLowerLimits(TimeSpan timespan, CultureInfo culture, TimeUnit maxUnit, TimeUnit minUnit, bool toWords = false, bool abbreviated = false)
{
var cultureFormatter = Configurator.GetFormatter(culture);
var firstValueFound = false;
Expand All @@ -62,7 +62,7 @@ private static IEnumerable<string> CreateTheTimePartsWithUpperAndLowerLimits(Tim

foreach (var timeUnitType in timeUnitsEnumTypes)
{
var timepart = GetTimeUnitPart(timeUnitType,timespan, maxUnit, minUnit, cultureFormatter, toWords);
var timepart = GetTimeUnitPart(timeUnitType,timespan, maxUnit, minUnit, cultureFormatter, toWords, abbreviated);

if (timepart != null || firstValueFound)
{
Expand All @@ -85,12 +85,12 @@ private static IEnumerable<TimeUnit> GetEnumTypesForTimeUnit()
return enumTypeEnumerator.Reverse();
}

private static string GetTimeUnitPart(TimeUnit timeUnitToGet, TimeSpan timespan, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false)
private static string GetTimeUnitPart(TimeUnit timeUnitToGet, TimeSpan timespan, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false, bool abbreviated = false)
{
if (timeUnitToGet <= maximumTimeUnit && timeUnitToGet >= minimumTimeUnit)
{
var numberOfTimeUnits = GetTimeUnitNumericalValue(timeUnitToGet, timespan, maximumTimeUnit);
return BuildFormatTimePart(cultureFormatter, timeUnitToGet, numberOfTimeUnits, toWords);
return BuildFormatTimePart(cultureFormatter, timeUnitToGet, numberOfTimeUnits, toWords, abbreviated);
}
return null;
}
Expand Down Expand Up @@ -179,11 +179,11 @@ private static int GetNormalCaseTimeAsInteger(int timeNumberOfUnits, double tota
return timeNumberOfUnits;
}

private static string BuildFormatTimePart(IFormatter cultureFormatter, TimeUnit timeUnitType, int amountOfTimeUnits, bool toWords = false)
private static string BuildFormatTimePart(IFormatter cultureFormatter, TimeUnit timeUnitType, int amountOfTimeUnits, bool toWords = false, bool abbreviated = false)
{
// Always use positive units to account for negative timespans
return amountOfTimeUnits != 0
? cultureFormatter.TimeSpanHumanize(timeUnitType, Math.Abs(amountOfTimeUnits), toWords)
? cultureFormatter.TimeSpanHumanize(timeUnitType, Math.Abs(amountOfTimeUnits), toWords, abbreviated)
: null;
}

Expand Down

0 comments on commit 8373d2f

Please sign in to comment.