用 C 语言计算相对时间#

Calculate relative time in C#

提问人: 提问时间:8/1/2008 最后编辑:24 revs, 21 users 22%Jeff Atwood 更新时间:9/5/2022 访问量:201328

问:

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

  • 2 hours ago
  • 3 days ago
  • a month ago
C# 时间 日期差异 相对时间跨度

评论

87赞 Jhonny D. Cano -Leftware- 3/27/2009
如果要计算从现在到未来的相对时间怎么办?
3赞 Matej 6/30/2014
moment.js 是一个非常好的日期解析库。.您可以考虑使用它(服务器端或客户端),具体取决于您的需要。仅供参考,因为这里没有人提到它
5赞 Aaron Hudon 1/16/2020
这个项目对于日期格式设置 github.com/Humanizr/Humanizer#humanize-datetime 非常流畅

答:

348赞 13 revs, 7 users 50%Jeff Atwood #1

我是这样做的

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

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

评论

114赞 Joe 2/2/2009
“< 48*60*60s”是“昨天”的一个相当非常规的定义。如果是周三上午9点,你真的会把周一上午9点01分当成“昨天”吗?我本来以为昨天或“n 天前”的算法应该在午夜之前/之后考虑。
141赞 Andriy Volkov 6/11/2009
编译器通常非常擅长预先计算常量表达式,比如 24 * 60 * 60,所以你可以直接使用它们,而不是自己计算为 86400 并将原始表达式放在注释中
11赞 jray 11/10/2010
@bzlm我想我是为我正在做的一个项目做的。我在这里的动机是提醒其他人,此代码示例中省略了周。至于如何做到这一点,对我来说似乎很简单。
9赞 David Levin 2/1/2012
我认为改进算法的好方法是显示 2 个单位,例如“2 个月 21 天前”、“1 小时 40 分钟前”以提高准确性。
6赞 Saboor Awan 6/20/2013
@Jeffy,您错过了闰年和相关检查的计算
34赞 3 revs, 2 users 95%Nick Berardi #2

@jeff

恕我直言,你的似乎有点长。但是,它似乎确实更强大,支持“昨天”和“年”。但根据我的经验,当使用它时,这个人最有可能在前 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();
}
1071赞 9 revs, 6 users 78%Vincent Robert #3

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

评论

231赞 Roman Starkov 8/18/2010
我满怀激情地讨厌这样的常数。这对任何人来说都是错误的吗??因为它错了 1000 倍。Thread.Sleep(1 * MINUTE)
39赞 seriousdev 6/18/2011
const int SECOND = 1;太奇怪了,一秒钟就是一秒钟。
67赞 Nik Reiman 5/23/2012
这种类型的代码几乎不可能本地化。如果您的应用程序只需要保持英文,那就好了。但是,如果你跳到其他语言,你会讨厌自己做这样的逻辑。只是你们都知道......
78赞 slolife 8/30/2012
我认为,如果重命名常量以准确描述其中的值,则会更容易理解。所以 SecondsPerMinute = 60;分钟数/小时 = 60;SecondsPerHour = 分钟数每小时 * 秒数/小时;等。仅将其称为 MINUTE=60 并不能让读者确定该值是什么。
18赞 CtrlX 9/26/2013
为什么没有人(除了乔)关心错误的“昨天”或“几天前”值???昨天不是一个小时的计算,而是每天的计算。所以是的,至少在两种常见情况下,这是一个错误的代码。
16赞 5 revsWedge #4

我想我会使用类和多态性来试一试。我之前有一个使用子类的迭代,最终产生了太多的开销。我已经切换到更灵活的委托/公共属性对象模型,这要好得多。我的代码稍微准确一些,我希望我能想出一种更好的方法来生成“几个月前”,这似乎不会太过度设计。

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

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

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);
    }
}
100赞 5 revs, 5 users 65%DevelopingChris #5
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 的扩展而不是那么长的 1 行来封装,但为了发布简洁起见,这将可以。 这修复了 1 小时前、1 小时前的问题,提供 1 小时直到 2 小时过去Latest()

评论

