用C#计算相对时间

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

  • 2小时前
  • 3天前
  • 一个月前
0
额外 编辑
意见: 21
如果你想计算从现在到未来的相对时间呢?
额外 作者 Jhonny D. Cano -Leftware-,
有一个.net软件包 github.com/NickStrupat/TimeAgo ,它几乎可以满足要求。
额外 作者 Rossco,
moment.js是一个非常不错的日期解析库..你可以考虑使用(服务器端或客户端),这取决于你的需要。因为没有人在这里提到它
额外 作者 code ninja,

32 答案

public static string RelativeDate(DateTime theDate)
{
    Dictionary thresholds = new Dictionary();
    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 "";
}

I prefer this version for its conciseness, and ability to add in new tick points. This could be encapsulated with a Latest() extension to Timespan instead of that long 1 liner, but for the sake of brevity in posting, this will do. This fixes the an hour ago, 1 hours ago, by providing an hour until 2 hours have elapsed

0
额外
例如,如果你模拟'theDate = DateTime.Now.AddMinutes(-40);'这个函数,我会遇到各种各样的问题。我'40小时前',但与迈克尔的refactormycode响应,它在'40分钟前'返回正确?
额外 作者 GONeale,
要解决@CodeMonkeyKing提到的问题,您可以使用 SortedDictionary 来代替普通的 Dictionary :用法相同,但它可以确保密钥被排序。但即使如此,该算法也存在缺陷,因为 RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))返回“95个月前”,无论你使用哪种字典类型,这是不正确的(根据你使用的门槛,它应该返回“3个月前”或“4个月前”) - 即使-3在过去的一年中没有创建日期(我已经在12月份测试过了,所以在这种情况下它不应该发生)。
额外 作者 Matt,
OP忘了t.Days> 30? t.Days / 30:
额外 作者 Lars Holm Jensen,
嗯,虽然这段代码可能工作,但假设Dictionary中键的顺序将按特定顺序是错误和无效的。 Dictionary使用Object.GetHashCode(),它不会返回一个long,而是一个int !.如果你想对这些进行排序,那么你应该使用SortedList 。在一组if / else if /.../ else中评估的阈值有什么问题?你得到相同数量的比较。 FYI散列为long.MaxValue结果与int.MinValue相同!
额外 作者 CodeMonkeyKing,
我认为你错过了一个零点,试试:long since =(DateTime.Now.Ticks - theDate.Ticks)/ 10000000;
额外 作者 robnardo,

这是我如何做到的

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 < 120)
{
  return "a minute ago";
}
if (delta < 2700) // 45 * 60
{
  return ts.Minutes + " minutes ago";
}
if (delta < 5400) // 90 * 60
{
  return "an hour ago";
}
if (delta < 86400) // 24 * 60 * 60
{
  return ts.Hours + " hours ago";
}
if (delta < 172800) // 48 * 60 * 60
{
  return "yesterday";
}
if (delta < 2592000) // 30 * 24 * 60 * 60
{
  return ts.Days + " days ago";
}
if (delta < 31104000) // 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";

建议?注释?如何改进这种算法?

