Berechnen Sie die relative Zeit in C #

1513

DateTimeWie zeige ich bei einem bestimmten Wert die relative Zeit an, z.

  • vor 2 Stunden
  • vor 3 Tagen
  • vor einem Monat
Jeff Atwood
quelle
80
Was ist, wenn Sie eine relative Zeit von jetzt bis in die Zukunft berechnen möchten?
Jhonny D. Cano -Leftware-
2
moment.js ist eine sehr schöne Datumsanalyse-Bibliothek. Sie können diese je nach Ihren Anforderungen (Server- oder Client-Seite) verwenden. Nur zu Ihrer Information, weil es hier niemand erwähnt hat
Matej
1
Es gibt das .net-Paket github.com/NickStrupat/TimeAgo, das so ziemlich das tut, was gefragt wird.
Rossco
Dieses Projekt eignet sich hervorragend für die Datumsformatierung. Github.com/Humanizr/Humanizer#humanize-datetime
Aaron Hudon

Antworten:

988

Jeff, dein Code ist nett, könnte aber mit Konstanten klarer sein (wie in Code Complete vorgeschlagen).

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";
}
Vincent Robert
quelle
220
Ich hasse solche Konstanten aus Leidenschaft. Sieht das für jemanden falsch aus? Thread.Sleep(1 * MINUTE)? Weil es um den Faktor 1000 falsch ist.
Roman Starkov
31
const int SECOND = 1;So seltsam ist eine Sekunde eine Sekunde.
Seriousdev
62
Diese Art von Code ist kaum zu lokalisieren. Wenn Ihre App nur auf Englisch bleiben muss, ist das in Ordnung. Aber wenn Sie den Sprung in andere Sprachen schaffen, werden Sie sich dafür hassen, dass Sie Logik wie diese machen. Nur damit Sie wissen ...
Nik Reiman
73
Ich denke, wenn die Konstanten umbenannt würden, um den Wert, der in ihnen enthalten ist, genau zu beschreiben, wäre es einfacher zu verstehen. Also SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour; usw. Wenn Sie es nur MINUTE = 60 nennen, kann der Leser den Wert nicht bestimmen.
Slolife
14
Warum kümmert sich niemand (außer Joe) um den falschen Wert für "Gestern" oder "Vor Tagen" ??? Gestern ist keine Stundenberechnung, sondern eine tägliche Berechnung. Also ja, dies ist zumindest in zwei häufigen Fällen ein falscher Code.
CtrlX
363

jquery.timeago Plugin

Jeff, da Stack Overflow jQuery häufig verwendet, empfehle ich das Plugin jquery.timeago .

Leistungen:

  • Vermeiden Sie Zeitstempel mit dem Datum "vor 1 Minute", obwohl die Seite vor 10 Minuten geöffnet wurde. timeago wird automatisch aktualisiert.
  • Sie können das Seiten- und / oder Fragment-Caching in Ihren Webanwendungen voll ausnutzen, da die Zeitstempel nicht auf dem Server berechnet werden.
  • Sie können Mikroformate wie die coolen Kids verwenden.

Hängen Sie es einfach an Ihre Zeitstempel auf DOM bereit:

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

Dadurch werden alle abbrElemente mit einer Zeitklasse und einem ISO 8601- Zeitstempel im Titel gedreht:

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

in so etwas:

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

was ergibt: vor 4 Monaten. Mit der Zeit werden die Zeitstempel automatisch aktualisiert.

Haftungsausschluss: Ich habe dieses Plugin geschrieben, bin also voreingenommen.

