给定特定的DateTime值,如何显示相对时间,例如:

2小时前3天前一个月前


当前回答

我想我应该用类和多态性来尝试一下。我以前的迭代使用了子类,结果开销太大。我已经切换到一个更灵活的委托/公共属性对象模型,这明显更好。我的代码稍微准确一点,我希望我能想出一种更好的方法来生成“几个月前”的代码,而这种方法看起来并没有过度设计。

我想我还是会坚持Jeff的if-then-cascade,因为它的代码更少,而且更简单(肯定更容易确保它按预期工作)。

对于以下代码,PrintRelativeTime.GetRelativeTime message(TimeSpan ago)返回相对时间消息(例如“昨天”)。

public class RelativeTimeRange : IComparable
{
    public TimeSpan UpperBound { get; set; }

    public delegate string RelativeTimeTextDelegate(TimeSpan timeDelta);

    public RelativeTimeTextDelegate MessageCreator { get; set; }

    public int CompareTo(object obj)
    {
        if (!(obj is RelativeTimeRange))
        {
            return 1;
        }
        // note that this sorts in reverse order to the way you'd expect, 
        // this saves having to reverse a list later
        return (obj as RelativeTimeRange).UpperBound.CompareTo(UpperBound);
    }
}

public class PrintRelativeTime
{
    private static List<RelativeTimeRange> timeRanges;