0
额外
但是目前SO只显示“Time ago”格式,直到2天。 2天以上,你把标准的日期时间格式。我对吗 ?
额外 作者 Hugo Hilário,
由于所有这些If..else都只是timeslabs,您可以创建一个枚举以提高可读性。说枚举TimeSlabs {秒= 60,分钟= 60 * 60,小时= 60 * 60 * 24,天数= 60 * 60 * 24 * 365,}
额外 作者 Antony Thomas,
如果我现在(星期日晚上)在星期五晚上看到“昨天”的答案,我还认为这是对“昨天”的相当奇怪的定义。
额外 作者 Paŭlo Ebermann,
“<48 * 60 * 60s”对于“昨天”来说是一个非常规的定义。如果周三早上九点,你是否真的认为星期一早上九点一分钟是“昨天”。我以为昨天或“n天前”的算法应该在午夜之前/之后考虑。
额外 作者 Joe,
我只是指定 var delta = ts.Duration(),以便在 TimeSpan 对象中获取绝对值,然后可以执行诸如 或 if(delta 。仍然可读,不需要神秘的数字或常量。
额外 作者 Rufus L,
编译器通常非常擅长预先计算常量表达式,例如24 * 60 * 60,因此您可以直接使用这些表达式而不是自己计算它为86400,并将原始表达式置于注释中
额外 作者 zvolkov,
@bzlm我认为我为我正在开发的一个项目做过。我的动机是提醒其他人,在此代码示例中忽略了几周。至于如何做到这一点,我觉得这很简单。
额外 作者 jray,
注意到这个函数排除了几个星期
额外 作者 jray,
我认为改进算法的好方法是显示2个单位,如“2个月前21天前”,“1小时40分钟前”,以提高准确性。
额外 作者 Evgeny Levin,
@ Jeffy,你错过了闰年和相关检查的计算
额外 作者 Saboor Awan,
45分钟显示为“一小时前”?这不是一个错误吗?如果不是,为什么不在秒和分钟内做同样的事情?
额外 作者 Eldritch Conundrum,

@jeff

恕我直言,你的看起来有点长。不过,它支持“昨天”和“年”似乎更强劲。但根据我的经验,这个人在最初30天内最有可能看到内容。那只是真正的铁杆人。所以我通常选择保持简短。

这是我目前在我的某个网站上使用的方法。这只会返回一个相对的日期,小时,时间。然后用户必须在输出中的“前面”拍打。

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();
}
0
额外

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";
}
0
额外
@Atmo:不,这不可能
额外 作者 André Pena,
我真的不明白为什么人们不使用TotalMinutes,TotalDays,TotalHours ...它会缩短很多...
额外 作者 lord.fist,
有没有人有任何关于如何使用自定义格式字符串来实现的指导,以便'string.Format(mydate,“dd / MM / yyyy(YTT)”)==“26/04/2011(today)”?
额外 作者 Neil Barnwell,
我非常喜欢这样的常量。这对任何人都是错的吗? Thread.Sleep(1 * MINUTE)?因为它错了1000倍。
额外 作者 Roman Starkov,
另一个bug:因为delta是绝对的,所以“not yet”的路径永远不会到达。更改为:delta = ts.TotalSeconds;
额外 作者 StefanG,
我认为如果常量被重新命名为准确地描述它们中的值,那么它会更容易理解。所以SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour;等等。只要将它称为MINUTE = 60,不允许读者确定该值是什么。
额外 作者 slolife,
这个小故障出现在“昨天”,它不是很准确,因为我今天上午7点看到的事情发生在昨天晚上10点,会产生“昨天”,这是不准确的。
额外 作者 Ayyash,
在某些语言中,你甚至可以为switch()指定任何类型的比较运算符 - 语句,比如>,> =,<=等等...在C#中这不是可能吗?
额外 作者 Atmocreations,
为什么不 var ts = DateTime.UtcNow - yourDate; ,它更简单? Math.Abs​​ 的理由是什么?
额外 作者 Jeppe Stig Nielsen,
不应该是(delta <= 90 * MINUTE)?不知何故,我得到了“1小时前”。
额外 作者 Roberto,
为什么没有人(除了乔)关心错误的'昨天'或'天前'值?昨天不是一小时计算,而是一天一天的计算。所以是的,至少在两个常见的情况下,这是一个错误的代码。
额外 作者 CtrlX,
const int SECOND = 1; 奇怪的是秒是一秒。
额外 作者 seriousdev,
这种类型的代码几乎不可能进行本地化。如果你的应用只需要保持英文,那么很好。但如果你跳到其他语言,你会恨自己做这样的逻辑。所以你们都知道...
额外 作者 Nik Reiman,
@romkyns Thread.Sleep(1 * MINUTE) 对我来说看起来不对,这是因为 MINUTE 没有指定单位。现在, MINUTE_IN_SECONDS ,这将很容易被发现和轻松修复: Thread.Sleep(1 * MINUTE_IN_SECONDS * 1000) Thread.Sleep(1 * MINUTE_IN_SECONDS * SECOND_IN_MILLISECONDS)</代码>。 (获得详细的信息,但比隐藏的bug更好。)
额外 作者 jpmc26,
当我看到所有的资本时,我回想起为C / C ++开发的编码风格。微软的建议是Pascal。检查这个问题的答案 - stackoverflow.com /问题/ 242534 /&hellip;
额外 作者 CodeMonkeyKing,
@nick如果你把它放在IValueConverter中,它会自动为你处理CultureInfo(假设你已经设置了文化)
额外 作者 Lance McCarthy,
您可以使用DateTime类型上定义的常量来优化解决方案,但您不必手动定义它们。
额外 作者 SOAL ABDELDJALLIL,

我以为我会用类和多态来给这个镜头一个镜头。我有一个以前的迭代使用子分类,最终导致了太多的开销。我已经切换到一个更加灵活的委托/公共属性对象模型,它显着更好。我的代码稍微更准确一点,我希望我能想出一个更好的方法来生成“几个月前”,但似乎没有过度设计。

我认为我仍然坚持使用Jeff的if-then cascade,因为它的代码更少,而且更简单(确保按预期工作会更容易)。

对于下面的代码 PrintRelativeTime.GetRelativeTimeMessage(TimeSpan前)返回相对时间消息(例如“昨天”)。

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 timeRanges;

    static PrintRelativeTime()
    {
        timeRanges = new List{
            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);
    }
}
0
额外

我会建议在客户端计算这个。为服务器减少工作。

以下是我使用的版本(来自Zach Leatherman)

/*
 * Javascript Humane Dates
 * Copyright (c) 2008 Dean Landolt (deanlandolt.com)
 * Re-write by Zach Leatherman (zachleat.com)
 * 
 * Adopted from the John Resig's pretty.js
 * at http://ejohn.org/blog/javascript-pretty-date
 * and henrah's proposed modification 
 * at http://ejohn.org/blog/javascript-pretty-date/#comment-297458
 * 
 * Licensed under the MIT license.
 */