Ryan McGeary
quelle
39
Seb, Wenn Sie Javascript deaktiviert haben, wird die Zeichenfolge angezeigt, die Sie ursprünglich zwischen die abbr-Tags eingefügt haben. In der Regel ist dies nur ein Datum oder eine Uhrzeit in einem beliebigen Format. Timeago verschlechtert sich anmutig. Einfacher geht es nicht.
Ryan McGeary
23
Ryan, ich habe vor einiger Zeit vorgeschlagen, dass SO timeago verwendet. Jeffs Antwort brachte mich zum Weinen, ich schlage vor, Sie setzen sich: stackoverflow.uservoice.com/pages/1722-general/suggestions/…
Rob Fonseca-Ensor
7
Heh, danke Rob. Das ist okay. Es ist kaum wahrnehmbar, insbesondere wenn sich während des Übergangs nur eine Nummer ändert, obwohl SO-Seiten viele Zeitstempel haben. Ich hätte gedacht, er hätte zumindest die Vorteile des Seiten-Cachings geschätzt, selbst wenn er sich dafür entschieden hätte, automatische Updates zu vermeiden. Ich bin sicher, Jeff hätte Feedback geben können, um auch das Plugin zu verbessern. Ich tröste mich mit dem Wissen, dass Websites wie arstechnica.com es verwenden.
Ryan McGeary
19
@ Rob Fonseca-Ensor - jetzt bringt es mich auch zum Weinen. Wie kann ein Update einmal pro Minute durchgeführt werden, um genaue Informationen anzuzeigen, die in irgendeiner Weise damit zusammenhängen, dass Text einmal pro Sekunde blinkt?
Daniel Earwicker
25
Die Frage bezieht sich auf C #. Ich sehe nicht, wie relevant ein jQuery-Plugin ist.
BartoszKP
331

So mache ich es

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

Vorschläge? Bemerkungen? Möglichkeiten, diesen Algorithmus zu verbessern?

Jeff Atwood
quelle
112
"<48 * 60 * 60s" ist eine eher unkonventionelle Definition für "gestern". Wenn es am Mittwoch 9 Uhr morgens ist, würden Sie 9:01 Uhr am Montag wirklich als "gestern" betrachten. Ich hätte gedacht, ein Algorithmus für gestern oder "vor n Tagen" sollte vor / nach Mitternacht in Betracht gezogen werden.
Joe
139
Compiler sind normalerweise ziemlich gut darin, konstante Ausdrücke wie 24 * 60 * 60 vorab zu berechnen. Sie können diese also direkt verwenden, anstatt sie selbst auf 86400 zu berechnen und den ursprünglichen Ausdruck in Kommentare zu setzen
zvolkov
11
@bzlm Ich glaube, ich habe es für ein Projekt getan, an dem ich gearbeitet habe. Meine Motivation hier war es, andere darauf aufmerksam zu machen, dass Wochen in diesem Codebeispiel weggelassen wurden. Wie das geht, kam mir ziemlich direkt vor.
Jray
9
Ich denke, dass ein guter Weg, um den Algorithmus zu verbessern, darin besteht, 2 Einheiten wie "vor 2 Monaten 21 Tagen" und "vor 1 Stunde 40 Minuten" anzuzeigen, um die Genauigkeit zu erhöhen.
Evgeny Levin
5
@ Jeffy, Sie haben die Berechnung für das Schaltjahr und die damit verbundenen Prüfungen verpasst
Saboor Awan
92
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 "";
}

Ich bevorzuge diese Version wegen ihrer Prägnanz und der Fähigkeit, neue Tickpunkte hinzuzufügen. Dies könnte mit einer Latest()Erweiterung auf Timespan anstelle dieses langen 1-Liner gekapselt werden , aber der Kürze halber reicht dies aus. Dies behebt das Problem vor einer Stunde, vor 1 Stunde, indem eine Stunde angegeben wird, bis 2 Stunden vergangen sind

DevelopingChris
quelle
Ich bekomme alle möglichen Probleme mit dieser Funktion, zum Beispiel wenn Sie 'theDate = DateTime.Now.AddMinutes (-40);' verspotten. Ich erhalte 'vor 40 Stunden', aber mit Michaels Refactormycode-Antwort wird es korrekt bei 'vor 40 Minuten' zurückgegeben?
GONeale
Ich denke, Sie vermissen eine Null, versuchen Sie: long Since = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
Robnardo
8
Hmm, obwohl dieser Code möglicherweise funktioniert, ist es falsch und ungültig anzunehmen, dass die Reihenfolge der Schlüssel im Wörterbuch in einer bestimmten Reihenfolge liegt. Das Wörterbuch verwendet den Object.GetHashCode (), der kein langes, sondern ein int! Zurückgibt. Wenn Sie möchten, dass diese sortiert werden, sollten Sie eine SortedList <long, string> verwenden. Was ist falsch daran, dass die Schwellenwerte in einer Reihe von if / else if /.../ else ausgewertet werden? Sie erhalten die gleiche Anzahl von Vergleichen. Zu Ihrer Information, der Hash für lange. MaxValue ist der gleiche wie int.MinValue!
CodeMonkeyKing
OP vergessen t.Days> 30? t.Days / 30:
Lars Holm Jensen
Um das von @CodeMonkeyKing erwähnte Problem zu beheben, können Sie ein SortedDictionarystatt eines einfachen verwenden Dictionary: Die Verwendung ist dieselbe, stellt jedoch sicher, dass die Schlüssel sortiert sind. Aber selbst dann hat der Algorithmus Fehler, da RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))kehrt „95 Monate vor“ , unabhängig , welche Wörterbuchtyp Sie verwenden, was falsch ist (es sollte zurückkehren „3 Monate zuvor“ oder „4 Monate“ je nachdem , welcher Schwelle Sie‘ Wiederverwendung) - auch wenn -3 im letzten Jahr kein Datum erstellt (ich habe dies im Dezember getestet, daher sollte es in diesem Fall nicht passieren).
Matt
71

