【问题标题】:Format A TimeSpan With Years用年份格式化 TimeSpan
【发布时间】:2013-04-04 04:35:26
【问题描述】:

我有一个具有 2 个日期属性的类:FirstDayLastDayLastDay 可以为空。我想生成一个"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;
    }
}

有没有更简洁的方法来做到这一点(但仍然可读)?

【问题讨论】:

标签: c# .net formatting .net-4.5 timespan


【解决方案1】:

TimeSpan 没有合理的“年”概念,因为它取决于起点和终点。 (月份类似 - 29 天有多少个月?嗯,这取决于...)

为了给一个无耻的插件,我的Noda Time 项目让这变得非常简单:

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

【讨论】:

  • 您绝对可以相信 Jon 在 timesdates 上的专业知识
  • 呃,额外的第三方库 - 但 Jon Skeet 的库非常令人信服。
  • 这甚至没有意义。年可以通过起点和终点来计算。它与它没有意义无关,它与微软没有支持有关。可以使用 DateTimeOffset,但他们将最小常量值设为 0001/01/01,而不是正确地允许 0000/00/00,以便人们可以计算差异。
  • @Shadowblitz16:“年数可以通过起点和终点来计算”——当然,但这不是 TimeSpan 所代表的。不,我不认为允许 0000/00/00 是“正确的”或有帮助的。
  • @Jon Skeet 我不同意。在获得两个日期之间的差异时,它实际上很有帮助。如果日、月或年没有区别,则为零,但 C# 不允许这样做,因此我必须手动单独计算它们。
【解决方案2】:
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);
}

【讨论】:

  • 将最后一行修改为 return (iYear &gt; 0 ? $"{iYear} years, " : "") + (iMonths &gt; 0 ? $"{iMonths} months, " : "") + (iDays &gt; 0 ? $"{iDays} days" : "").Trim(' ', ','); 以清理输出。很棒的简单解决方案
【解决方案3】:

我不会用TimeSpan 这样做。一旦超过天数,日期数学就会变得棘手,因为一个月中的天数和一年中的天数不再是恒定的。这可能是TimeSpan 不包含YearsMonths 的属性的原因。相反,我会确定两个 DateTime 值之间的年数/月数/天数等,并相应地显示结果。

【讨论】:

  • 也就是说,看起来 OP 已经做出了充分的妥协(出于他的目的):365.242M 每年的天数。
  • 一年没有 365.242 天。有些年份有 365 天,有些年份有 366 天。平均年有 365.242 天,如果您要比较两个特定日期,这将不起作用。如果所有可用的只是天数(这是 TimeSpan 所能做的最糟糕的事情),那么这将是一个不错的估计,但在某些情况下可能会相差一天。
  • 我同意你的观点,我只是说一个非公开的个人项目,精确度可能(合法地)退居二线。
  • 我其实是用这个来计算国债利息的。
  • 酷!你能把支票寄给我四舍五入的差额吗? ;)
【解决方案4】:

我认为这应该可行:

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;
}

【讨论】:

  • 您在这里渲染月份和日期以转换为 int,然后除以一万 - 转换为 Int。就准确度而言,这几乎相当于做dateValue1.Year - dateValue2.year。其结果将是一个数字,表示无论任何可能的 354 个尾随天数已经完全过去了多少年。从语义上讲,这也很可疑。
【解决方案5】:
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

【讨论】:

  • 最好解释一下为什么这段代码有效。在回答问题时,提供更多细节总是一个好主意。
【解决方案6】:

我需要为 Core 3 执行此操作。NodaTime 似乎依赖于 Framework 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();
    }

【讨论】:

    猜你喜欢
    • 2013-10-13
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-23
    • 2019-10-23
    • 2016-07-04
    • 2013-04-12
    • 1970-01-01
    相关资源
    最近更新 更多