给定特定的DateTime值,如何显示相对时间,例如:
2小时前3天前一个月前
给定特定的DateTime值,如何显示相对时间,例如:
2小时前3天前一个月前
当前回答
这是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";
其他回答
我想我应该用类和多态性来尝试一下。我以前的迭代使用了子类,结果开销太大。我已经切换到一个更灵活的委托/公共属性对象模型,这明显更好。我的代码稍微准确一点,我希望我能想出一种更好的方法来生成“几个月前”的代码,而这种方法看起来并没有过度设计。
我想我还是会坚持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=新时间跨度(DateTime.UtcNow.Ticks-dt.Ticks);
对DateTime执行减法仍会返回TimeSpan。
所以你可以这样做
(DateTime.UtcNow - dt).TotalSeconds
我也很惊讶地看到常数用手相乘,然后注释加上乘法。这是错误的优化吗?
Jeff,您的代码很好,但使用常量可以更清晰(如代码完成中所建议的)。
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 == 1 ? "one second ago" : ts.Seconds + " seconds ago";
if (delta < 2 * MINUTE)
return "a minute ago";
if (delta < 45 * MINUTE)
return ts.Minutes + " minutes ago";
if (delta < 90 * MINUTE)
return "an hour ago";
if (delta < 24 * HOUR)
return ts.Hours + " hours ago";
if (delta < 48 * HOUR)
return "yesterday";
if (delta < 30 * DAY)
return ts.Days + " days ago";
if (delta < 12 * MONTH)
{
int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
return months <= 1 ? "one month ago" : months + " months ago";
}
else
{
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
return years <= 1 ? "one year ago" : years + " years ago";
}
这是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";
在PHP中,我是这样做的:
<?php
function timesince($original) {
// array of time period chunks
$chunks = array(
array(60 * 60 * 24 * 365 , 'year'),
array(60 * 60 * 24 * 30 , 'month'),
array(60 * 60 * 24 * 7, 'week'),
array(60 * 60 * 24 , 'day'),
array(60 * 60 , 'hour'),
array(60 , 'minute'),
);
$today = time(); /* Current unix time */
$since = $today - $original;
if($since > 604800) {
$print = date("M jS", $original);
if($since > 31536000) {
$print .= ", " . date("Y", $original);
}
return $print;
}
// $j saves performing the count function each time around the loop
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
// finding the biggest chunk (if the chunk fits, break)
if (($count = floor($since / $seconds)) != 0) {
break;
}
}
$print = ($count == 1) ? '1 '.$name : "$count {$name}s";
return $print . " ago";
} ?>