Write Timespan in full

Asked

Viewed 1,166 times

18

I want to write a static class (can be Extension) to represent the value of a structure TimeSpan in full, in Portuguese.

The idea is to always compare the current date (Datetime.Now) with the date the event occurred. The result of this is a TimeSpan that I want to use to write on Razor dates like this:

3 seconds ago.

Two hours ago.

2 days and 5 hours ago.

A week ago.

3 weeks and 2 days ago.

A month ago.

3 months, 2 weeks and 4 days ago.

A year ago.

2 years and 7 months ago.

How this could be done in an elegant way?

3 answers

16


There’s a response in the OS And it was accepted. I would probably improve something to look more elegant and still not doing exactly as you wish, but this is it:

public static String RelativeTime(this TimeSpan ts) {
    const int second = 1;
    const int minute = 60 * second;
    const int hour = 60 * minute;
    const int day = 24 * hour;
    const int month = 30 * day;
    double delta = Math.Abs(ts.TotalSeconds);
    //melhor se escrever só "Agora há pouco"
    if (delta < 1 * minute) return "Há " + (ts.Seconds == 1 ? "um segundo" : ts.Seconds + " segundos");
    if (delta < 2 * minute) return "Há um minuto";
    if (delta < 45 * minute) return "Há " + ts.Minutes + " minutos";
    if (delta < 90 * minute) return "Há uma hora";
    if (delta < 24 * hour) return "Há " + ts.Hours + " horas";
    if (delta < 48 * hour) return "ontem";
    if (delta < 30 * day) return "Há " + ts.Days + " dias";
    if (delta < 12 * month) {
        var months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
        return "Há " + (months <= 1 ? "um mês" : months + " meses");
    } else {
        var years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
        return "Há " + (years <= 1 ? "um ano" : years + " anos");
    }
}

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

It has some problems but solves most situations. One of the problems is that it doesn’t deal with whether the value is future. The rounding calibration is rigid, finally, can make several improvements in the algorithm, as well as improve the style of the code.

There’s also this package that helps to "humanize" all this data.

There is an alternative that considers part-time as shown in the question. The implementation does not take into account some small displacements, plural, hours, etc., as reported in original response in the OS:

var dtNow = DateTime.Now;
var dtYesterday = DateTime.Now.AddDays(-435.0);
var ts = dtNow.Subtract(dtYesterday);
var years = ts.Days / 365; //no leap year accounting
var months = (ts.Days % 365) / 30; //naive guess at month size
var weeks = ((ts.Days % 365) % 30) / 7;
var days = (((ts.Days % 365) % 30) % 7);
var sb = new StringBuilder("Há ");
if(years > 0) sb.Append(years.ToString() + " anos, ");
if(months > 0) sb.Append(months.ToString() + " meses, ");
if(weeks > 0) sb.Append(weeks.ToString() + " semanas, ");
if(days > 0) sb.Append(days.ToString() + " dias.");
var FormattedTimeSpan = sb.ToString();

I put in the Github for future reference.

  • I can improve. Maybe I need to change the data source. Do you have anything you need besides putting the fractions of time? I think using this recursively makes it easy since you can go on subtracting what you have already found and picking up the rest of the time that has not yet been transformed for a new execution. This seems to be closer: http://stackoverflow.com/a/1138852/221800 if I take better care, I can adapt and put in the answer.

  • No need. This is great. Thank you!

14

Just out of curiosity, I implemented the @Maniero response as a Extension, next:

public static class RelativeTimeExtensions
{
    public static String PorExtenso(this TimeSpan timeSpan)
    {
        const int SECOND = 1;
        const int MINUTE = 60 * SECOND;
        const int HOUR = 60 * MINUTE;
        const int DAY = 24 * HOUR;
        const int MONTH = 30 * DAY;

        // var ts = new TimeSpan(DateTime.UtcNow.Ticks - yourDate.Ticks);
        double delta = Math.Abs(timeSpan.TotalSeconds);

        if (delta < 1 * MINUTE)
        {
            return "Há " + (timeSpan.Seconds == 1 ? "um segundo" : timeSpan.Seconds + " segundos");
        }
        if (delta < 2 * MINUTE)
        {
            return "Há um minuto";
        }
        if (delta < 45 * MINUTE)
        {
            return "Há " + timeSpan.Minutes + " minutos";
        }
        if (delta < 90 * MINUTE)
        {
            return "Há uma hora";
        }
        if (delta < 24 * HOUR)
        {
            return "Há " + timeSpan.Hours + " horas";
        }
        if (delta < 48 * HOUR)
        {
            return "ontem";
        }
        if (delta < 30 * DAY)
        {
            return "Há " + timeSpan.Days + " dias";
        }
        if (delta < 12 * MONTH)
        {
            int months = Convert.ToInt32(Math.Floor((double)timeSpan.Days / 30));
            return "Há " + (months <= 1 ? "um mês" : months + " meses");
        }
        else
        {
            int years = Convert.ToInt32(Math.Floor((double)timeSpan.Days / 365));
            return "Há " + (years <= 1 ? "um ano" : years + " anos");
        }
    }
}

Usage (in the Razor):

@((DateTime.Now - dataDeComparacao).PorExtenso())

4

Just to add more content this is my somewhat similar solution, where I pass a datetime as a parameter:

public static string TimeAgo(DateTime dt)
    {
        TimeSpan span = DateTime.Now - dt;

        if (span.Days > 365)
        {
            int years = (span.Days / 365);

            if (span.Days % 365 != 0)
                years += 1;

            return String.Format("Há {0} {1} atrás",

            years, years == 1 ? "dia" : "dias");
        }
        if (span.Days > 30)
        {
            int months = (span.Days / 30);

            if (span.Days % 31 != 0)
                months += 1;

            return String.Format("Há {0} {1} atrás",

            months, months == 1 ? "mês" : "Mês");
        }
        if (span.Days > 0)
            return String.Format("Há {0} {1} Atrás",

            span.Days, span.Days == 1 ? "Dia" : "Dias");

        if (span.Hours > 0)
            return String.Format("Há {0} {1} Atrás",

            span.Hours, span.Hours == 1 ? "hora" : "horas");

        if (span.Minutes > 0)
            return String.Format("Há {0} {1} Atrás",

            span.Minutes, span.Minutes == 1 ? "minutos" : "minutos");

        if (span.Seconds > 5)
            return String.Format("Há {0} segundos atrás", span.Seconds);

        if (span.Seconds <= 5)
            return "agora";

        return string.Empty;
    }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.