0赞 GONeale 12/15/2008
我在使用这个函数时遇到了各种各样的问题,例如,如果你嘲笑'theDate = DateTime.Now.AddMinutes(-40);'我得到的是“40 小时前”,但使用 Michael 的 refactormycode 响应,它在“40 分钟前”返回正确?
0赞 robnardo 8/8/2009
我认为你缺少一个零,尝试:long since = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
9赞 CodeMonkeyKing 11/9/2011
嗯,虽然此代码可能有效,但假设字典中键的顺序将按特定顺序排列是不正确和无效的。字典使用 Object.GetHashCode(),它不返回 long,而是返回 int!。如果要对它们进行排序,则应使用 SortedList<long, string>。在一组 if/else if/.../else 中评估的阈值有什么问题?你得到相同数量的比较。仅供参考,哈希值很长。MaxValue 与 int 相同。最小值!
0赞 Lars Holm Jensen 10/31/2012
OP 忘记了 t.Days > 30 ?天数 / 30 :
0赞 Matt 11/22/2016
要解决@CodeMonkeyKing提到的问题,您可以使用 SortedDictionary 而不是普通的 : 用法是相同的,但它确保键被排序。但即便如此,该算法也有缺陷,因为无论您使用哪种字典类型,都会返回“95 个月前”,这是不正确的(它应该返回“3 个月前”或“4 个月前”,具体取决于您使用的阈值) - 即使 -3 不会创建过去一年的日期(我已经在 12 月测试过,所以在这种情况下它不应该发生)。DictionaryRelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))
12赞 4 revs, 4 users 80%Will Dean #6

@Jeff

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

无论如何,做减法都会返回 a。DateTimeTimeSpan

所以你可以做

(DateTime.UtcNow - dt).TotalSeconds

我也很惊讶地看到常量是手动乘法的,然后是随着乘法添加的注释。这是一些误导性的优化吗?

14赞 markpasc #7

当您知道观看者的时区时,使用日级的日历日可能会更清晰。我不熟悉 .NET 库,所以不幸的是,我不知道您如何在 C# 中做到这一点。

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

11赞 3 revs, 3 users 77%J #8

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

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

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

14赞 2 revs, 2 users 98%icco #9

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

} ?>

评论

9赞 Kiquenet 3/6/2017
该问题带有 C# 标记。为什么是这个PHP代码恕我直言,仅适用于 C# 代码
75赞 5 revs, 4 users 55%leppie #10
public static string ToRelativeDate(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";
    }

    var aValue = new SortedList<double, Func<string>>();
    aValue.Add(0.75, () => "less than a minute");
    aValue.Add(1.5, () => "about a minute");
    aValue.Add(45, () => string.Format("{0} minutes", Math.Round(TotalMinutes)));
    aValue.Add(90, () => "about an hour");
    aValue.Add(1440, () => string.Format("about {0} hours", Math.Round(Math.Abs(oSpan.TotalHours)))); // 60 * 24
    aValue.Add(2880, () => "a day"); // 60 * 48
    aValue.Add(43200, () => string.Format("{0} days", Math.Floor(Math.Abs(oSpan.TotalDays)))); // 60 * 24 * 30
    aValue.Add(86400, () => "about a month"); // 60 * 24 * 60
    aValue.Add(525600, () => string.Format("{0} months", Math.Floor(Math.Abs(oSpan.TotalDays / 30)))); // 60 * 24 * 365 
    aValue.Add(1051200, () => "about a year"); // 60 * 24 * 365 * 2
    aValue.Add(double.MaxValue, () => string.Format("{0} years", Math.Floor(Math.Abs(oSpan.TotalDays / 365))));

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

http://refactormycode.com/codes/493-twitter-esque-relative-dates

C# 6 版本:

static readonly SortedList<double, Func<TimeSpan, string>> offsets = 
   new SortedList<double, Func<TimeSpan, string>>
{
    { 0.75, _ => "less than a minute"},
    { 1.5, _ => "about a minute"},
    { 45, x => $"{x.TotalMinutes:F0} minutes"},
    { 90, x => "about an hour"},
    { 1440, x => $"about {x.TotalHours:F0} hours"},
    { 2880, x => "a day"},
    { 43200, x => $"{x.TotalDays:F0} days"},
    { 86400, x => "about a month"},
    { 525600, x => $"{x.TotalDays / 30:F0} months"},
    { 1051200, x => "about a year"},
    { double.MaxValue, x => $"{x.TotalDays / 365:F0} years"}
};

public static string ToRelativeDate(this DateTime input)
{
    TimeSpan x = DateTime.Now - input;
    string Suffix = x.TotalMinutes > 0 ? " ago" : " from now";
    x = new TimeSpan(Math.Abs(x.Ticks));
    return offsets.First(n => x.TotalMinutes < n.Key).Value(x) + Suffix;
}

评论

0赞 Pure.Krome 5/6/2009
这是非常好的IMO:)这也可以重构为扩展方法吗?字典是否可以变成静态的,所以它只创建一次,然后从那里引用?
0赞 Chris Charabaruk 7/17/2009
Pure.Krome:stackoverflow.com/questions/11/how-do-i-calculate-relative-time/......
5赞 Drew Noakes 8/23/2010
您可能希望将该字典拉出到字段中,以减少实例化和 GC 流失。您必须更改为 .Func<string>Func<double>
372赞 9 revs, 4 users 69%Ryan McGeary #11

