37 Вопрос: Рассчитать относительное время в C #

вопрос создан в Sun, Jun 4, 2017 12:00 AM

При заданном значении DateTime как отображать относительное время, например:

  • 2 часа назад
  • 3 дня назад
  • месяц назад
1435
  1. Что, если вы хотите рассчитать относительное время с настоящего момента до будущего?
    2009-03-26 20: 42: 53Z
  2. moment.js - очень хорошая библиотека для разбора дат. Вы можете использовать ее (на стороне сервера или на стороне клиента), в зависимости от ваших потребностей. просто к твоему сведению, потому что здесь никто не упомянул
    2014-06-30 13: 26: 43Z
  3. Существует пакет .net github.com/NickStrupat /TimeAgo , который в значительной степени делает то, что просят.
    2016-06-08 11: 50: 57Z
30 ответов                              30                         

Джефф, ваш код хорош, но может быть понятнее с константами (как предложено в Code Complete).

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";
}
    
937
2017-05-23 12: 10: 45Z
  1. Я ненавижу такие константы со страстью. Это кому-то кажется неправильным? Thread.Sleep(1 * MINUTE)? Потому что это неправильно в 1000 раз.
    2010-08-17 17: 06: 20Z
  2. const int SECOND = 1; Так странно, что секунда - это одна секунда.
    2011-06-17 22: 46: 18Z
  3. Этот тип кода практически невозможно локализовать. Если ваше приложение должно оставаться только на английском языке, тогда хорошо. Но если вы перейдете на другие языки, вы возненавидите себя за то, что делаете логику подобным образом. Просто чтобы вы знали ...
    2012-05-23 14: 31: 17Z
  4. Я думаю, что если бы константы были переименованы, чтобы точно описать значение, которое в них содержится, это было бы легче понять. Итак, SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour; и т. д. Просто назвать его MINUTE = 60 не позволяет читателю определить значение.
    2012-08-29 16: 21: 40Z
  5. Почему никто (кроме Джо) не заботится о неправильном значении «вчера» или «дня назад» ??? Вчера это не часовой расчет, а ежедневный расчет. Так что да, это неправильный код, по крайней мере, в двух частых случаях.
    2013-09-26 15: 47: 05Z

плагин jquery.timeago

Джефф, потому что в Stack Overflow широко используется jQuery, я рекомендую подключаемый модуль jquery.timeago .

Преимущества:

  • Избегайте отметок времени от «1 минута назад», даже если страница была открыта 10 минут назад; Timeago обновляется автоматически.
  • Вы можете в полной мере использовать преимущества кэширования страниц и /или фрагментов в своих веб-приложениях, поскольку временные метки не рассчитываются на сервере.
  • Вы можете использовать микроформаты, как крутые дети.

Просто прикрепите его к своим временным меткам на DOM ready:

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

Это превратит все элементы abbr в класс времени.назад и ISO 8601 отметка времени в заголовке:

<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 месяца назад. По прошествии времени метки времени будут автоматически обновляться.

Отказ от ответственности: я написал этот плагин, поэтому я пристрастен.

    
358
2018-07-05 03: 59: 30Z
  1. Seb. Если у вас отключен Javascript, отображается строка, которую вы изначально поместили между тегами abbr. Как правило, это просто дата или время в любом формате, который вы пожелаете. Timeago грациозно ухудшается. Это не становится намного проще.
    2009-03-24 04: 40: 18Z
  2. Райан, я предложил ТАК использовать время назад. Ответ Джеффа заставил меня плакать, я предлагаю вам сесть: stackoverflow.uservoice.com/pages/1722-general/suggestions/…
    2009-12-26 07: 26: 41Z
  3. Хех, спасибо, Роб. Это нормально. Это едва заметно, особенно когда во время перехода изменяется только одно число, хотя на страницах SO много временных отметок. Я бы подумал, что он по крайней мере оценил бы преимущества кэширования страниц, даже если бы он решил избегать автообновлений. Я уверен, что Джефф мог бы оставить отзыв, чтобы улучшить плагин. Я пользуюсь утешением, зная, что такие сайты, как arstechnica.com , используют его.
    2009-12-26 13: 56: 41Z
  4. @ Роб Фонсека-Энсор - теперь я тоже плачу. Как происходит обновление раз в минуту, чтобы показывать точную информацию, каким-либо образом связанную с миганием текста раз в секунду?
    2010-04-20 00: 23: 49Z
  5. Вопрос касается C #, я не вижу, насколько уместен плагин jQuery.
    2015-11-30 12: 25: 09Z

