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

2小时前3天前一个月前


当前回答

// Calculate total days in current year
int daysInYear;

for (var i = 1; i <= 12; i++)
    daysInYear += DateTime.DaysInMonth(DateTime.Now.Year, i);

// Past date
DateTime dateToCompare = DateTime.Now.Subtract(TimeSpan.FromMinutes(582));

// Calculate difference between current date and past date
double diff = (DateTime.Now - dateToCompare).TotalMilliseconds;

TimeSpan ts = TimeSpan.FromMilliseconds(diff);

var years = ts.TotalDays / daysInYear; // Years
var months = ts.TotalDays / (daysInYear / (double)12); // Months
var weeks = ts.TotalDays / 7; // Weeks
var days = ts.TotalDays; // Days
var hours = ts.TotalHours; // Hours
var minutes = ts.TotalMinutes; // Minutes
var seconds = ts.TotalSeconds; // Seconds

if (years >= 1)
    Console.WriteLine(Math.Round(years, 0) + " year(s) ago");
else if (months >= 1)
    Console.WriteLine(Math.Round(months, 0) + " month(s) ago");
else if (weeks >= 1)
    Console.WriteLine(Math.Round(weeks, 0) + " week(s) ago");
else if (days >= 1)
    Console.WriteLine(Math.Round(days, 0) + " days(s) ago");
else if (hours >= 1)
    Console.WriteLine(Math.Round(hours, 0) + " hour(s) ago");
else if (minutes >= 1)
    Console.WriteLine(Math.Round(minutes, 0) + " minute(s) ago");
else if (seconds >= 1)
    Console.WriteLine(Math.Round(seconds, 0) + " second(s) ago");

Console.ReadLine();

其他回答

我是这样做的

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

if (delta < 60)
{
  return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago";
}
if (delta < 60 * 2)
{
  return "a minute ago";
}
if (delta < 45 * 60)
{
  return ts.Minutes + " minutes ago";
}
if (delta < 90 * 60)
{
  return "an hour ago";
}
if (delta < 24 * 60 * 60)
{
  return ts.Hours + " hours ago";
}
if (delta < 48 * 60 * 60)
{
  return "yesterday";
}
if (delta < 30 * 24 * 60 * 60)
{
  return ts.Days + " days ago";
}
if (delta < 12 * 30 * 24 * 60 * 60)
{
  int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
  return months <= 1 ? "one month ago" : months + " months ago";
}
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
return years <= 1 ? "one year ago" : years + " years ago";

建议?评论?如何改进此算法?

public static string RelativeDate(DateTime theDate)
{
    Dictionary<long, string> thresholds = new Dictionary<long, string>();
    int minute = 60;
    int hour = 60 * minute;
    int day = 24 * hour;
    thresholds.Add(60, "{0} seconds ago");
    thresholds.Add(minute * 2, "a minute ago");
    thresholds.Add(45 * minute, "{0} minutes ago");
    thresholds.Add(120 * minute, "an hour ago");
    thresholds.Add(day, "{0} hours ago");
    thresholds.Add(day * 2, "yesterday");
    thresholds.Add(day * 30, "{0} days ago");
    thresholds.Add(day * 365, "{0} months ago");
    thresholds.Add(long.MaxValue, "{0} years ago");
    long since = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
    foreach (long threshold in thresholds.Keys) 
    {
        if (since < threshold) 
        {
            TimeSpan t = new TimeSpan((DateTime.Now.Ticks - theDate.Ticks));
            return string.Format(thresholds[threshold], (t.Days > 365 ? t.Days / 365 : (t.Days > 0 ? t.Days : (t.Hours > 0 ? t.Hours : (t.Minutes > 0 ? t.Minutes : (t.Seconds > 0 ? t.Seconds : 0))))).ToString());
        }
    }
    return "";
}

我更喜欢这个版本,因为它简洁,并且能够添加新的刻度点。这可以用Timespan的Latest()扩展来封装,而不是长的1行,但为了发布的简洁,这可以。这修复了一小时前、一小时前的问题,提供了一个小时直到两小时过去

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

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

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

这是stackoverflow使用的算法,但使用了错误修复(没有“一小时前”)的perlish伪代码进行了更简洁的重写。该函数在秒前取一个(正数),并返回一个人类友好的字符串,如“3小时前”或“昨天”。

agoify($delta)
  local($y, $mo, $d, $h, $m, $s);
  $s = floor($delta);
  if($s<=1)            return "a second ago";
  if($s<60)            return "$s seconds ago";
  $m = floor($s/60);
  if($m==1)            return "a minute ago";
  if($m<45)            return "$m minutes ago";
  $h = floor($m/60);
  if($h==1)            return "an hour ago";
  if($h<24)            return "$h hours ago";
  $d = floor($h/24);
  if($d<2)             return "yesterday";
  if($d<30)            return "$d days ago";
  $mo = floor($d/30);
  if($mo<=1)           return "a month ago";
  $y = floor($mo/12);
  if($y<1)             return "$mo months ago";
  if($y==1)            return "a year ago";
  return "$y years ago";

@杰夫

var ts=新时间跨度(DateTime.UtcNow.Ticks-dt.Ticks);

对DateTime执行减法仍会返回TimeSpan。

所以你可以这样做

(DateTime.UtcNow - dt).TotalSeconds

我也很惊讶地看到常数用手相乘,然后注释加上乘法。这是错误的优化吗?