Hier eine Neufassung von Jeffs Script für 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";
    }
}    
Thomaschaaf
quelle
7
Die Frage ist mit C # gekennzeichnet. Warum PHP-Code ?
Kiquenet
65
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 Version:

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;
}
Leppie
quelle
das ist sehr schön IMO :) Dies könnte auch als Erweiterungsmethode überarbeitet werden? Könnte das Wörterbuch statisch werden, sodass es nur einmal erstellt und von da an referenziert wird?
Pure.Krome
5
Sie möchten dieses Wörterbuch wahrscheinlich in ein Feld ziehen, um die Instanziierung und die GC-Abwanderung zu reduzieren. Sie müssten sich ändern Func<string>zu Func<double>.
Drew Noakes
49

Hier ist eine Implementierung, die ich als Erweiterungsmethode zur DateTime-Klasse hinzugefügt habe. Sie behandelt sowohl zukünftige als auch vergangene Daten und bietet eine Annäherungsoption, mit der Sie den gewünschten Detaillierungsgrad angeben können ("vor 3 Stunden" gegenüber "3 Stunden". Vor 23 Minuten, 12 Sekunden "):

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

Ich würde empfehlen, dies auch auf der Client-Seite zu berechnen. Weniger Arbeit für den Server.

Das Folgende ist die Version, die ich benutze (von 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);
                });
        };
}
Jauder Ho
quelle
4
Die Frage ist mit C # gekennzeichnet. Warum Javascript-Code ?
Kiquenet
36

Es gibt auch ein Paket namens Humanizr auf Nuget, das wirklich gut funktioniert und in der .NET Foundation enthalten ist.

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 hat einen Artikel darüber in seinem Blog

Karl-Henrik
quelle
3
freundlicher Hinweis: Installieren Sie auf .net 4.5 oder höher nicht den vollständigen Humanizer ... installieren Sie nur Humanizer.Core Teil davon .. weil andere Sprachpakete in dieser Version nicht unterstützt werden
Ahmad
So nützlich! Diese Antwort muss in dieser Liste viel höher sein. Wenn ich 100 Stimmen hätte, würde ich es geben. Anscheinend (aus JS-Land) war die Suche nach diesem Paket nicht einfach.
Kumarshar
29

@ Jeff

IMHO deine scheint ein wenig lang. Mit Unterstützung für "gestern" und "Jahre" scheint es jedoch etwas robuster zu sein. Nach meiner Erfahrung ist es jedoch am wahrscheinlichsten, dass die Person den Inhalt in den ersten 30 Tagen anzeigt, wenn dies verwendet wird. Es sind nur die wirklich Hardcore-Leute, die danach kommen. Deshalb entscheide ich mich normalerweise dafür, dies kurz und einfach zu halten.

Dies ist die Methode, die ich derzeit auf einer meiner Websites verwende. Dies gibt nur einen relativen Tag, eine relative Stunde und eine relative Uhrzeit zurück. Und dann muss der Benutzer in der Ausgabe auf "vor" klatschen.

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();
}
Nick Berardi
quelle
24

Ein paar Jahre zu spät zur Party, aber ich musste dies sowohl für vergangene als auch für zukünftige Daten tun, also kombinierte ich Jeffs und Vincents dazu. Es ist eine ternarytastische Extravaganz! :) :)

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";
            }
        }
    }
Stadt
quelle
21