function humane_date(date_str){
        var time_formats = [
                [60, 'just now'],
                [90, '1 minute'], // 60*1.5
                [3600, 'minutes', 60], // 60*60, 60
                [5400, '1 hour'], // 60*60*1.5
                [86400, 'hours', 3600], // 60*60*24, 60*60
                [129600, '1 day'], // 60*60*24*1.5
                [604800, 'days', 86400], // 60*60*24*7, 60*60*24
                [907200, '1 week'], // 60*60*24*7*1.5
                [2628000, 'weeks', 604800], // 60*60*24*(365/12), 60*60*24*7
                [3942000, '1 month'], // 60*60*24*(365/12)*1.5
                [31536000, 'months', 2628000], // 60*60*24*365, 60*60*24*(365/12)
                [47304000, '1 year'], // 60*60*24*365*1.5
                [3153600000, 'years', 31536000], // 60*60*24*365*100, 60*60*24*365
                [4730400000, '1 century'] // 60*60*24*365*100*1.5
        ];

        var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," "),
                dt = new Date,
                seconds = ((dt - new Date(time) + (dt.getTimezoneOffset() * 60000)) / 1000),
                token = ' ago',
                i = 0,
                format;

        if (seconds < 0) {
                seconds = Math.abs(seconds);
                token = '';
        }

        while (format = time_formats[i++]) {
                if (seconds < format[0]) {
                        if (format.length == 2) {
                                return format[1] + (i > 1 ? token : ''); // Conditional so we don't return Just Now Ago
                        } else {
                                return Math.round(seconds / format[2]) + ' ' + format[1] + (i > 1 ? token : '');
                        }
                }
        }

        // overflow for centuries
        if(seconds > 4730400000)
                return Math.round(seconds / 4730400000) + ' centuries' + token;

        return date_str;
};

if(typeof jQuery != 'undefined') {
        jQuery.fn.humane_dates = function(){
                return this.each(function(){
                        var date = humane_date(this.title);
                        if(date && jQuery(this).text() != date) // don't modify the dom if we don't have to
                                jQuery(this).text(date);
                });
        };
}
0
额外
问题是为什么 Javascript代码 C#标记了
额外 作者 Kiquenet,

在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";

} ?>
0
额外
问题是 C#标记 。为什么这个 PHP代码 ?恕我直言,只适用于C#代码
额外 作者 Kiquenet,

@Jeff

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

无论如何,在 DateTime 上执行减法操作都会返回 TimeSpan

所以你可以做

(DateTime.UtcNow - dt).TotalSeconds

我也很惊讶地发现常数是手工增加的,然后是用乘法增加的注释。是否有一些误导性的优化?

0
额外

这是我从比尔盖茨的一篇博客中得到的。我需要在浏览器历史记录中找到它,然后我会给你链接。

Javascript代码做同样的事情(按要求):

function posted(t) {
    var now = new Date();
    var diff = parseInt((now.getTime() - Date.parse(t)) / 1000);
    if (diff < 60) { return 'less than a minute ago'; }
    else if (diff < 120) { return 'about a minute ago'; }
    else if (diff < (2700)) { return (parseInt(diff / 60)).toString() + ' minutes ago'; }
    else if (diff < (5400)) { return 'about an hour ago'; }
    else if (diff < (86400)) { return 'about ' + (parseInt(diff / 3600)).toString() + ' hours ago'; }
    else if (diff < (172800)) { return '1 day ago'; } 
    else {return (parseInt(diff / 86400)).toString() + ' days ago'; }
}

基本上,你以秒为单位工作......

0
额外

您可以使用 TimeAgo扩展,其格式如下所示:

public static string TimeAgo(this DateTime dateTime)
{
    string result = string.Empty;
    var timeSpan = DateTime.Now.Subtract(dateTime);

    if (timeSpan <= TimeSpan.FromSeconds(60))
    {
        result = string.Format("{0} seconds ago", timeSpan.Seconds);
    }
    else if (timeSpan <= TimeSpan.FromMinutes(60))
    {
        result = timeSpan.Minutes > 1 ? 
            String.Format("about {0} minutes ago", timeSpan.Minutes) :
            "about a minute ago";
    }
    else if (timeSpan <= TimeSpan.FromHours(24))
    {
        result = timeSpan.Hours > 1 ? 
            String.Format("about {0} hours ago", timeSpan.Hours) : 
            "about an hour ago";
    }
    else if (timeSpan <= TimeSpan.FromDays(30))
    {
        result = timeSpan.Days > 1 ? 
            String.Format("about {0} days ago", timeSpan.Days) : 
            "yesterday";
    }
    else if (timeSpan <= TimeSpan.FromDays(365))
    {
        result = timeSpan.Days > 30 ? 
            String.Format("about {0} months ago", timeSpan.Days / 30) : 
            "about a month ago";
    }
    else
    {
        result = timeSpan.Days > 365 ? 
            String.Format("about {0} years ago", timeSpan.Days / 365) : 
            "about a year ago";
    }

    return result;
}

或者使用来自Timeago的Razor扩展程序的 jQuery插件

0
额外

我会为此提供一些方便的扩展方法,并使代码更具可读性。首先,用于 Int32 的几个扩展方法。

public static class TimeSpanExtensions {