jquery.timeago 插件

Jeff,因为 Stack Overflow 广泛使用 jQuery,所以我推荐 jquery.timeago 插件

好处:

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

只需将其附加到 DOM 上的时间戳即可:

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

这将在标题中转换具有 timeago 类和 ISO 8601 时间戳的所有元素:abbr

<abbr class="timeago" title="2008-07-17T09:24:17Z">July 17, 2008</abbr>

变成这样的东西:

<abbr class="timeago" title="July 17, 2008">4 months ago</abbr>

其产量:4个月前。随着时间的流逝,时间戳将自动更新。

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

评论

39赞 Ryan McGeary 3/24/2009
Seb,如果您禁用了 Javascript,则会显示您最初放在 abbr 标签之间的字符串。通常,这只是您希望的任何格式的日期或时间。Timeago 优雅地降解。它没有变得更简单。
23赞 Rob Fonseca-Ensor 12/26/2009
Ryan,我前段时间建议 SO 使用时间。杰夫的回答让我哭了,我建议你坐下:stackoverflow.uservoice.com/pages/1722-general/suggestions/......
7赞 Ryan McGeary 12/26/2009
呵呵,谢谢罗伯。没关系。它几乎不明显,尤其是当转换过程中只有一个数字发生变化时,尽管 SO 页面有很多时间戳。我本来以为他至少会欣赏页面缓存的好处,即使他选择避免自动更新。我相信 Jeff 也可以提供反馈来改进插件。知道像这样的网站 arstechnica.com 使用它,我感到很欣慰。
19赞 Daniel Earwicker 4/20/2010
@Rob Fonseca-Ensor - 现在它也让我哭了。每分钟更新一次,以显示准确的信息,与每秒闪烁一次的文本有什么关系?
36赞 BartoszKP 11/30/2015
问题是关于 C#,我看不出 jQuery 插件是如何相关的。
10赞 2 revsdreeves #12

这是 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";
37赞 2 revs, 2 users 99%Jauder Ho #13

我建议在客户端也进行计算。减少服务器的工作量。

以下是我使用的版本(来自 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);
                });
        };
}

评论

11赞 Kiquenet 3/6/2017
问题是 C# 标记为什么是 Javascript 代码
74赞 2 revs, 2 users 98%Thomaschaaf #14

这里是 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";
    }
}    

评论

19赞 Kiquenet 3/6/2017
问题是 C# 标记为为什么是 PHP 代码
19赞 3 revs, 2 users 97%Jo Vermeulen #15

在 Java 中有一种简单的方法可以做到这一点吗?这门课似乎相当有限。java.util.Date

这是我快速而肮脏的 Java 解决方案:

import java.util.Date;
import javax.management.timer.Timer;

String getRelativeDate(Date date) {     
  long delta = new Date().getTime() - date.getTime();
  if (delta < 1L * Timer.ONE_MINUTE) {
    return toSeconds(delta) == 1 ? "one second ago" : toSeconds(delta) + " seconds ago";
  }
  if (delta < 2L * Timer.ONE_MINUTE) {
    return "a minute ago";
  }
  if (delta < 45L * Timer.ONE_MINUTE) {
    return toMinutes(delta) + " minutes ago";
  }
  if (delta < 90L * Timer.ONE_MINUTE) {
    return "an hour ago";
  }
  if (delta < 24L * Timer.ONE_HOUR) {
    return toHours(delta) + " hours ago";
  }
  if (delta < 48L * Timer.ONE_HOUR) {
    return "yesterday";
  }
  if (delta < 30L * Timer.ONE_DAY) {
    return toDays(delta) + " days ago";
  }
  if (delta < 12L * 4L * Timer.ONE_WEEK) { // a month
    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 long toSeconds(long date) {
  return date / 1000L;
}

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

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

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

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

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

评论

4赞 Kiquenet 3/6/2017
问题是 C# 标记为为什么是 Java 代码
55赞 6 revs, 3 users 75%neuracnu #16

下面是我作为扩展方法添加到 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();
}
14赞 2 revsChris Charabaruk #17
using System;
using System.Collections.Generic;
using System.Linq;

public static class RelativeDateHelper
{
    private static Dictionary<double, Func<double, string>> sm_Dict = null;

    private static Dictionary<double, Func<double, string>> DictionarySetup()
    {
        var dict = new Dictionary<double, Func<double, string>>();
        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 5/27/2011
字典在这里给你买了什么?
0赞 Chris Charabaruk 5/31/2011
StriplingWarrior:与switch语句或一堆if/else语句相比,易于阅读和修改。字典是静态的,这意味着每次我们要使用 ToRelativeDate 时,都不必创建它和 Func<,> 对象;与我在答案中链接的那个相比,它只创建了一次。
0赞 StriplingWarrior 6/1/2011
明白了。我只是在想,既然文档指出“返回项目的顺序是未定义的”(msdn.microsoft.com/en-us/library/xfhwa508.aspx),那么当您不关心查找时间时,也许这不是最好的数据结构,而是让事情保持井然有序。Dictionary
0赞 Chris Charabaruk 6/1/2011
StriplingWarrior:我相信 LINQ 在与 s 一起使用时会考虑到这一点。如果你仍然对它感到不舒服,你可以使用 SortedDictionary,但我自己的经验表明这是不必要的。Dictionary
20赞 4 revs, 3 users 60%Simon #18

使用 Fluent DateTime

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();
8赞 2 revsantony.trupe #19

用于客户端 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;
 }

}

评论

2赞 Kiquenet 3/6/2017
该问题带有 C# 标记。为什么是这个 Java 代码恕我直言,仅适用于 C# 代码
9赞 4 revs, 4 users 87%Buhake Sindi #20

我从比尔·盖茨(Bill Gates)的一个博客中得到了这个答案。我需要在我的浏览器历史记录中找到它,我会给你链接。

执行相同操作的 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'; }
}

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