Gibt es eine einfache Möglichkeit, dies in Java zu tun? Die java.util.DateKlasse scheint eher begrenzt.

Hier ist meine schnelle und schmutzige Java-Lösung:

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;
}
Jo Vermeulen
quelle
1
Die Frage ist mit C # gekennzeichnet. Warum Java-Code ?
Kiquenet
20

iPhone Objective-C-Version

+ (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];
    }
}
ollie
quelle
19

Angesichts der Tatsache, dass die Welt und ihr Ehemann anscheinend Codebeispiele veröffentlichen, habe ich vor einiger Zeit Folgendes geschrieben, basierend auf einigen dieser Antworten.

Ich hatte ein spezielles Bedürfnis, dass dieser Code lokalisierbar ist. Ich habe also zwei Klassen - Grammardie die lokalisierbaren Begriffe angeben und FuzzyDateExtensionseine Reihe von Erweiterungsmethoden enthalten. Ich musste mich nicht mit zukünftigen Datenzeiten befassen, daher wird kein Versuch unternommen, sie mit diesem Code zu behandeln.

Ich habe einen Teil des XMLdocs in der Quelle belassen, aber der Kürze halber die meisten (wo sie offensichtlich wären) entfernt. Ich habe auch nicht jedes Klassenmitglied hier aufgenommen:

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)
    { ... }
}

Die FuzzyDateStringKlasse enthält:

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

Eines der wichtigsten Dinge , die ich als auch Lokalisierung, war , dass „heute“ würde nur bedeuten , „dieses Kalendertag“, so das erreichen wollte IsToday, IsThisMonth, IsThisYearschauen Methoden wie folgt aus :

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

und die Rundungsmethoden sind wie folgt (ich habe aufgenommen RoundedMonths, da das etwas anders ist):

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

Ich hoffe, die Leute finden das nützlich und / oder interessant: o)

Owen Blacker
quelle
17

In PHP mache ich das so:

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

} ?>
icco
quelle
5
Die Frage ist mit C # gekennzeichnet . Warum dieser PHP-Code ? IMHO, gilt nur C # Code
Kiquenet
17

mit 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();
Simon
quelle
14

Ich dachte, ich würde es mit Klassen und Polymorphismus versuchen. Ich hatte eine frühere Iteration, bei der Unterklassen verwendet wurden, die viel zu viel Aufwand verursachten. Ich habe zu einem flexibleren Objektmodell für Delegierte / öffentliche Objekte gewechselt, das erheblich besser ist. Mein Code ist etwas genauer, ich wünschte, ich könnte einen besseren Weg finden, um "vor Monaten" zu generieren, der nicht zu überentwickelt schien.

Ich denke, ich würde mich immer noch an Jeffs Wenn-Dann-Kaskade halten, weil es weniger Code und einfacher ist (es ist definitiv einfacher sicherzustellen, dass es wie erwartet funktioniert).

Für den folgenden Code gibt PrintRelativeTime.GetRelativeTimeMessage (vor TimeSpan) die relative Zeitnachricht zurück (z. B. "gestern").

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);
    }
}
Keil
quelle
13
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;
    }
}

Das gleiche wie eine andere Antwort auf diese Frage, jedoch als Erweiterungsmethode mit einem statischen Wörterbuch.

Chris Charabaruk
quelle
Was kauft dir das Wörterbuch hier?
StriplingWarrior
StriplingWarrior: Einfaches Lesen und Ändern im Vergleich zu einer switch-Anweisung oder einem Stapel von if / else-Anweisungen. Das statische Wörterbuch bedeutet, dass es und die Func <,> -Objekte nicht jedes Mal erstellt werden müssen, wenn ToRelativeDate verwendet werden soll. Es wird nur einmal erstellt, verglichen mit dem, den ich in meiner Antwort verlinkt habe.
Chris Charabaruk
Aha. Ich habe nur gedacht, da in der Dokumentation zu Dictionary"Die Reihenfolge, in der die Artikel zurückgegeben werden, ist nicht definiert" ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) angegeben ist, dass dies möglicherweise nicht die beste zu verwendende Datenstruktur ist wenn Sie sich weniger für Suchzeiten interessieren als dafür, dass die Dinge in Ordnung bleiben.
StriplingWarrior
StriplingWarrior: Ich glaube, LINQ berücksichtigt dies, wenn es mit Dictionarys verwendet wird. Wenn Sie sich immer noch unwohl fühlen, können Sie es verwenden SortedDictionary, aber meine eigene Erfahrung zeigt, dass dies unnötig ist.
Chris Charabaruk
12