    public static TimeSpan Days(this int value) {

        return new TimeSpan(value, 0, 0, 0);
    }

    public static TimeSpan Hours(this int value) {

        return new TimeSpan(0, value, 0, 0);
    }

    public static TimeSpan Minutes(this int value) {

        return new TimeSpan(0, 0, value, 0);
    }

    public static TimeSpan Seconds(this int value) {

        return new TimeSpan(0, 0, 0, value);
    }

    public static TimeSpan Milliseconds(this int value) {

        return new TimeSpan(0, 0, 0, 0, value);
    }

    public static DateTime Ago(this TimeSpan value) {

        return DateTime.Now - value;
    }
}

然后,一个用于 DateTime

public static class DateTimeExtensions {

    public static DateTime Ago(this DateTime dateTime, TimeSpan delta) {

        return dateTime - delta;
    }
}

现在,你可以做如下的事情:

var date = DateTime.Now;
date.Ago(2.Days()); // 2 days ago
date.Ago(7.Hours()); // 7 hours ago
date.Ago(567.Milliseconds()); // 567 milliseconds ago
0
额外

这里从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";
    }
}    
0
额外
问题在于 为什么 PHP代码 标记了 ?
额外 作者 Kiquenet,

using Fluent DateTime https://github.com/FluentDateTime

var dateTime1 = 2.Hours().Ago();
var dateTime2 = 3.Days().Ago();
var dateTime3 = 1.Months().Ago();
var dateTime4 = 5.Hours().FromNow();
var dateTime5 = 2.Weeks().FromNow();
var dateTime6 = 40.Seconds().FromNow();
0
额外

这里是算法stackoverflow使用,但重写更完整的伪代码与错误修复(没有“一个小时前”)。该函数需要(正数)秒以前的时间,并返回一个人性化的字符串,如“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";
0
额外
using System;
using System.Collections.Generic;
using System.Linq;

public static class RelativeDateHelper
{
    private static Dictionary> sm_Dict = null;

    private static Dictionary> DictionarySetup()
    {
        var dict = new Dictionary>();
        dict.Add(0.75, (mins) => "less than a minute");
        dict.Add(1.5, (mins) => "about a minute");
        dict.Add(45, (mins) => string.Format("{0} minutes", Math.Round(mins)));
        dict.Add(90, (mins) => "about an hour");
        dict.Add(1440, (mins) => string.Format("about {0} hours", Math.Round(Math.Abs(mins / 60)))); // 60 * 24
        dict.Add(2880, (mins) => "a day"); // 60 * 48
        dict.Add(43200, (mins) => string.Format("{0} days", Math.Floor(Math.Abs(mins / 1440)))); // 60 * 24 * 30
        dict.Add(86400, (mins) => "about a month"); // 60 * 24 * 60
        dict.Add(525600, (mins) => string.Format("{0} months", Math.Floor(Math.Abs(mins / 43200)))); // 60 * 24 * 365 
        dict.Add(1051200, (mins) => "about a year"); // 60 * 24 * 365 * 2
        dict.Add(double.MaxValue, (mins) => string.Format("{0} years", Math.Floor(Math.Abs(mins / 525600))));

        return dict;
    }

    public static string ToRelativeDate(this DateTime input)
    {
        TimeSpan oSpan = DateTime.Now.Subtract(input);
        double TotalMinutes = oSpan.TotalMinutes;
        string Suffix = " ago";

        if (TotalMinutes < 0.0)
        {
            TotalMinutes = Math.Abs(TotalMinutes);
            Suffix = " from now";
        }

        if (null == sm_Dict)
            sm_Dict = DictionarySetup();

        return sm_Dict.First(n => TotalMinutes < n.Key).Value.Invoke(TotalMinutes) + Suffix;
    }
}

对此问题的另一个答案相同,但作为扩展名方法与静态字典。

0
额外
字典在这里买你什么?
额外 作者 StriplingWarrior,
我懂了。我只是在想,因为关于 Dictionary 的文档声明“返回项目的顺序是未定义的”( msdn.microsoft.com/en-us/library/xfhwa508.aspx )或许当你不在乎时,这不是最好的数据结构关于查找时间和事物保持有序一样。
额外 作者 StriplingWarrior,
StriplingWarrior:与switch语句或if / else语句堆栈相比,易于阅读和修改。字典是静态的,意味着它和Func <�,>对象不需要每次我们要使用ToRelativeDate时创建;它只创建一次,与我在答案中链接的那个相比。
额外 作者 Chris Charabaruk,
StriplingWarrior:我认为LINQ在与 Dictionary 一起使用时会考虑到这一点。如果您仍然不舒服,可以使用 SortedDictionary 代码> ,但我自己的经验表明,这是不必要的。
额外 作者 Chris Charabaruk,
public string getRelativeDateTime(DateTime date)
{
    TimeSpan ts = DateTime.Now - date;
    if (ts.TotalMinutes < 1)//seconds ago
        return "just now";
    if (ts.TotalHours < 1)//min ago
        return (int)ts.TotalMinutes == 1 ? "1 Minute ago" : (int)ts.TotalMinutes + " Minutes ago";
    if (ts.TotalDays < 1)//hours ago
        return (int)ts.TotalHours == 1 ? "1 Hour ago" : (int)ts.TotalHours + " Hours ago";
    if (ts.TotalDays < 7)//days ago
        return (int)ts.TotalDays == 1 ? "1 Day ago" : (int)ts.TotalDays + " Days ago";
    if (ts.TotalDays < 30.4368)//weeks ago
        return (int)(ts.TotalDays / 7) == 1 ? "1 Week ago" : (int)(ts.TotalDays / 7) + " Weeks ago";
    if (ts.TotalDays < 365.242)//months ago
        return (int)(ts.TotalDays / 30.4368) == 1 ? "1 Month ago" : (int)(ts.TotalDays / 30.4368) + " Months ago";
    //years ago
    return (int)(ts.TotalDays / 365.242) == 1 ? "1 Year ago" : (int)(ts.TotalDays / 365.242) + " Years ago";
}

