Humanizer icon indicating copy to clipboard operation
Humanizer copied to clipboard

Minimize verbosity of Timespan formatting.

Open rlightner opened this issue 8 years ago • 25 comments

Is there a way to use duration abbreviations when Humanizing a TimeSpan? I'd like to use mins instead of minutes etc..

rlightner avatar Jun 30 '16 16:06 rlightner

@rlightner Can you please show a code example of what you are doing? Thank you.

darylbehrens avatar Aug 03 '16 06:08 darylbehrens

@darylbehrens Who was that directed at?

rlightner avatar Aug 03 '16 15:08 rlightner

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.

evansaa1 avatar Sep 04 '16 11:09 evansaa1

I would like to use "m" instead of "mins or minutes"

1y, 2m, 4d, 20h, 59m, 12s

michal-ciechan avatar Mar 13 '17 22:03 michal-ciechan

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?

michal-ciechan avatar Mar 13 '17 23:03 michal-ciechan

This would be awesome to have for DateTimeOffset as well: "23s ago" instead of "23 seconds ago"...

erdmenchen avatar May 02 '17 10:05 erdmenchen

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?

odemeulder avatar May 09 '17 13:05 odemeulder

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.

MaStr11 avatar May 09 '17 15:05 MaStr11

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?

dotnetwise avatar Mar 12 '18 15:03 dotnetwise

I would like to work on this issue. Can you please assign it to me?

NageshAndani avatar Mar 19 '18 13:03 NageshAndani

It's yours.... GitHub only allows assignments to organization members, but anyone looking should see this thread.

clairernovotny avatar Mar 19 '18 13:03 clairernovotny

I've started working on this issue. I like @michal-ciechan's idea of introducing a format enum

ZacharyCouchman avatar Jan 16 '19 04:01 ZacharyCouchman

Hey @ZacharyCouchman did you get anywhere on this by any chance?

AKTheKnight avatar Mar 25 '19 21:03 AKTheKnight

Any news about it?

Janek91 avatar Mar 17 '20 13:03 Janek91

This would be awesome indeed!

tynor88 avatar Mar 19 '20 14:03 tynor88

Any progress?

chrisjshields avatar Oct 21 '20 10:10 chrisjshields

This looks like the same issue was duplicated in #969 and I made a pull request for some of the abbreviation in #990

mr-aboutin avatar Oct 21 '20 13:10 mr-aboutin

Any progress? It seems it's not available.

prince272 avatar Jan 26 '22 13:01 prince272

I'll be happy to take a PR that adds support for this feature if someone wants to take it on.

clairernovotny avatar Jan 26 '22 13:01 clairernovotny

What happened to this commit? https://github.com/Humanizr/Humanizer/pull/990/commits/8373d2ff8e7d9f126b99f7409739b9f0690c51dd

prince272 avatar Jan 26 '22 13:01 prince272

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!

clairernovotny avatar Jan 26 '22 14:01 clairernovotny

Oh okay.

prince272 avatar Jan 26 '22 14:01 prince272

It's real pity it has not been implemented yet, not even for English :-(

petrkoutnycz avatar Oct 03 '22 17:10 petrkoutnycz

It would be amazing if this could be part of the v3 milestone somehow!

derekoakley avatar Feb 22 '24 08:02 derekoakley

Would love to see this in the near future, as well.

dpaulino avatar Feb 26 '24 08:02 dpaulino

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

simonziegler avatar Mar 04 '24 15:03 simonziegler