代码之家  ›  专栏  ›  技术社区  ›  Keith Williams

TimeSpan到友好的字符串库(C#)

  •  14
  • Keith Williams  · 技术社区  · 17 年前

    • 两年零三个月零四天

    10 回复  |  直到 12 年前
        1
  •  18
  •   Gert Arnold    15 年前

    public static class TimeSpanExtensions
    {
        private enum TimeSpanElement
        {
            Millisecond,
            Second,
            Minute,
            Hour,
            Day
        }
    
        public static string ToFriendlyDisplay(this TimeSpan timeSpan, int maxNrOfElements)
        {
            maxNrOfElements = Math.Max(Math.Min(maxNrOfElements, 5), 1);
            var parts = new[]
                            {
                                Tuple.Create(TimeSpanElement.Day, timeSpan.Days),
                                Tuple.Create(TimeSpanElement.Hour, timeSpan.Hours),
                                Tuple.Create(TimeSpanElement.Minute, timeSpan.Minutes),
                                Tuple.Create(TimeSpanElement.Second, timeSpan.Seconds),
                                Tuple.Create(TimeSpanElement.Millisecond, timeSpan.Milliseconds)
                            }
                                        .SkipWhile(i => i.Item2 <= 0)
                                        .Take(maxNrOfElements);
    
            return string.Join(", ", parts.Select(p => string.Format("{0} {1}{2}", p.Item2, p.Item1, p.Item2 > 1 ? "s" : string.Empty)));
        }
    }
    

    示例(LinqPad):

    new TimeSpan(1,2,3,4,5).ToFriendlyDisplay(3).Dump();
    new TimeSpan(0,5,3,4,5).ToFriendlyDisplay(3).Dump();
    

    1 Day, 2 Hours, 3 Minutes
    5 Hours, 3 Minutes, 4 Seconds
    

    适合我,看看是否适合你。

        2
  •  14
  •   Patrick McDonald    17 年前

    这不是一个功能齐全的实现,但它应该让你足够接近。

    DateTime dtNow = DateTime.Now;
    DateTime dtYesterday = DateTime.Now.AddDays(-435.0);
    TimeSpan ts = dtNow.Subtract(dtYesterday);
    
    int years = ts.Days / 365; //no leap year accounting
    int months = (ts.Days % 365) / 30; //naive guess at month size
    int weeks = ((ts.Days % 365) % 30) / 7;
    int days = (((ts.Days % 365) % 30) % 7);
    
    StringBuilder sb = new StringBuilder();
    if(years > 0)
    {
        sb.Append(years.ToString() + " years, ");
    }
    if(months > 0)
    {
        sb.Append(months.ToString() + " months, ");
    }
    if(weeks > 0)
    {
        sb.Append(weeks.ToString() + " weeks, ");
    }
    if(days > 0)
    {
        sb.Append(days.ToString() + " days.");
    }
    string FormattedTimeSpan = sb.ToString();
    

    最后,你真的需要让别人知道一份文件将在1年、5个月、2周和3天后到期吗?你不能告诉他们文件将在1年后或5个月后到期吗?只需取最大的单位,并说出该单位的n。

        3
  •  4
  •   Mark    11 年前

    现在还有 Humanizer project

        4
  •  4
  •   DaggeJ    7 年前

    这是我对此的解决方案。它基于此线程中的其他答案,并按照原始问题中的要求(也是我所需要的)增加了对年和月的支持。

    [Test]
    public void ToFriendlyDuration_produces_expected_result()
    {
        new DateTime(2019, 5, 28).ToFriendlyDuration(null).Should().Be("Until further notice");
        new DateTime(2019, 5, 28).ToFriendlyDuration(new DateTime(2020, 5, 28)).Should().Be("1 year");
        new DateTime(2019, 5, 28).ToFriendlyDuration(new DateTime(2021, 5, 28)).Should().Be("2 years");
        new DateTime(2019, 5, 28).ToFriendlyDuration(new DateTime(2021, 8, 28)).Should().Be("2 years, 3 months");
        new DateTime(2019, 5, 28).ToFriendlyDuration(new DateTime(2019, 8, 28)).Should().Be("3 months");
        new DateTime(2019, 5, 28).ToFriendlyDuration(new DateTime(2019, 8, 31)).Should().Be("3 months, 3 days");
        new DateTime(2019, 5, 1).ToFriendlyDuration(new DateTime(2019, 5, 31)).Should().Be("30 days");
        new DateTime(2010, 5, 28).ToFriendlyDuration(new DateTime(2020, 8, 28)).Should().Be("10 years, 3 months");
        new DateTime(2010, 5, 28).ToFriendlyDuration(new DateTime(2020, 5, 29)).Should().Be("10 years, 1 day");
    }
    

    private class TermAndValue
    {
        public TermAndValue(string singular, string plural, int value)
        {
            Singular = singular;
            Plural = plural;
            Value = value;
        }
    
        public string Singular { get; }
        public string Plural { get; }
        public int Value { get; }
        public string Term => Value > 1 ? Plural : Singular;
    }
    
    public static string ToFriendlyDuration(this DateTime value, DateTime? endDate, int maxNrOfElements = 2)
    {
        if (!endDate.HasValue)
            return "Until further notice";
    
        var extendedTimeSpan = new TimeSpanWithYearAndMonth(value, endDate.Value);
        maxNrOfElements = Math.Max(Math.Min(maxNrOfElements, 5), 1);
        var termsAndValues = new[]
        {
            new TermAndValue("year", "years", extendedTimeSpan.Years),
            new TermAndValue("month", "months", extendedTimeSpan.Months),
            new TermAndValue("day", "days", extendedTimeSpan.Days),
            new TermAndValue("hour", "hours", extendedTimeSpan.Hours),
            new TermAndValue("minute", "minutes", extendedTimeSpan.Minutes)
        };
    
        var parts = termsAndValues.Where(i => i.Value != 0).Take(maxNrOfElements);
    
        return string.Join(", ", parts.Select(p => $"{p.Value} {p.Term}"));
    }
    
    internal class TimeSpanWithYearAndMonth
    {
        internal TimeSpanWithYearAndMonth(DateTime startDate, DateTime endDate)
        {
            var span = endDate - startDate;
    
            Months = 12 * (endDate.Year - startDate.Year) + (endDate.Month - startDate.Month);
            Years = Months / 12;
            Months -= Years * 12;
    
            if (Months == 0 && Years == 0)
            {
                Days = span.Days;
            }
            else
            {
                var startDateExceptYearsAndMonths = startDate.AddYears(Years);
                startDateExceptYearsAndMonths = startDateExceptYearsAndMonths.AddMonths(Months);
                Days = (endDate - startDateExceptYearsAndMonths).Days;
            }
    
            Hours = span.Hours;
            Minutes = span.Minutes;
        }
    
        public int Minutes { get; }
        public int Hours { get; }
        public int Days { get; }
        public int Years { get; }
        public int Months { get; }
    }
    
        5
  •  4
  •   Lord Darth Vader    6 年前

    我知道这很旧,但我想用一个很棒的裸体包来回答。

    Install-Package Humanizer
    

    https://www.nuget.org/packages/Humanizer

    https://github.com/MehdiK/Humanizer

    示例来自readme.md

    TimeSpan.FromMilliseconds(1299630020).Humanize(4) => "2 weeks, 1 day, 1 hour, 30 seconds"
    

    @伊恩·贝克尔需要荣誉

        6
  •  3
  •   Justin Niessner    17 年前



    我会把到期时间间隔留到日期相当接近的时候(如果你担心文件更新,可能需要30或60天)。

        7
  •  1
  •   Doug McClean    17 年前

    它可能不会做你想要的一切,但在v4中,微软将 implementing IFormattable on TimeSpan .

        8
  •  0
  •   Patrick McDonald    17 年前

    要格式化任何长于1天的时段(即月/年/十年等),仅使用Timespan对象是不够的。

        9
  •  0
  •   csabinho    7 年前

    Esteche装配

    它很容易实现:

    using EstecheAssemblies;  
    
    var date = new DateTime("2019-08-08 01:03:21");  
    var text = date.ToFriendlyTimeSpan();