diff --git a/readme.md b/readme.md index d94b52fc6..4bf3de73e 100644 --- a/readme.md +++ b/readme.md @@ -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. ### Humanize Collections 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`. diff --git a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs index 0ac198cb5..e66b56359 100644 --- a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs @@ -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)] diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index bdfd5f692..cfcad6d43 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 85ff1673c..f17302c4b 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -12,5 +12,8 @@ true Humanizer.snk embedded - + + + + \ No newline at end of file diff --git a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs index 2efe8d73b..721786e61 100644 --- a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs @@ -66,9 +66,9 @@ public virtual string TimeSpanHumanize_Zero() /// /// /// Is thrown when timeUnit is larger than TimeUnit.Week - 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) @@ -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); } /// diff --git a/src/Humanizer/Localisation/Formatters/IFormatter.cs b/src/Humanizer/Localisation/Formatters/IFormatter.cs index 398fbd4fd..7703b36f8 100644 --- a/src/Humanizer/Localisation/Formatters/IFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/IFormatter.cs @@ -41,6 +41,6 @@ public interface IFormatter /// /// /// - string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false); + string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false, bool abbreviate = false); } } diff --git a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs index 8cf64dc9d..bd78aba19 100644 --- a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs @@ -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) { @@ -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); diff --git a/src/Humanizer/Localisation/ResourceKeys.Common.cs b/src/Humanizer/Localisation/ResourceKeys.Common.cs index a8643cc06..310346136 100644 --- a/src/Humanizer/Localisation/ResourceKeys.Common.cs +++ b/src/Humanizer/Localisation/ResourceKeys.Common.cs @@ -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) { diff --git a/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs b/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs index a389a49bd..7b6e838c2 100644 --- a/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs +++ b/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs @@ -20,8 +20,9 @@ public static class TimeSpanHumanize /// Time unit, . /// Number of units, default is One. /// Result to words, default is false. + /// Resulting units abbreviated => seconds: s. Default false /// Resource key, like TimeSpanHumanize_SingleMinute - 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); @@ -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"); } } diff --git a/src/Humanizer/Properties/Resources.fr.resx b/src/Humanizer/Properties/Resources.fr.resx index 7cf0af1bf..aa248c097 100644 --- a/src/Humanizer/Properties/Resources.fr.resx +++ b/src/Humanizer/Properties/Resources.fr.resx @@ -276,4 +276,76 @@ {0} milliseconde + + {0} j + + + {0} h + + + {0} ms + + + {0} m + + + {0} M + + + {0} s + + + {0} W + + + {0} Y + + + un jour + + + une heure + + + une milliseconde + + + une minute + + + un mois + + + une seconde + + + une semaine + + + un an + + + {0} semaines + + + {0} jours + + + {0} heures + + + {0} millisecondes + + + {0} minutes + + + {0} mois + + + {0} secondes + + + {0} ans + \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.resx b/src/Humanizer/Properties/Resources.resx index 678f0d5cf..805e8b048 100644 --- a/src/Humanizer/Properties/Resources.resx +++ b/src/Humanizer/Properties/Resources.resx @@ -675,4 +675,28 @@ NNW + + {0} s + + + {0} d + + + {0} h + + + {0} ms + + + {0} m + + + {0} W + + + {0} M + + + {0} Y + \ No newline at end of file diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index 4fd5a0437..86390dc10 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -28,9 +28,9 @@ public static class TimeSpanHumanizeExtensions /// The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used. /// Uses words instead of numbers if true. E.g. one day. /// - 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); } /// @@ -45,15 +45,15 @@ public static string Humanize(this TimeSpan timeSpan, int precision = 1, Culture /// The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used. /// Uses words instead of numbers if true. E.g. one day. /// - 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 CreateTheTimePartsWithUpperAndLowerLimits(TimeSpan timespan, CultureInfo culture, TimeUnit maxUnit, TimeUnit minUnit, bool toWords = false) + private static IEnumerable CreateTheTimePartsWithUpperAndLowerLimits(TimeSpan timespan, CultureInfo culture, TimeUnit maxUnit, TimeUnit minUnit, bool toWords = false, bool abbreviated = false) { var cultureFormatter = Configurator.GetFormatter(culture); var firstValueFound = false; @@ -62,7 +62,7 @@ private static IEnumerable 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) { @@ -85,12 +85,12 @@ private static IEnumerable 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; } @@ -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; }