Humanizer
Humanizer copied to clipboard
Minimize verbosity of Timespan formatting.
Is there a way to use duration abbreviations when Humanizing a TimeSpan? I'd like to use mins instead of minutes etc..
@rlightner Can you please show a code example of what you are doing? Thank you.
@darylbehrens Who was that directed at?
I'm taking a look at this but when attempting to edit Resources.resx I get the following error: Could not resolve mscorlib for target framework '.NETPortable,Version=v5.0'. This can happen if the target framework is not installed or if the framework moniker is incorrectly formatted. Any idea what might be causing this for me? What should Build Action and Copy To Output directory be set to when dealing with these resx files? Using VS2015 Update 3.
I would like to use "m" instead of "mins or minutes"
1y, 2m, 4d, 20h, 59m, 12s
I am thinking of adding the functionality of using short(er) variants.
e.g.
TimeSpan.FromSeconds().Humanize(short: true)
or
TimeSpan.FromSeconds().Humanize(format: TimeSpanFormat.Short)
possibly giving
public enum TimeSpanFormat
{
Normal,
Short, // 1 yr, 2 mos, 2 days, 5 hrs, 56 mins, 12 secs - Original request?
Single, // 1y, 2m, 2d, 4h, 56m, 12s
}
I guess these could be as separate keys in the resources, with falling back to the previous if one is not found?
This would be awesome to have for DateTimeOffset as well: "23s ago" instead of "23 seconds ago"...
I would love to work on this issue. One question though, how would we deal with all the languages? Let's say we had a new parameter indicating the format, and the new key did not exist for a particular language. Would it revert to the default format?
If you look at the current language support you will find 145 entries:
RessourceKey | Value |
---|---|
DateHumanize_MultipleDaysAgo | {0} days ago |
DateHumanize_MultipleDaysAgo_Above20 | {0} days ago |
DateHumanize_MultipleDaysAgo_Dual | {0} days ago |
DateHumanize_MultipleDaysAgo_Plural | {0} days ago |
DateHumanize_MultipleDaysAgo_Singular | {0} day ago |
DateHumanize_MultipleDaysAgo_TrialQuadral | {0} days ago |
DateHumanize_MultipleDaysFromNow | {0} days from now |
DateHumanize_MultipleDaysFromNow_Dual | {0} days from now |
DateHumanize_MultipleDaysFromNow_Plural | {0} days from now |
DateHumanize_MultipleDaysFromNow_Singular | {0} day from now |
DateHumanize_MultipleDaysFromNow_TrialQuadral | {0} days from now |
DateHumanize_MultipleHoursAgo | {0} hours ago |
DateHumanize_MultipleHoursAgo_Above20 | {0} hours ago |
DateHumanize_MultipleHoursAgo_Dual | {0} hours ago |
DateHumanize_MultipleHoursAgo_DualTrialQuadral | {0} hours ago |
DateHumanize_MultipleHoursAgo_Plural | {0} hours ago |
DateHumanize_MultipleHoursAgo_Singular | {0} hour ago |
DateHumanize_MultipleHoursAgo_TrialQuadral | {0} hours ago |
DateHumanize_MultipleHoursFromNow | {0} hours from now |
DateHumanize_MultipleHoursFromNow_Dual | {0} hours from now |
DateHumanize_MultipleHoursFromNow_DualTrialQuadral | {0} hours from now |
DateHumanize_MultipleHoursFromNow_Plural | {0} hours from now |
DateHumanize_MultipleHoursFromNow_Singular | {0} hour from now |
DateHumanize_MultipleHoursFromNow_TrialQuadral | {0} hours from now |
DateHumanize_MultipleMinutesAgo | {0} minutes ago |
DateHumanize_MultipleMinutesAgo_Above20 | {0} minutes ago |
DateHumanize_MultipleMinutesAgo_Dual | {0} minutes ago |
DateHumanize_MultipleMinutesAgo_DualTrialQuadral | {0} minutes ago |
DateHumanize_MultipleMinutesAgo_Plural | {0} minutes ago |
DateHumanize_MultipleMinutesAgo_Singular | {0} minute ago |
DateHumanize_MultipleMinutesAgo_TrialQuadral | {0} minutes ago |
DateHumanize_MultipleMinutesFromNow | {0} minutes from now |
DateHumanize_MultipleMinutesFromNow_Dual | {0} minutes from now |
DateHumanize_MultipleMinutesFromNow_DualTrialQuadral | {0} minutes from now |
DateHumanize_MultipleMinutesFromNow_Plural | {0} minutes from now |
DateHumanize_MultipleMinutesFromNow_Singular | {0} minute from now |
DateHumanize_MultipleMinutesFromNow_TrialQuadral | {0} minutes from now |
DateHumanize_MultipleMonthsAgo | {0} months ago |
DateHumanize_MultipleMonthsAgo_Dual | {0} months ago |
DateHumanize_MultipleMonthsAgo_DualTrialQuadral | {0} months ago |
DateHumanize_MultipleMonthsAgo_Plural | {0} months ago |
DateHumanize_MultipleMonthsAgo_Singular | {0} month ago |
DateHumanize_MultipleMonthsAgo_TrialQuadral | {0} months ago |
DateHumanize_MultipleMonthsFromNow | {0} months from now |
DateHumanize_MultipleMonthsFromNow_Dual | {0} months from now |
DateHumanize_MultipleMonthsFromNow_DualTrialQuadral | {0} months from now |
DateHumanize_MultipleMonthsFromNow_Plural | {0} months from now |
DateHumanize_MultipleMonthsFromNow_Singular | {0} month from now |
DateHumanize_MultipleMonthsFromNow_TrialQuadral | {0} months from now |
DateHumanize_MultipleSecondsAgo | {0} seconds ago |
DateHumanize_MultipleSecondsAgo_Above20 | {0} seconds ago |
DateHumanize_MultipleSecondsAgo_Dual | {0} seconds ago |
DateHumanize_MultipleSecondsAgo_DualTrialQuadral | {0} seconds ago |
DateHumanize_MultipleSecondsAgo_Plural | {0} seconds ago |
DateHumanize_MultipleSecondsAgo_Singular | {0} second ago |
DateHumanize_MultipleSecondsAgo_TrialQuadral | {0} seconds ago |
DateHumanize_MultipleSecondsFromNow | {0} seconds from now |
DateHumanize_MultipleSecondsFromNow_Dual | {0} seconds from now |
DateHumanize_MultipleSecondsFromNow_DualTrialQuadral | {0} seconds from now |
DateHumanize_MultipleSecondsFromNow_Plural | {0} seconds from now |
DateHumanize_MultipleSecondsFromNow_Singular | {0} second from now |
DateHumanize_MultipleSecondsFromNow_TrialQuadral | {0} seconds from now |
DateHumanize_MultipleYearsAgo | {0} years ago |
DateHumanize_MultipleYearsAgo_Above20 | {0} years ago |
DateHumanize_MultipleYearsAgo_Dual | {0} years ago |
DateHumanize_MultipleYearsAgo_DualTrialQuadral | {0} years from ago |
DateHumanize_MultipleYearsAgo_Plural | {0} years ago |
DateHumanize_MultipleYearsAgo_Singular | {0} year ago |
DateHumanize_MultipleYearsAgo_TrialQuadral | {0} years ago |
DateHumanize_MultipleYearsFromNow | {0} years from now |
DateHumanize_MultipleYearsFromNow_Dual | {0} years from now |
DateHumanize_MultipleYearsFromNow_DualTrialQuadral | {0} years from now |
DateHumanize_MultipleYearsFromNow_Plural | {0} years from now |
DateHumanize_MultipleYearsFromNow_Singular | {0} year from now |
DateHumanize_MultipleYearsFromNow_TrialQuadral | {0} years from now |
DateHumanize_Never | never |
DateHumanize_Now | now |
DateHumanize_SingleDayAgo | yesterday |
DateHumanize_SingleDayFromNow | tomorrow |
DateHumanize_SingleHourAgo | an hour ago |
DateHumanize_SingleHourFromNow | an hour from now |
DateHumanize_SingleMinuteAgo | a minute ago |
DateHumanize_SingleMinuteFromNow | a minute from now |
DateHumanize_SingleMonthAgo | one month ago |
DateHumanize_SingleMonthFromNow | one month from now |
DateHumanize_SingleSecondAgo | one second ago |
DateHumanize_SingleSecondFromNow | one second from now |
DateHumanize_SingleYearAgo | one year ago |
DateHumanize_SingleYearFromNow | one year from now |
TimeSpanHumanize_MultipleDays | {0} days |
TimeSpanHumanize_MultipleDays_Dual | {0} days |
TimeSpanHumanize_MultipleDays_Plural | {0} days |
TimeSpanHumanize_MultipleDays_Singular | {0} day |
TimeSpanHumanize_MultipleDays_TrialQuadral | {0} days |
TimeSpanHumanize_MultipleHours | {0} hours |
TimeSpanHumanize_MultipleHours_Dual | {0} hours |
TimeSpanHumanize_MultipleHours_DualTrialQuadral | {0} hours |
TimeSpanHumanize_MultipleHours_Plural | {0} hours |
TimeSpanHumanize_MultipleHours_Singular | {0} hour |
TimeSpanHumanize_MultipleHours_TrialQuadral | {0} hours |
TimeSpanHumanize_MultipleMilliseconds | {0} milliseconds |
TimeSpanHumanize_MultipleMilliseconds_Dual | {0} milliseconds |
TimeSpanHumanize_MultipleMilliseconds_DualTrialQuadral | {0} milliseconds |
TimeSpanHumanize_MultipleMilliseconds_Plural | {0} milliseconds |
TimeSpanHumanize_MultipleMilliseconds_Singular | {0} millisecond |
TimeSpanHumanize_MultipleMilliseconds_TrialQuadral | {0} milliseconds |
TimeSpanHumanize_MultipleMinutes | {0} minutes |
TimeSpanHumanize_MultipleMinutes_Dual | {0} minutes |
TimeSpanHumanize_MultipleMinutes_DualTrialQuadral | {0} minutes |
TimeSpanHumanize_MultipleMinutes_Plural | {0} minutes |
TimeSpanHumanize_MultipleMinutes_Singular | {0} minute |
TimeSpanHumanize_MultipleMinutes_TrialQuadral | {0} minutes |
TimeSpanHumanize_MultipleMonths | {0} months |
TimeSpanHumanize_MultipleMonths_Dual | {0} months |
TimeSpanHumanize_MultipleMonths_DualTrialQuadral | {0} months |
TimeSpanHumanize_MultipleMonths_Plural | {0} months |
TimeSpanHumanize_MultipleMonths_Singular | {0} months |
TimeSpanHumanize_MultipleMonths_TrialQuadral | {0} months |
TimeSpanHumanize_MultipleSeconds | {0} seconds |
TimeSpanHumanize_MultipleSeconds_Dual | {0} seconds |
TimeSpanHumanize_MultipleSeconds_DualTrialQuadral | {0} seconds |
TimeSpanHumanize_MultipleSeconds_Plural | {0} seconds |
TimeSpanHumanize_MultipleSeconds_Singular | {0} second |
TimeSpanHumanize_MultipleSeconds_TrialQuadral | {0} seconds |
TimeSpanHumanize_MultipleWeeks | {0} weeks |
TimeSpanHumanize_MultipleWeeks_Dual | {0} weeks |
TimeSpanHumanize_MultipleWeeks_DualTrialQuadral | {0} weeks |
TimeSpanHumanize_MultipleWeeks_Plural | {0} weeks |
TimeSpanHumanize_MultipleWeeks_Singular | {0} week |
TimeSpanHumanize_MultipleWeeks_TrialQuadral | {0} weeks |
TimeSpanHumanize_MultipleYears | {0} years |
TimeSpanHumanize_MultipleYears_Dual | {0} years |
TimeSpanHumanize_MultipleYears_DualTrialQuadral | {0} years |
TimeSpanHumanize_MultipleYears_Plural | {0} years |
TimeSpanHumanize_MultipleYears_Singular | {0} years |
TimeSpanHumanize_MultipleYears_TrialQuadral | {0} years |
TimeSpanHumanize_SingleDay | 1 day |
TimeSpanHumanize_SingleHour | 1 hour |
TimeSpanHumanize_SingleMillisecond | 1 millisecond |
TimeSpanHumanize_SingleMinute | 1 minute |
TimeSpanHumanize_SingleMonth | 1 month |
TimeSpanHumanize_SingleSecond | 1 second |
TimeSpanHumanize_SingleWeek | 1 week |
TimeSpanHumanize_SingleYear | 1 year |
TimeSpanHumanize_Zero | no time |
All of these have to be shortened so there will be 145 entries for TimeSpanFormat.Short
and 145 entries for TimeSpanFormat.Single
. This will not scale for 50+ languages currently supported (To be fair: Actually for most languages it is sufficient to translate 45 entries).
If you have a closer look it is possible to get rid of all the DateHumanize_
entries because those are TimeSpanHumanize_
entries with ago
or from now
added (other languages prefix those). Therefore it would be sufficient to have DateHumanize_FromNow_Format -> {0} from now
and DateHumanize_Ago_Format -> {0} ago
. This would also help to implement #498 (Currently it is not possible to output 'You are 40 years old.' based on a birth-date and a reference date).
I would suggest to discuss the design of the localization before we add a few 100 more entries to the resource files.
One year passed, nothing moved on (about this). Even with 50+ languages to translate in, is far better than 0 support for it.
Any plan to actually support this?
I would like to work on this issue. Can you please assign it to me?
It's yours.... GitHub only allows assignments to organization members, but anyone looking should see this thread.
I've started working on this issue. I like @michal-ciechan's idea of introducing a format enum
Hey @ZacharyCouchman did you get anywhere on this by any chance?
Any news about it?
This would be awesome indeed!
Any progress?
This looks like the same issue was duplicated in #969 and I made a pull request for some of the abbreviation in #990
Any progress? It seems it's not available.
I'll be happy to take a PR that adds support for this feature if someone wants to take it on.
What happened to this commit? https://github.com/Humanizr/Humanizer/pull/990/commits/8373d2ff8e7d9f126b99f7409739b9f0690c51dd
I have no idea and haven't looked. All I can see is that the PR looked like it needed fixing: https://github.com/Humanizr/Humanizer/pull/990. If someone wants to finish it off, that'd be great!
Oh okay.
It's real pity it has not been implemented yet, not even for English :-(
It would be amazing if this could be part of the v3 milestone somehow!
Would love to see this in the near future, as well.
I just did this for my purposes with some regex. There's a sample program below that I'd love to claim credit for, but that goes to Copilot!
using Humanizer;
using System.Globalization;
using System.Text.RegularExpressions;
TimeSpan t = new DateTime(2024, 3, 25) - new DateTime(2024, 2, 13);
CultureInfo culture = new("de-DE");
var longString = t.Humanize(3, culture, maxUnit: Humanizer.Localisation.TimeUnit.Month);
string pattern = @"(\d+)\s+(\w)\w*";
string shortString = Regex.Replace(longString, pattern, ReplaceWithFirstLetter);
Console.WriteLine("Long timespan: " + longString);
Console.WriteLine("Short timespan: " + shortString);
static string ReplaceWithFirstLetter(Match match)
{
var digits = match.Groups[1].Value;
var firstLetter = match.Groups[2].Value;
return $"{digits}{firstLetter}";
}
And here's the output:
Long timespan: 1 Monat, 10 Tage
Short timespan: 1M, 10T