一个月和一年中的天数转换值来自Google。

0
额外

这个晚会晚了两年,但我有要求在过去和未来的日期都这样做,所以我结合了 Jeff入此。这是一个三元的盛会! :)

public static class DateTimeHelper
    {
        private const int SECOND = 1;
        private const int MINUTE = 60 * SECOND;
        private const int HOUR = 60 * MINUTE;
        private const int DAY = 24 * HOUR;
        private const int MONTH = 30 * DAY;

        /// 
/// Returns a friendly version of the provided DateTime, relative to now. E.g.: "2 days ago", or "in 6 months". ///
 
        /// 
The DateTime to compare to Now
        /// A friendly string
        public static string GetFriendlyRelativeTime(DateTime dateTime)
        {
            if (DateTime.UtcNow.Ticks == dateTime.Ticks)
            {
                return "Right now!";
            }

            bool isFuture = (DateTime.UtcNow.Ticks < dateTime.Ticks);
            var ts = DateTime.UtcNow.Ticks < dateTime.Ticks ? new TimeSpan(dateTime.Ticks - DateTime.UtcNow.Ticks) : new TimeSpan(DateTime.UtcNow.Ticks - dateTime.Ticks);

            double delta = ts.TotalSeconds;

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

我认为已经有很多与这篇文章相关的答案,但是可以使用这个插件就像插件一样易于使用,而且程序员也很容易阅读。 发送您的具体日期,并以字符串形式获取其值:

public string RelativeDateTimeCount(DateTime inputDateTime)
{
    string outputDateTime = string.Empty;
    TimeSpan ts = DateTime.Now - inputDateTime;

    if (ts.Days > 7)
    { outputDateTime = inputDateTime.ToString("MMMM d, yyyy"); }

    else if (ts.Days > 0)
    {
        outputDateTime = ts.Days == 1 ? ("about 1 Day ago") : ("about " + ts.Days.ToString() + " Days ago");
    }
    else if (ts.Hours > 0)
    {
        outputDateTime = ts.Hours == 1 ? ("an hour ago") : (ts.Hours.ToString() + " hours ago");
    }
    else if (ts.Minutes > 0)
    {
        outputDateTime = ts.Minutes == 1 ? ("1 minute ago") : (ts.Minutes.ToString() + " minutes ago");
    }
    else outputDateTime = "few seconds ago";

    return outputDateTime;
}
0
额外

我的方式要简单得多。你可以随意调整返回字符串

    public static string TimeLeft(DateTime utcDate)
    {
        TimeSpan timeLeft = DateTime.UtcNow - utcDate;
        string timeLeftString = "";
        if (timeLeft.Days > 0)
        {
            timeLeftString += timeLeft.Days == 1 ? timeLeft.Days + " day" : timeLeft.Days + " days";
        }
        else if (timeLeft.Hours > 0)
        {
            timeLeftString += timeLeft.Hours == 1 ? timeLeft.Hours + " hour" : timeLeft.Hours + " hours";
        }
        else
        {
            timeLeftString += timeLeft.Minutes == 1 ? timeLeft.Minutes+" minute" : timeLeft.Minutes + " minutes";
        }
        return timeLeftString;
    }
0
额外

当然,摆脱“1小时前”问题的一个简单方法就是增加“一小时前”有效的窗口。 更改

if (delta < 5400) // 90 * 60
{
    return "an hour ago";
}

if (delta < 7200) // 120 * 60
{
    return "an hour ago";
}

这意味着,在110分钟前发生的事情会在“一小时前”被读取 - 这可能并不完美,但我认为它比'1小时前'的现状要好。

0
额外

考虑到这个世界和她的丈夫似乎正在发布代码样本,这是我前一段时间写的,基于这些答案。

我特别需要此代码是可本地化的。所以我有两堂课? Grammar ,它指定了可局部化的术语, FuzzyDateExtensions ,它包含一堆扩展方法。我不需要处理未来的日期时间,所以没有尝试用这个代码来处理它们。

我已经在源代码中留下了一些XMLdoc,但为了简洁起见,删除了大部分(它们显而易见的地方)。我也没有在这里包括每个班级成员:

public class Grammar
{
    /// 
Gets or sets the term for "just now".
 
    public string JustNow { get; set; }
    /// 
Gets or sets the term for "X minutes ago".
 
    /// 
    ///     This is a  pattern, where {0}
    ///     is the number of minutes.
    /// 
    public string MinutesAgo { get; set; }
    public string OneHourAgo { get; set; }
    public string HoursAgo { get; set; }
    public string Yesterday { get; set; }
    public string DaysAgo { get; set; }
    public string LastMonth { get; set; }
    public string MonthsAgo { get; set; }
    public string LastYear { get; set; }
    public string YearsAgo { get; set; }
    /// 
Gets or sets the term for "ages ago".
 
    public string AgesAgo { get; set; }

    /// 
/// Gets or sets the threshold beyond which the fuzzy date should be /// considered "ages ago". ///
 
    public TimeSpan AgesAgoThreshold { get; set; }

    /// 
/// Initialises a new instance with the /// specified properties. ///
 
    private void Initialise(string justNow, string minutesAgo,
        string oneHourAgo, string hoursAgo, string yesterday, string daysAgo,
        string lastMonth, string monthsAgo, string lastYear, string yearsAgo,
        string agesAgo, TimeSpan agesAgoThreshold)
    { ... }
}

FuzzyDateString 类包含:

public static class FuzzyDateExtensions
{
    public static string ToFuzzyDateString(this TimeSpan timespan)
    {
        return timespan.ToFuzzyDateString(new Grammar());
    }

    public static string ToFuzzyDateString(this TimeSpan timespan,
        Grammar grammar)
    {
        return GetFuzzyDateString(timespan, grammar);
    }

    public static string ToFuzzyDateString(this DateTime datetime)
    {
        return (DateTime.Now - datetime).ToFuzzyDateString();
    }

    public static string ToFuzzyDateString(this DateTime datetime,
       Grammar grammar)
    {
        return (DateTime.Now - datetime).ToFuzzyDateString(grammar);
    }


    private static string GetFuzzyDateString(TimeSpan timespan,
       Grammar grammar)
    {
        timespan = timespan.Duration();

        if (timespan >= grammar.AgesAgoThreshold)
        {
            return grammar.AgesAgo;
        }

        if (timespan < new TimeSpan(0, 2, 0))    // 2 minutes
        {
            return grammar.JustNow;
        }

        if (timespan < new TimeSpan(1, 0, 0))    // 1 hour
        {
            return String.Format(grammar.MinutesAgo, timespan.Minutes);
        }

        if (timespan < new TimeSpan(1, 55, 0))    // 1 hour 55 minutes
        {
            return grammar.OneHourAgo;
        }

        if (timespan < new TimeSpan(12, 0, 0)    // 12 hours
            && (DateTime.Now - timespan).IsToday())
        {
            return String.Format(grammar.HoursAgo, timespan.RoundedHours());
        }

        if ((DateTime.Now.AddDays(1) - timespan).IsToday())
        {
            return grammar.Yesterday;
        }

        if (timespan < new TimeSpan(32, 0, 0, 0)    // 32 days
            && (DateTime.Now - timespan).IsThisMonth())
        {
            return String.Format(grammar.DaysAgo, timespan.RoundedDays());
        }

        if ((DateTime.Now.AddMonths(1) - timespan).IsThisMonth())
        {
            return grammar.LastMonth;
        }

        if (timespan < new TimeSpan(365, 0, 0, 0, 0)    // 365 days
            && (DateTime.Now - timespan).IsThisYear())
        {
            return String.Format(grammar.MonthsAgo, timespan.RoundedMonths());
        }

        if ((DateTime.Now - timespan).AddYears(1).IsThisYear())
        {
            return grammar.LastYear;
        }

        return String.Format(grammar.YearsAgo, timespan.RoundedYears());
    }
}

我想实现的以及本地化的关键之一是“今天”只意味着“这个日历日”,所以 IsTodayIsThisMonth ,<�代码> IsThisYear 方法如下所示:

public static bool IsToday(this DateTime date)
{
    return date.DayOfYear == DateTime.Now.DayOfYear && date.IsThisYear();
}

和舍入方法是这样的(我已经包含 RoundedMonths ,因为这有点不同):

public static int RoundedDays(this TimeSpan timespan)
{
    return (timespan.Hours > 12) ? timespan.Days + 1 : timespan.Days;
}

public static int RoundedMonths(this TimeSpan timespan)
{
    DateTime then = DateTime.Now - timespan;

    // Number of partial months elapsed since 1 Jan, AD 1 (DateTime.MinValue)
    int nowMonthYears = DateTime.Now.Year * 12 + DateTime.Now.Month;
    int thenMonthYears = then.Year * 12 + then.Month;                    

    return nowMonthYears - thenMonthYears;
}

我希望人们发现这个有用的和/或有趣的:o)

0
额外

用于客户端gwt使用的Java:

import java.util.Date;

public class RelativeDateFormat {

 private static final long ONE_MINUTE = 60000L;
 private static final long ONE_HOUR = 3600000L;
 private static final long ONE_DAY = 86400000L;
 private static final long ONE_WEEK = 604800000L;

 public static String format(Date date) {

  long delta = new Date().getTime() - date.getTime();
  if (delta < 1L * ONE_MINUTE) {
   return toSeconds(delta) == 1 ? "one second ago" : toSeconds(delta)
     + " seconds ago";
  }
  if (delta < 2L * ONE_MINUTE) {
   return "one minute ago";
  }
  if (delta < 45L * ONE_MINUTE) {
   return toMinutes(delta) + " minutes ago";
  }
  if (delta < 90L * ONE_MINUTE) {
   return "one hour ago";
  }
  if (delta < 24L * ONE_HOUR) {
   return toHours(delta) + " hours ago";
  }
  if (delta < 48L * ONE_HOUR) {
   return "yesterday";
  }
  if (delta < 30L * ONE_DAY) {
   return toDays(delta) + " days ago";
  }
  if (delta < 12L * 4L * ONE_WEEK) {
   long months = toMonths(delta);
   return months <= 1 ? "one month ago" : months + " months ago";
  } else {
   long years = toYears(delta);
   return years <= 1 ? "one year ago" : years + " years ago";
  }
 }

 private static long toSeconds(long date) {
  return date / 1000L;
 }

 private static long toMinutes(long date) {
  return toSeconds(date) / 60L;
 }

 private static long toHours(long date) {
  return toMinutes(date) / 60L;
 }

 private static long toDays(long date) {
  return toHours(date) / 24L;
 }

 private static long toMonths(long date) {
  return toDays(date) / 30L;
 }

 private static long toYears(long date) {
  return toMonths(date) / 365L;
 }

}
0
额外
问题是 C#标记 。为什么这个 Java代码 ?恕我直言,只适用于C#代码
额外 作者 Kiquenet,

在Nuget上也有一个名为Humanizer的包,它实际上工作得很好

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 has a writeup on it on his blog

0
额外
非常有用!这个答案在这个列表中必须高得多。如果我有100票,我会把它给这个。显然(来自JS-land),寻找这个软件包并不容易。
额外 作者 kumar_harsh,
友好的说明:在.net 4.5或更高版本上不安装完整的Humanizer ...只安装它的Humanizer.Core部分..导致此版本不支持其他语言包
额外 作者 Ahmad,

如果您知道观众的时区,则可以更清楚地使用日程表中的日历天数。我不熟悉.NET库,所以我不知道如何在C#中执行此操作,遗憾的是。

在消费者网站上,您可能会在一分钟之内手动变动。 “不到一分钟前”或“刚才”可能就足够了。

0
额外

jquery.timeago插件

Jeff, because Stack Overflow uses jQuery extensively, I recommend the jquery.timeago插件.

优点:

  • 避免时间戳记为“1分钟前”,即使页面在10分钟前打开; timeago会自动刷新。
  • 由于时间戳不在服务器上计算,因此您可以充分利用网页应用程序中的页面和/或片段缓存。
  • 您可以像使用酷炫的孩子一样使用微格式。

只需将它附加到DOM准备好的时间戳上即可:

jQuery(document).ready(function() {
    jQuery('abbr.timeago').timeago();
});

这将使所有 abbr 元素具有一个timeago类和一个 ISO 8601 标题中的时间戳:

July 17, 2008

变成这样的东西:

4 months ago

这产生了:4个月前。随着时间的推移,时间戳会自动更新。

免责声明:我写了这个插件,所以我很偏向。

0
额外
@罗伯丰塞卡 - 恩索 - 现在它也让我哭了。如何每分钟更新一次,以显示准确的信息,以任何方式与每秒闪烁一次的文字相关?
额外 作者 Daniel Earwicker,
一个伟大的插件 - 只是为了好玩,我通过减少功能(比如autoupdating)来“减少它”,并将它与 refactormycode answer </ html> a> - jsfiddle.net/drzaus/eMUzF
额外 作者 drzaus,
瑞恩,我建议,前段时间使用timeago。杰夫的回应让我哭了,我建议你坐下来: stackoverflow.uservoice.com/pages/1722-general/suggestions/…
额外 作者 Rob Fonseca-Ensor,
@RyanMcGeary:这可能是错误的地方,但我有关于使用TimeAgo的疑问。我在英国(格林威治标准时间,包括DST)和我的数据库中存储的日期是UTC。如果我在夏令时将数据插入我的数据库中,那么显然时间将会缩短1小时。因此,而不是TimeAgo打印“大约1分钟前”,它会说“大约1小时前”。 TimeAgo插件是否处理DST差异?
额外 作者 TheCarver,
问题是关于C#,我看不出jQuery插件是如何相关的。
额外 作者 BartoszKP,
如果用户禁用了JavaScript,该怎么办? jQuery(和JavaScritp一般来说)应该仅用于改善客户端体验,但是您的应用程序功能不应该依赖于客户端技术。无需使用JavaScript即可完成此操作。
额外 作者 Seb,
禁用自动更新的选项不应该那么难,对吗?如果它尚不存在,那就是......只需在页面加载上更新一次时间戳并完成它。
额外 作者 Dean Harding,
做这种支持。 c#datetime.utcNow ??这将是很好的
额外 作者 blackholeearth0_gmail,
@PaparazzoKid是的,只要您的时间戳是一个合适的ISO8601时间戳,Timeago就可以处理时区。但你是对的。这是问这个问题的错误地方。请转到此处: github.com/rmm5t/jquery-timeago/issues
额外 作者 Ryan McGeary,
Seb,如果您禁用Javascript,则显示您最初放在abbr标签之间的字符串。通常,这只是您希望的任何格式的日期或时间。 Timeago优雅地降级。它并没有变得更简单。
额外 作者 Ryan McGeary,
嘿,谢谢Rob。没关系。它几乎没有引人注目,特别是在转换期间只有一个数字发生变化时,尽管SO页面有很多时间戳。尽管他选择避免自动更新,但我会认为他至少会赞赏页面缓存的好处。我相信杰夫可以提供反馈来改进插件。我知道像 arstechnica.com 这样的网站使用它会让人安慰。
额外 作者 Ryan McGeary,

这是一个作为DateTime类的扩展方法添加的实现,它处理将来和过去的日期,并提供一个近似选项,允许您指定要查找的详细程度(“3小时前”vs“3小时, 23分钟,12秒前“):

using System.Text;

/// 
/// Compares a supplied date to the current date and generates a friendly English /// comparison ("5 days ago", "5 days from now") ///
 
/// 
The date to convert
/// 
When off, calculate timespan down to the second.
/// When on, approximate to the largest round unit of time.
/// 
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();
}
0
额外