Вот как я это делаю

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

Предложения? Комментарии? Как улучшить этот алгоритм?

    
326
2018-01-12 16: 10: 22Z
  1. "< 48 * 60 * 60s" - довольно необычное определение для "вчера". Если в среду 9 часов утра, неужели вы думаете о 9:01 утра в понедельник как о «вчера»? Я бы подумал, что алгоритм для вчерашнего дня или "n дней назад" следует рассмотреть до /после полуночи.
    2009-02-01 19: 33: 54Z
  2. Компиляторы, как правило, довольно хороши в предварительном вычислении константных выражений, например, 24 * 60 * 60, поэтому вы можете напрямую использовать их вместо того, чтобы вычислять их как 86400 и помещать оригинальное выражение в комментариях
    2009-06-11 12: 50: 23Z
  3. заметил, что эта функция исключает недели
    2010-01-26 21: 03: 44Z
  4. @ bzlm Я думаю, что сделал для проекта, над которым я работал. Моя мотивация заключалась в том, чтобы предупредить других о том, что в этом примере кода были пропущены недели. Что касается того, как это сделать, мне это показалось довольно простым.
    2010-11-09 23: 38: 21Z
  5. Я думаю, что хорошим способом улучшить алгоритм является отображение 2 единиц, таких как «2 месяца 21 день назад», «1 час 40 минут назад» для повышения точности.
    2012-02-01 13: 42: 59Z
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 "";
}

Я предпочитаю эту версию за ее краткость и возможность добавлять новые отметки. Это может быть инкапсулировано с расширением Latest() для Timespan вместо этого длинного 1 вкладыша, но для краткости публикации это подойдет. Это исправляет час назад, 1 час назад, предоставляя час до истечения 2 часов

    
91
2018-02-22 16: 30: 42Z
  1. Я получаю всевозможные проблемы с использованием этой функции, например, если вы издеваетесь над 'theDate = DateTime.Now.AddMinutes (-40);' Я получаю «40 часов назад», но с ответом рефакторикода Майкла, он возвращается правильно в «40 минут назад»?
    2008-12-15 02: 17: 48Z
  2. я думаю, что вы пропустили ноль, попробуйте: давно с = (DateTime.Now.Ticks - theDate.Ticks) /10000000;
    2009-08-07 19: 37: 37Z
  3. Хм, хотя этот код может работать, он некорректен и недопустим, если предположить, что порядок ключей в Словаре будет в определенном порядке. Словарь использует Object.GetHashCode (), который возвращает не long, а int !. Если вы хотите, чтобы они были отсортированы, вам следует использовать SortedList < long, string &gt ;. Что не так с пороговыми значениями, оцениваемыми в наборе if /else if /.../else? Вы получаете такое же количество сравнений. К сведению, хеш для long.MaxValue оказывается таким же, как int.MinValue!
    2011-11-09 04: 47: 25Z
  4. ОП забыл t.Days > 30? t.Days /30:
    2012-10-31 10: 39: 54Z
  5. Чтобы исправить проблему, упомянутую @CodeMonkeyKing, вы можете использовать SortedDictionary вместо простого Dictionary: использование такое же, но оно гарантирует, что ключи отсортированы. Но даже в этом случае алгоритм имеет недостатки, потому что RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3)) возвращает «95 месяцев назад» , независимо от того, какой тип словаря вы используете, что неверно (он должен возвращать «3 месяца назад» или «4 месяца» назад "в зависимости от того, какой порог вы используете) - даже если -3 не создает дату в прошлом году (я проверял это в декабре, поэтому в этом случае это не должно происходить).
    2016-11-22 12: 00: 41Z

Вот переписывание сценария Jeffs Script для 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";
    }
}    
    
71
2012-02-06 02: 04: 43Z
  1. Вопрос помечен C # почему код PHP ?
    2017-03-06 10: 31: 26Z
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- относительные даты

Версия 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;
}
    
