Ich habe eine Klasse mit 2 Datumseigenschaften: FirstDay
und LastDay
. LastDay
ist nullfähig. Ich möchte eine Zeichenfolge im Format "x year(s) y day(s)"
erzeugen. Wenn die Gesamtjahreszahl weniger als 1 beträgt, möchte ich den Jahresabschnitt auslassen. Wenn die Gesamttage weniger als 1 sind, möchte ich den Tagesabschnitt auslassen. Wenn Jahre oder Tage 0 sind, sollten sie "Tag/Jahr" statt "Tage/Jahre" sagen.
Beispiele:
2,2 Jahre: "2 Jahre 73 Tage"
1.002738 Jahre: "1 Jahr 1 Tag"
0,2 Jahre: "73 Tage"
2 Jahre: "2 Jahre"
Was ich habe, funktioniert aber lange:
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;
}
}
Gibt es einen etwas präziseren Weg (aber immer noch lesbar)?
Eine TimeSpan
hat kein vernünftiges Konzept von "Jahren", da sie vom Start- und Endpunkt abhängt. (Monate sind ähnlich - wie viele Monate gibt es in 29 Tagen? Nun, das hängt davon ab ...)
Um einen schamlosen Plug zu geben, macht mein Noda Time -Projekt dies jedoch ganz einfach:
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);
}
}
Ausgabe:
Between 19 June 2010 and 11 April 2013 are 2 years and 296 days
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);
}
Ich würde das nicht mit einer TimeSpan
machen. Die Datumsberechnung wird schwierig, sobald Sie über Tage hinausgehen, da die Anzahl der Tage eines Monats und der Tage eines Jahres nicht mehr konstant ist. Wahrscheinlich enthält TimeSpan
keine Eigenschaften für Years
und Months
. Ich würde stattdessen die Anzahl der Jahre/Monate/Tage usw. zwischen den beiden DateTime
-Werten bestimmen und die Ergebnisse entsprechend anzeigen.
Ich denke das sollte funktionieren:
public static int DiffYears(DateTime dateValue1, DateTime dateValue2)
{
var intToCompare1 = Convert.ToInt32(dateValue1.ToString("yyyyMMdd"));
var intToCompare2 = Convert.ToInt32(dateValue2.ToString("yyyyMMdd"));
return (intToCompare2 - intToCompare1) / 10000;
}
Public Function TimeYMDBetween(StartDate As DateTime, EndDate As DateTime) As String
Dim Years As Integer = EndDate.Year - StartDate.Year
Dim Months As Integer = EndDate.Month - StartDate.Month
Dim Days As Integer = EndDate.Day - StartDate.Day
Dim DaysLastMonth As Integer
'figure out how many days were in last month
If EndDate.Month = 1 Then
DaysLastMonth = DateTime.DaysInMonth(EndDate.Year - 1, 12)
Else
DaysLastMonth = DateTime.DaysInMonth(EndDate.Year, EndDate.Month - 1)
End If
'adjust for negative days
If Days < 0 Then
Months = Months - 1
Days = Days + DaysLastMonth 'borrowing from last month
End If
'adjust for negative Months
If Months < 0 Then 'startdate hasn't happend this year yet
Years = Years - 1
Months = Months + 12
End If
Return Years.ToString() + " Years, " + Months.ToString() + " Months and " + Days.ToString() + " Days"
End Function