    static PrintRelativeTime()
    {
        timeRanges = new List<RelativeTimeRange>{
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromSeconds(1),
                MessageCreator = (delta) => 
                { return "one second ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromSeconds(60),
                MessageCreator = (delta) => 
                { return delta.Seconds + " seconds ago"; }

            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromMinutes(2),
                MessageCreator = (delta) => 
                { return "one minute ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromMinutes(60),
                MessageCreator = (delta) => 
                { return delta.Minutes + " minutes ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromHours(2),
                MessageCreator = (delta) => 
                { return "one hour ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromHours(24),
                MessageCreator = (delta) => 
                { return delta.Hours + " hours ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.FromDays(2),
                MessageCreator = (delta) => 
                { return "yesterday"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = DateTime.Now.Subtract(DateTime.Now.AddMonths(-1)),
                MessageCreator = (delta) => 
                { return delta.Days + " days ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = DateTime.Now.Subtract(DateTime.Now.AddMonths(-2)),
                MessageCreator = (delta) => 
                { return "one month ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = DateTime.Now.Subtract(DateTime.Now.AddYears(-1)),
                MessageCreator = (delta) => 
                { return (int)Math.Floor(delta.TotalDays / 30) + " months ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = DateTime.Now.Subtract(DateTime.Now.AddYears(-2)),
                MessageCreator = (delta) => 
                { return "one year ago"; }
            }, 
            new RelativeTimeRange
            {
                UpperBound = TimeSpan.MaxValue,
                MessageCreator = (delta) => 
                { return (int)Math.Floor(delta.TotalDays / 365.24D) + " years ago"; }
            }
        };

        timeRanges.Sort();
    }

    public static string GetRelativeTimeMessage(TimeSpan ago)
    {
        RelativeTimeRange postRelativeDateRange = timeRanges[0];

        foreach (var timeRange in timeRanges)
        {
            if (ago.CompareTo(timeRange.UpperBound) <= 0)
            {
                postRelativeDateRange = timeRange;
            }
        }

        return postRelativeDateRange.MessageCreator(ago);
    }
}

其他回答

var ts = new TimeSpan(DateTime.Now.Ticks - dt.Ticks);

通过在客户端执行此逻辑,可以减少服务器端负载。在一些Digg页面上查看源代码以供参考。它们让服务器发出一个由Javascript处理的历元时间值。这样,您就不需要管理最终用户的时区。新的服务器端代码类似于:

public string GetRelativeTime(DateTime timeStamp)
{
    return string.Format("<script>printdate({0});</script>", timeStamp.ToFileTimeUtc());
}

您甚至可以在那里添加一个NOSCRIPT块,然后执行ToString()。

简单且100%的工作解决方案。

处理过去和将来的时间。。以防万一

        public string GetTimeSince(DateTime postDate)
    {
        string message = "";
        DateTime currentDate = DateTime.Now;
        TimeSpan timegap = currentDate - postDate;

     
        if (timegap.Days > 365)
        {
            message = string.Format(L("Ago") + " {0} " + L("Years"), (((timegap.Days) / 30) / 12));                
        }
        else if (timegap.Days > 30)
        {
            message = string.Format(L("Ago") + " {0} " + L("Months"), timegap.Days/30);                
        }
        else if (timegap.Days > 0)
        {
            message = string.Format(L("Ago") + " {0} " + L("Days"), timegap.Days);
        }           
        else if (timegap.Hours > 0)
        {
            message = string.Format(L("Ago") + " {0} " + L("Hours"), timegap.Hours);
        }           
        else if (timegap.Minutes > 0)
        {
            message = string.Format(L("Ago") + " {0} " + L("Minutes"), timegap.Minutes);
        }
        else if (timegap.Seconds > 0)
        {
            message = string.Format(L("Ago") + " {0} " + L("Seconds"), timegap.Seconds);
        }

        // let's handle future times..just in case       
        else if (timegap.Days < -365)
        {
            message = string.Format(L("In") + " {0} " + L("Years"), (((Math.Abs(timegap.Days)) / 30) / 12));                
        }
        else if (timegap.Days < -30)
        {
            message = string.Format(L("In") + " {0} " + L("Months"), ((Math.Abs(timegap.Days)) / 30));                
        }
        else if (timegap.Days < 0)
        {
            message = string.Format(L("In") + " {0} " + L("Days"), Math.Abs(timegap.Days));                
        }           
      
        else if (timegap.Hours < 0)
        {
            message = string.Format(L("In") + " {0} " + L("Hours"), Math.Abs(timegap.Hours));                
        }
        else if (timegap.Minutes < 0)
        {
            message = string.Format(L("In") + " {0} " + L("Minutes"), Math.Abs(timegap.Minutes));                
        }
        else if (timegap.Seconds < 0)
        {
            message = string.Format(L("In") + " {0} " + L("Seconds"), Math.Abs(timegap.Seconds));                
        }


        else
        {
            message = "a bit";
        }

        return message;
    }

Nuget上还有一个名为Humanizr的软件包,它实际上运行得很好,并且在.NET Foundation中。

DateTime.UtcNow.AddHours(-30).Humanize() => "yesterday"
DateTime.UtcNow.AddHours(-2).Humanize() => "2 hours ago"

DateTime.UtcNow.AddHours(30).Humanize() => "tomorrow"
DateTime.UtcNow.AddHours(2).Humanize() => "2 hours from now"

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

Scott Hanselman在他的博客上写了一篇文章

文斯回答的土耳其语本地化版本。

    const int SECOND = 1;
    const int MINUTE = 60 * SECOND;
    const int HOUR = 60 * MINUTE;
    const int DAY = 24 * HOUR;
    const int MONTH = 30 * DAY;

    var ts = new TimeSpan(DateTime.UtcNow.Ticks - yourDate.Ticks);
    double delta = Math.Abs(ts.TotalSeconds);

    if (delta < 1 * MINUTE)
        return ts.Seconds + " saniye önce";

    if (delta < 45 * MINUTE)
        return ts.Minutes + " dakika önce";

    if (delta < 24 * HOUR)
        return ts.Hours + " saat önce";

    if (delta < 48 * HOUR)
        return "dün";

    if (delta < 30 * DAY)
        return ts.Days + " gün önce";

    if (delta < 12 * MONTH)
    {
        int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
        return months + " ay önce";
    }
    else
    {
        int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
        return years + " yıl önce";
    }