65
2016-05-27 19: 26: 31Z
  1. это очень хорошая IMO :) Это также может быть изменено как метод расширения? может ли словарь стать статичным, поэтому он создается только один раз и на него ссылаются потом?
    2009-05-06 12: 29: 46Z
  2. 2009-07-17 02: 48: 11Z
  3. Возможно, вы захотите извлечь этот словарь в поле, чтобы уменьшить количество экземпляров и отток GC. Вам нужно изменить Func<string> на Func<double>.
    2010-08-23 10: 34: 59Z
  4. 2013-09-26 13: 13: 57Z

Вот реализация, которую я добавил в качестве метода расширения для класса 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();
}
    
50
2014-02-18 14: 20: 03Z

Я бы порекомендовал вычислить это и на стороне клиента. Меньше работы для сервера.

Ниже приведена версия, которую я использую (от Зак Кожаного)

/*
 * 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);
                });
        };
}
    
39
2014-02-18 14: 20: 13Z
  1. Вопрос помечен C # , почему код Javascript ?
    2017-03-06 10: 37: 39Z

Есть также пакет под названием Humanizer для Nuget, и он действительно работает очень хорошо

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"

Скотт Хансельман написал на своем блог

    
34
2014-04-09 11: 50: 10Z
  1. дружеское примечание: на .net 4.5 или выше не устанавливайте полный Humanizer ... устанавливайте только Humanizer.Core часть этого .. потому что другие языковые пакеты не поддерживаются на этой версии
    2017-03-10 18: 50: 32Z
  2. Так полезно! Этот ответ должен быть намного выше в этом списке. Если бы у меня было 100 голосов, я бы отдал это за это. Судя по всему (из JS-land), поиск этого пакета был непростым.
    2017-05-27 23: 54: 28Z

@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();
}
    
29
2008-08-01 13: 16: 49Z

Пару лет назад на вечеринку, но у меня было требование сделать это как для прошлых, так и для будущих дат, поэтому я объединил Джеффа и Винсент в это. Это тернаритическая феерия! :)

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";
            }
        }
    }
    
24
2017-05-23 12: 10: 45Z

Есть ли простой способ сделать это на 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;
}
    
22
2014-02-05 09: 32: 17Z
  1. Вопрос помечен C # почему код Java ?
    2017-03-06 10: 39: 01Z

Версия для 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];
    }
}
    
20
2018-10-05 02: 03: 55Z

Учитывая, что мир и ее муж, похоже, публикуют примеры кода, вот то, что я написал некоторое время назад, на основе нескольких из этих ответов.

У меня была особая необходимость, чтобы этот код был локализуемым. Итак, у меня есть два класса - Grammar, который определяет локализуемые термины, и FuzzyDateExtensions, который содержит несколько методов расширения. Мне не нужно было иметь дело с будущими датами, поэтому я не пытался справиться с ними с помощью этого кода.

Я оставил некоторые XML-документы в источнике, но для краткости удалил большинство (где они были бы очевидны). Я также не включил сюда каждого ученика:

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

Одной из ключевых вещей, которых я хотел добиться, а также локализации, было то, что «сегодня» будет означать только «этот календарный день», поэтому методы IsToday, IsThisMonth, 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;
}

Я надеюсь, что люди найдут это полезным и /или интересным: о)

    
19
2018-02-22 16: 34: 26Z

используя 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();
    
18
2018-07-05 04: 00: 56Z

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

} ?>
    
17
2012-02-06 02: 05: 49Z
  1. Вопрос помечен C # . Почему этот код PHP ? ИМХО, применяется только код C #
    2017-03-06 10: 39: 51Z

Я думал, что смогу сделать это, используя классы и полиморфизм. ябыла предыдущая итерация, в которой использовалось подклассификация, которая в итоге имела слишком много накладных расходов. Я переключился на более гибкую объектную модель делегата /публичного свойства, которая значительно лучше. Мой код немного более точен, я хотел бы придумать лучший способ генерировать «месяцы назад», который не выглядел бы слишком изощренным.

Я думаю, что я все еще буду придерживаться каскада Джеффа if-then, потому что он меньше кода и проще (определенно легче гарантировать, что он будет работать должным образом).

Для приведенного ниже кода PrintRelativeTime.GetRelativeTimeMessage (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);
    }
}
    
14
2008-08-05 01: 23: 39Z

Когда вы знаете часовой пояс зрителя, возможно, будет удобнее использовать календарные дни в масштабе дня. Я не знаком с библиотеками .NET, поэтому, к сожалению, я не знаю, как вы это сделаете в C #.

На потребительских сайтах вы также можете помахать рукой меньше минуты. «Менее минуты назад» или «только сейчас» может быть достаточно хорошо.

    
12
2008-08-15 22: 42: 33Z
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;
    }
}

Так же, как и другой ответ на этот вопрос , но как метод расширения со статическим словарем.

    
12
2017-05-23 10: 31: 37Z
  1. Что словарь покупает здесь?
    2011-05-26 21: 46: 22Z
  2. StriplingWarrior: простота чтения и изменения по сравнению с оператором switch или стеком операторов if /else. Статический словарь означает, что он и Func <, > объекты не нужно создавать каждый раз, когда мы хотим использовать ToRelativeDate; он создается только один раз по сравнению с тем, который я указал в своем ответе.
    2011-05-31 04: 42: 40Z
  3. Я вижу. Я просто подумал, поскольку в документации по Dictionary говорится, что «порядок, в котором возвращаются элементы, не определен» ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) возможно, это не лучшая структура данных для использования, когда вам не нужно столько времени искать, сколько иметь вещи остаются в порядке.
    2011-05-31 16: 00: 16Z
  4. StriplingWarrior: Я считаю, что LINQ учитывает это при использовании с Dictionarys. Если вам по-прежнему не по себе, вы можете использовать SortedDictionary , но мой собственный опыт показывает, что в этом нет необходимости.
    2011-06-01 02: 44: 57Z

вы можете попробовать это. Я думаю, что это будет работать правильно.

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";
}
    
12
2013-10-15 09: 36: 10Z

@Jeff р>

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

Выполнение вычитания DateTime в любом случае возвращает TimeSpan.

Так что вы можете просто сделать

(DateTime.UtcNow - dt).TotalSeconds

Я также удивлен, что константы были умножены вручную, а затем добавлены комментарии с умножениями. Было ли это неправильным оптимизмом?Тион?

    
10
2018-10-05 02: 38: 18Z

Java для использования gwt на стороне клиента:

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

}
    
9
2009-11-14 18: 56: 01Z
  1. Вопрос помечен C # . Почему этот код Java ? ИМХО, применяется только код C #
    2017-03-06 10: 40: 35Z

Вот алгоритм, который использует 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";
    
8
2008-09-23 01: 39: 15Z

Вы можете использовать расширение 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 с расширением Razor от Timeago. р>     

8
2016-07-13 23: 23: 58Z

Вы можете уменьшить нагрузку на стороне сервера, выполнив эту логику на стороне клиента. Посмотреть источник на некоторых страницах Digg для справки. У них сервер выдает значение времени эпохи, которое обрабатывается Javascript. Таким образом, вам не нужно управлять часовым поясом конечного пользователя. Новый код на стороне сервера будет выглядеть примерно так:

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

Вы даже можете добавить туда блок NOSCRIPT и просто выполнить ToString ().

    
8
2018-09-27 09: 13: 12Z

Это я получил из блога Билла Гейтса. Мне нужно найти его в истории моего браузера, и я дам вам ссылку.

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

Обычно вы работаете в секундах ...

    
8
2018-09-27 09: 14: 26Z
/** 
 * {@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";
    }
}
    
6
2014-09-05 01: 11: 31Z

Я думаю, что к этому посту уже есть несколько ответов, но можно использовать его, который прост в использовании, как плагин, и легко читаем для программистов. Отправьте свою конкретную дату и получите ее значение в виде строки:

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;
}
    
6
2018-09-27 09: 14: 42Z

Если вы хотите иметь вывод, например "2 days, 4 hours and 12 minutes ago", вам нужен промежуток времени:

TimeSpan timeDiff = DateTime.Now-CreatedDate;

Затем вы можете получить доступ к значениям, которые вам нравятся:

timeDiff.Days
timeDiff.Hours

и т.д ...

    Дел>
6
2018-09-27 10: 34: 32Z
var ts = new TimeSpan(DateTime.Now.Ticks - dt.Ticks);
    
5
2018-09-27 09: 15: 01Z
источник размещен Вот