iPhone obj-c版本

+ (NSString *)timeAgoString:(NSDate *)date {
int delta = -(int)[date timeIntervalSinceNow];

if (delta < 60)
{
    return delta == 1 ? @"one second ago" : [NSString stringWithFormat:@"%i seconds ago", delta];
}
if (delta < 120)
{
    return @"a minute ago";
}
if (delta < 2700)
{
    return [NSString stringWithFormat:@"%i minutes ago", delta/60];
}
if (delta < 5400)
{
    return @"an hour ago";
}
if (delta < 24 * 3600)
{
    return [NSString stringWithFormat:@"%i hours ago", delta/3600];
}
if (delta < 48 * 3600)
{
    return @"yesterday";
}
if (delta < 30 * 24 * 3600)
{
    return [NSString stringWithFormat:@"%i days ago", delta/(24*3600)];
}
if (delta < 12 * 30 * 24 * 3600)
{
    int months = delta/(30*24*3600);
    return months <= 1 ? @"one month ago" : [NSString stringWithFormat:@"%i months ago", months];
}
else
{
    int years = delta/(12*30*24*3600);
    return years <= 1 ? @"one year ago" : [NSString stringWithFormat:@"%i years ago", years];
}

}

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

你可以试试这个。我认为它会正常工作。

long delta = new Date().getTime() - date.getTime();
const int SECOND = 1;
const int MINUTE = 60 * SECOND;
const int HOUR = 60 * MINUTE;
const int DAY = 24 * HOUR;
const int MONTH = 30 * DAY;