19赞 2 revs, 2 users 67%ollie #21

iPhone Objective-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];
    }
}
25赞 3 revsTown #22

晚了几年参加派对,但我在过去和未来的约会中都要求这样做,所以我把杰夫文森特合并到这个里面。这是一场三元的盛会!:)

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;

        /// <summary>
        /// Returns a friendly version of the provided DateTime, relative to now. E.g.: "2 days ago", or "in 6 months".
        /// </summary>
        /// <param name="dateTime">The DateTime to compare to Now</param>
        /// <returns>A friendly string</returns>
        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";
            }
        }
    }
20赞 5 revs, 3 users 72%Owen Blacker #23

鉴于世界和她的丈夫似乎正在发布代码示例,这是我不久前根据其中几个答案编写的内容。

我特别需要这个代码是可本地化的。所以我有两个类 — ,它指定了可本地化的术语,以及 ,它包含一堆扩展方法。我不需要处理未来的日期时间,因此没有尝试使用此代码处理它们。GrammarFuzzyDateExtensions

我在源代码中保留了一些 XMLdoc,但为了简洁起见,删除了大部分(它们很明显的地方)。我也没有把每个班级成员都包括在内:

public class Grammar
{
    /// <summary> Gets or sets the term for "just now". </summary>
    public string JustNow { get; set; }
    /// <summary> Gets or sets the term for "X minutes ago". </summary>
    /// <remarks>
    ///     This is a <see cref="String.Format"/> pattern, where <c>{0}</c>
    ///     is the number of minutes.
    /// </remarks>
    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; }
    /// <summary> Gets or sets the term for "ages ago". </summary>
    public string AgesAgo { get; set; }

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

    /// <summary>
    ///     Initialises a new <see cref="Grammar"/> instance with the
    ///     specified properties.
    /// </summary>
    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());
    }
}

除了本地化之外,我想要实现的关键目标之一是“今天”仅表示“这个日历日”,因此 , , 方法如下所示:IsTodayIsThisMonthIsThisYear

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)

6赞 2 revs, 2 users 50%JoeyFur62 #24
var ts = new TimeSpan(DateTime.Now.Ticks - dt.Ticks);
5赞 tugberk #25

我会为此提供一些方便的扩展方法,并使代码更具可读性。首先,对 的几种扩展方法。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
8赞 3 revs, 3 users 95%Prashant Gupta #26

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

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;
}
12赞 Premdeep Mohanty #27

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

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";
}
51赞 2 revs, 2 users 97%Karl-Henrik #28

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)在他的博客上写了一篇关于它的文章

评论

3赞 Ahmad 3/11/2017
友情提示:在 .net 4.5 或更高版本上,不要安装完整的 Humanizer...只安装Humanizer.Core的一部分。原因 此版本不支持其他语言包
6赞 Bgl86 9/8/2015 #29

如果你想有一个像这样的输出,你需要一个时间跨度:"2 days, 4 hours and 12 minutes ago"

TimeSpan timeDiff = DateTime.Now-CreatedDate;

然后,您可以访问您喜欢的值:

timeDiff.Days
timeDiff.Hours

等。。。

12赞 Piotr Stapp 9/8/2015 #30

您可以按如下方式使用 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;
}

或者将 jQuery 插件与 Timeago 的 Razor 扩展一起使用。