Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format A TimeSpan With Years

I have a class with 2 date properties: FirstDay and LastDay. LastDay is nullable. I would like to generate a string in the format of "x year(s) y day(s)". If the total years are less than 1, I would like to omit the year section. If the total days are less than 1, I would like to omit the day section. If either years or days are 0, they should say "day/year", rather than "days/years" respectively.

Examples:
2.2 years:             "2 years 73 days"
1.002738 years:   "1 year 1 day"
0.2 years:             "73 days"
2 years:                "2 years"

What I have works, but it is long:

private const decimal DaysInAYear = 365.242M;  public string LengthInYearsAndDays {     get     {         var lastDay = this.LastDay ?? DateTime.Today;         var lengthValue = lastDay - this.FirstDay;          var builder = new StringBuilder();          var totalDays = (decimal)lengthValue.TotalDays;         var totalYears = totalDays / DaysInAYear;         var years = (int)Math.Floor(totalYears);          totalDays -= (years * DaysInAYear);         var days = (int)Math.Floor(totalDays);          Func<int, string> sIfPlural = value =>             value > 1 ? "s" : string.Empty;          if (years > 0)         {             builder.AppendFormat(                 CultureInfo.InvariantCulture,                 "{0} year{1}",                 years,                 sIfPlural(years));              if (days > 0)             {                 builder.Append(" ");             }         }          if (days > 0)         {             builder.AppendFormat(                 CultureInfo.InvariantCulture,                 "{0} day{1}",                 days,                 sIfPlural(days));         }          var length = builder.ToString();         return length;     } } 

Is there a more concise way of doing this (but still readable)?

like image 266
Dan Avatar asked Apr 11 '13 20:04

Dan


People also ask

What is TimeSpan format?

A TimeSpan format string defines the string representation of a TimeSpan value that results from a formatting operation. A custom format string consists of one or more custom TimeSpan format specifiers along with any number of literal characters.

How do you declare a TimeSpan?

By calling one of its explicit constructors. The following example initializes a TimeSpan value to a specified number of hours, minutes, and seconds. TimeSpan interval = new TimeSpan(2, 14, 18); Console. WriteLine(interval.

How do I convert DateTime to TimeSpan?

To convert a DateTime to a TimeSpan you should choose a base date/time - e.g. midnight of January 1st, 2000, and subtract it from your DateTime value (and add it when you want to convert back to DateTime ). If you simply want to convert a DateTime to a number you can use the Ticks property. Save this answer.

What is the format of DateTime?

For example, the "d" standard format string indicates that a date and time value is to be displayed using a short date pattern. For the invariant culture, this pattern is "MM/dd/yyyy". For the fr-FR culture, it is "dd/MM/yyyy". For the ja-JP culture, it is "yyyy/MM/dd".


2 Answers

A TimeSpan doesn't have a sensible concept of "years" because it depends on the start and end point. (Months is similar - how many months are there in 29 days? Well, it depends...)

To give a shameless plug, my Noda Time project makes this really simple though:

using System; using NodaTime;  public class Test {     static void Main(string[] args)     {         LocalDate start = new LocalDate(2010, 6, 19);         LocalDate end = new LocalDate(2013, 4, 11);         Period period = Period.Between(start, end,                                        PeriodUnits.Years | PeriodUnits.Days);          Console.WriteLine("Between {0} and {1} are {2} years and {3} days",                           start, end, period.Years, period.Days);     } } 

Output:

Between 19 June 2010 and 11 April 2013 are 2 years and 296 days 
like image 135
Jon Skeet Avatar answered Oct 13 '22 06:10

Jon Skeet


public string GetAgeText(DateTime birthDate) {         const double ApproxDaysPerMonth = 30.4375;         const double ApproxDaysPerYear = 365.25;          /*         The above are the average days per month/year over a normal 4 year period         We use these approximations as they are more accurate for the next century or so         After that you may want to switch over to these 400 year approximations             ApproxDaysPerMonth = 30.436875            ApproxDaysPerYear  = 365.2425             How to get theese numbers:             The are 365 days in a year, unless it is a leepyear.             Leepyear is every forth year if Year % 4 = 0             unless year % 100 == 1             unless if year % 400 == 0 then it is a leep year.              This gives us 97 leep years in 400 years.              So 400 * 365 + 97 = 146097 days.             146097 / 400      = 365.2425             146097 / 400 / 12 = 30,436875          Due to the nature of the leap year calculation, on this side of the year 2100         you can assume every 4th year is a leap year and use the other approximatiotions          */     //Calculate the span in days     int iDays = (DateTime.Now - birthDate).Days;      //Calculate years as an integer division     int iYear = (int)(iDays / ApproxDaysPerYear);      //Decrease remaing days     iDays -= (int)(iYear * ApproxDaysPerYear);      //Calculate months as an integer division     int iMonths = (int)(iDays / ApproxDaysPerMonth);      //Decrease remaing days     iDays -= (int)(iMonths * ApproxDaysPerMonth);      //Return the result as an string        return string.Format("{0} years, {1} months, {2} days", iYear, iMonths, iDays); } 
like image 22
Jens Borrisholt Avatar answered Oct 13 '22 05:10

Jens Borrisholt