Wenn Sie die Zeitzone des Betrachters kennen, ist es möglicherweise klarer, Kalendertage auf der Tagesskala zu verwenden. Ich bin mit den .NET-Bibliotheken nicht vertraut, daher weiß ich leider nicht, wie Sie das in C # tun würden.

Auf Verbraucherseiten können Sie auch unter einer Minute von Hand wavier sein. "Vor weniger als einer Minute" oder "gerade jetzt" könnte gut genug sein.

Markpasc
quelle
11

Sie können dies versuchen. Ich denke, es wird richtig funktionieren.

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";
}
Premdeep Mohanty
quelle
9

Java für die clientseitige GWT-Nutzung:

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

}
antony.trupe
quelle
Die Frage ist mit C # gekennzeichnet . Warum dieser Java-Code ? IMHO, gilt nur C # -Code
Kiquenet
9

@ Jeff

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

Doing eine Subtraktion auf DateTimekehrt ein TimeSpanohnehin.

Also kannst du es einfach tun

(DateTime.UtcNow - dt).TotalSeconds

Ich bin auch überrascht zu sehen, wie die Konstanten von Hand multipliziert und dann Kommentare mit den Multiplikationen hinzugefügt werden. War das eine fehlgeleitete Optimierung?

Will Dean
quelle
8

Hier ist der Algorithmus, den der Stackoverflow verwendet, der jedoch im perlischen Pseudocode mit einer Fehlerbehebung (keine "vor einer Stunde") präziser umgeschrieben wurde. Die Funktion benötigt eine (positive) Anzahl von Sekunden und gibt eine menschenfreundliche Zeichenfolge wie "vor 3 Stunden" oder "gestern" zurück.

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";
dreeves
quelle
8

Sie können die TimeAgo-Erweiterung verwenden, die wie folgt aussieht:

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

Oder verwenden Sie das jQuery-Plugin mit der Razor-Erweiterung von Timeago.

Piotr Stapp
quelle
8

Sie können die serverseitige Last reduzieren, indem Sie diese Logik clientseitig ausführen. Zeigen Sie die Quelle auf einigen Digg-Seiten als Referenz an. Der Server gibt einen Epochenzeitwert aus, der von Javascript verarbeitet wird. Auf diese Weise müssen Sie die Zeitzone des Endbenutzers nicht verwalten. Der neue serverseitige Code wäre ungefähr so:

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

Sie können dort sogar einen NOSCRIPT-Block hinzufügen und einfach einen ToString () ausführen.

J.
quelle
8

Das habe ich von einem Blog von Bill Gates bekommen. Ich muss es in meinem Browserverlauf finden und gebe Ihnen den Link.

Der Javascript-Code, um dasselbe zu tun (wie angefordert):

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

Grundsätzlich arbeiten Sie in Sekunden ...

Buhake Sindi
quelle
6

Ich denke, es gibt bereits eine Reihe von Antworten zu diesem Beitrag, aber man kann diese verwenden, die einfach wie das Plugin zu verwenden und auch für Programmierer leicht lesbar ist. Senden Sie Ihr bestimmtes Datum und erhalten Sie den Wert in Zeichenfolgenform:

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;
}
Prashant Gupta
quelle
5
/** 
 * {@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";
    }
}
Wai Ho Leung
quelle
5

Wenn Sie eine Ausgabe wie haben möchten "2 days, 4 hours and 12 minutes ago", benötigen Sie eine Zeitspanne:

TimeSpan timeDiff = DateTime.Now-CreatedDate;

Dann können Sie auf die gewünschten Werte zugreifen:

timeDiff.Days
timeDiff.Hours

usw...

Bgl86
quelle
4

Ich würde hierfür einige praktische Erweiterungsmethoden bereitstellen und den Code lesbarer machen. Zunächst einige Erweiterungsmethoden für 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;
    }
}

Dann eine für DateTime.

public static class DateTimeExtensions {

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

        return dateTime - delta;
    }
}

Jetzt können Sie Folgendes tun:

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
Tugberk
quelle