用年份格式化时间跨度
本文关键字:时间跨度 格式化 | 更新日期: 2023-09-27 18:08:38
我有一个类有2个日期属性:FirstDay
和LastDay
。LastDay
为空。我想生成"x year(s) y day(s)"
格式的字符串。如果总年份小于1,我想省略年份部分。如果总天数小于1,我想省略天数部分。如果年或日均为0,则应分别表示"日/年",而不是"日/年"。
例子:
2.2年:,,,,,,,,,,,,,"2年73天"
1.002738年:,,,"1年1天"
0.2年:,,,,,,,,,,,,,"73天"
2年:,,,,,,,,,,,,,,,,"2年"
我有什么工作,但它很长:
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;
}
}
是否有更简洁的方法来做到这一点(但仍然可读)?
TimeSpan
没有"年"的合理概念,因为它取决于起点和终点。(月也是一样——29天里有几个月?嗯,这要看……)
为了给一个无耻的宣传,我的野田时间项目让它变得非常简单:
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);
}
}
输出: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);
}
我不会这样做与TimeSpan
。一旦超过天数,日期计算就会变得棘手,因为一个月和一年的天数不再是恒定的。这可能就是为什么TimeSpan
不包含Years
和Months
的属性。相反,我将确定两个DateTime
值之间的年/月/日等数,并相应地显示结果。
我想这应该行得通:
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
我需要在Core 3中这样做。NodaTime似乎依赖于框架4.7.2。我编写了下面的方法,它似乎可以将时间范围格式化为年、月和日,省略不需要的部分。
public static string ToYearsMonthsAndDays(this TimeSpan span)
{
var result = string.Empty;
var totalYears = span.Days / 364.25;
var fullYears = Math.Floor(totalYears);
var totalMonths = (span.Days - (365.24 * fullYears)) / 30;
var fullMonths = Math.Floor(totalMonths);
var totalDays = (span.Days - (365.24 * totalYears) - (30 * fullMonths)) / 30;
var fullDays = Math.Floor(totalDays);
var sb = new StringBuilder();
if (fullYears > 0)
{
if (sb.Length > 0)
sb.Append(", ");
sb.Append(fullYears + "y");
}
if (fullMonths > 0)
{
if (sb.Length > 0)
sb.Append(", ");
sb.Append(fullMonths + "m");
}
if (fullDays > 0)
{
if (sb.Length > 0)
sb.Append(", ");
sb.Append(fullDays + "d");
}
return sb.ToString();
}