给定特定的DateTime值,如何显示相对时间,例如:
2小时前3天前一个月前
给定特定的DateTime值,如何显示相对时间,例如:
2小时前3天前一个月前
当前回答
/**
* {@code date1} has to be earlier than {@code date2}.
*/
public static String relativize(Date date1, Date date2) {
assert date2.getTime() >= date1.getTime();
long duration = date2.getTime() - date1.getTime();
long converted;
if ((converted = TimeUnit.MILLISECONDS.toDays(duration)) > 0) {
return String.format("%d %s ago", converted, converted == 1 ? "day" : "days");
} else if ((converted = TimeUnit.MILLISECONDS.toHours(duration)) > 0) {
return String.format("%d %s ago", converted, converted == 1 ? "hour" : "hours");
} else if ((converted = TimeUnit.MILLISECONDS.toMinutes(duration)) > 0) {
return String.format("%d %s ago", converted, converted == 1 ? "minute" : "minutes");
} else if ((converted = TimeUnit.MILLISECONDS.toSeconds(duration)) > 0) {
return String.format("%d %s ago", converted, converted == 1 ? "second" : "seconds");
} else {
return "just now";
}
}
其他回答
我想我应该用类和多态性来尝试一下。我以前的迭代使用了子类,结果开销太大。我已经切换到一个更灵活的委托/公共属性对象模型,这明显更好。我的代码稍微准确一点,我希望我能想出一种更好的方法来生成“几个月前”的代码,而这种方法看起来并没有过度设计。
我想我还是会坚持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);
}
}
这里是Jeffs Script for PHP的重写:
define("SECOND", 1);
define("MINUTE", 60 * SECOND);
define("HOUR", 60 * MINUTE);
define("DAY", 24 * HOUR);
define("MONTH", 30 * DAY);
function relativeTime($time)
{
$delta = time() - $time;
if ($delta < 1 * MINUTE)
{
return $delta == 1 ? "one second ago" : $delta . " seconds ago";
}
if ($delta < 2 * MINUTE)
{
return "a minute ago";
}
if ($delta < 45 * MINUTE)
{
return floor($delta / MINUTE) . " minutes ago";
}
if ($delta < 90 * MINUTE)
{
return "an hour ago";
}
if ($delta < 24 * HOUR)
{
return floor($delta / HOUR) . " hours ago";
}
if ($delta < 48 * HOUR)
{
return "yesterday";
}
if ($delta < 30 * DAY)
{
return floor($delta / DAY) . " days ago";
}
if ($delta < 12 * MONTH)
{
$months = floor($delta / DAY / 30);
return $months <= 1 ? "one month ago" : $months . " months ago";
}
else
{
$years = floor($delta / DAY / 365);
return $years <= 1 ? "one year ago" : $years . " years ago";
}
}
@杰夫
我知道你的有点长。然而,随着对“昨天”和“几年”的支持,它似乎确实更为有力。但根据我的经验,当使用此选项时,用户最有可能在前30天内查看内容。只有真正的铁杆人才会在这之后出现。所以,我通常选择保持简短。
这是我目前在我的一个网站上使用的方法。这只返回相对的日期、小时和时间。然后用户必须在输出中加上“ago”。
public static string ToLongString(this TimeSpan time)
{
string output = String.Empty;
if (time.Days > 0)
output += time.Days + " days ";
if ((time.Days == 0 || time.Days == 1) && time.Hours > 0)
output += time.Hours + " hr ";
if (time.Days == 0 && time.Minutes > 0)
output += time.Minutes + " min ";
if (output.Length == 0)
output += time.Seconds + " sec";
return output.Trim();
}
文斯回答的土耳其语本地化版本。
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";
}
下面是我作为DateTime类的扩展方法添加的一个实现,它处理未来和过去的日期,并提供了一个近似选项,允许您指定要查找的详细程度(“3小时前”与“3小时、23分钟、12秒前”):
using System.Text;
/// <summary>
/// Compares a supplied date to the current date and generates a friendly English
/// comparison ("5 days ago", "5 days from now")
/// </summary>
/// <param name="date">The date to convert</param>
/// <param name="approximate">When off, calculate timespan down to the second.
/// When on, approximate to the largest round unit of time.</param>
/// <returns></returns>
public static string ToRelativeDateString(this DateTime value, bool approximate)
{
StringBuilder sb = new StringBuilder();
string suffix = (value > DateTime.Now) ? " from now" : " ago";
TimeSpan timeSpan = new TimeSpan(Math.Abs(DateTime.Now.Subtract(value).Ticks));
if (timeSpan.Days > 0)
{
sb.AppendFormat("{0} {1}", timeSpan.Days,
(timeSpan.Days > 1) ? "days" : "day");
if (approximate) return sb.ToString() + suffix;
}
if (timeSpan.Hours > 0)
{
sb.AppendFormat("{0}{1} {2}", (sb.Length > 0) ? ", " : string.Empty,
timeSpan.Hours, (timeSpan.Hours > 1) ? "hours" : "hour");
if (approximate) return sb.ToString() + suffix;
}
if (timeSpan.Minutes > 0)
{
sb.AppendFormat("{0}{1} {2}", (sb.Length > 0) ? ", " : string.Empty,
timeSpan.Minutes, (timeSpan.Minutes > 1) ? "minutes" : "minute");
if (approximate) return sb.ToString() + suffix;
}
if (timeSpan.Seconds > 0)
{
sb.AppendFormat("{0}{1} {2}", (sb.Length > 0) ? ", " : string.Empty,
timeSpan.Seconds, (timeSpan.Seconds > 1) ? "seconds" : "second");
if (approximate) return sb.ToString() + suffix;
}
if (sb.Length == 0) return "right now";
sb.Append(suffix);
return sb.ToString();
}