if (delta < 0L)
{
  return "not yet";
}
if (delta < 1L * MINUTE)
{
  return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago";
}
if (delta < 2L * MINUTE)
{
  return "a minute ago";
}
if (delta < 45L * MINUTE)
{
  return ts.Minutes + " minutes ago";
}
if (delta < 90L * MINUTE)
{
  return "an hour ago";
}
if (delta < 24L * HOUR)
{
  return ts.Hours + " hours ago";
}
if (delta < 48L * HOUR)
{
  return "yesterday";
}
if (delta < 30L * DAY)
{
  return ts.Days + " days ago";
}
if (delta < 12L * 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";
}
0
额外
/** 
 * {@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";
    }
}
0
额外

如果您想要输出“2天,4小时和12分钟前”,则需要一个时间段:

TimeSpan timeDiff = DateTime.Now-CreatedDate;

然后你可以访问你喜欢的值:

timeDiff.Days
timeDiff.Hours

等等

0
额外

这是我的功能,就像一个魅力:)

public static string RelativeDate(DateTime theDate)
        {
            var span = DateTime.Now - theDate;
            if (span.Days > 365)
            {
                var years = (span.Days / 365);
                if (span.Days % 365 != 0)
                    years += 1;
                return $"about {years} {(years == 1 ? "year" : "years")} ago";
            }
            if (span.Days > 30)
            {
                var months = (span.Days / 30);
                if (span.Days % 31 != 0)
                    months += 1;
                return $"about {months} {(months == 1 ? "month" : "months")} ago";
            }
            if (span.Days > 0)
                return $"about {span.Days} {(span.Days == 1 ? "day" : "days")} ago";
            if (span.Hours > 0)
                return $"about {span.Hours} {(span.Hours == 1 ? "hour" : "hours")} ago";
            if (span.Minutes > 0)
                return $"about {span.Minutes} {(span.Minutes == 1 ? "minute" : "minutes")} ago";
            if (span.Seconds > 5)
                return $"about {span.Seconds} seconds ago";

            return span.Seconds <= 5 ? "about 5 seconds ago" : string.Empty;
        }
0
额外
快乐编码:D
额外 作者 VnDevil,
奇迹般有效 :)
额外 作者 densityx,