1654 Stimmen

Berechnung der relativen Zeit in C#

Angesichts einer bestimmten DateTime Wert, wie zeige ich die relative Zeit an, etwa:

  • vor 2 Stunden
  • vor 3 Tagen
  • vor einem Monat

87 Stimmen

Was ist, wenn Sie eine relative Zeit von jetzt bis in die Zukunft berechnen wollen?

3 Stimmen

Moment.js ist eine sehr schöne Datums-Parsing-Bibliothek. Sie können in Betracht ziehen, die Verwendung (Server-Seite oder Client-Seite), je nach Ihren Bedürfnissen. nur fyi, weil niemand es hier erwähnt

6 Stimmen

Dieses Projekt ist für die Datumsformatierung ziemlich raffiniert github.com/Humanizr/Humanizer#humanize-datetime

1070voto

Vincent Robert Punkte 34478

Jeff, Ihr 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";
}

231 Stimmen

Ich hasse solche Konstanten mit Leidenschaft. Sieht das für jemanden falsch aus? Thread.Sleep(1 * MINUTE) ? Weil es um den Faktor 1000 falsch ist.

0 Stimmen

Hat jemand eine Anleitung, wie dies mit einer benutzerdefinierten Formatzeichenfolge implementiert werden könnte, so dass `string.Format(mydate, "dd/MM/yyyy (YTT)") == "26/04/2011 (heute)"?

39 Stimmen

const int SECOND = 1; So seltsam eine Sekunde ist eine Sekunde.

372voto

Ryan McGeary Punkte 228632

jquery.timeago-Plugin

Jeff, da Stack Overflow jQuery ausgiebig nutzt, empfehle ich die jquery.timeago-Plugin .

Vorteile:

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

Fügen Sie sie einfach an Ihre Zeitstempel auf DOM bereit:

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

Dadurch werden alle abbr Elemente mit einer Klasse von timeago und einer ISO 8601 Zeitstempel im Titel:

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

zu etwas wie diesem:

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

was zu folgenden Ergebnissen führt: Vor 4 Monaten. Wenn die Zeit vergeht, werden die Zeitstempel automatisch aktualisiert.

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

0 Stimmen

Und was ist, wenn der Benutzer JavaScript deaktiviert hat? jQuery (und JavaScritp im Allgemeinen) sollte nur verwendet werden, um die Erfahrung auf der Client-Seite zu verbessern, aber Ihre Anwendungsfunktionen sollten nicht von Client-Technologien abhängen. Sie können dies auch ohne JavaScript erreichen.

39 Stimmen

Seb, Wenn Sie Javascript deaktiviert haben, wird die Zeichenkette angezeigt, die Sie ursprünglich zwischen die abbr-Tags gesetzt haben. Normalerweise ist dies nur ein Datum oder eine Uhrzeit in einem beliebigen Format. Timeago wird anständig abgebaut. Viel einfacher geht es nicht.

23 Stimmen

Ryan, ich habe SO vor einiger Zeit vorgeschlagen, timeago zu verwenden. Jeffs Antwort hat mich zum Weinen gebracht, ich schlage vor, du setzt dich hin: stackoverflow.uservoice.com/pages/1722-general/suggestions/

348voto

Jeff Atwood Punkte 62123

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 < 60 * 2)
{
  return "a minute ago";
}
if (delta < 45 * 60)
{
  return ts.Minutes + " minutes ago";
}
if (delta < 90 * 60)
{
  return "an hour ago";
}
if (delta < 24 * 60 * 60)
{
  return ts.Hours + " hours ago";
}
if (delta < 48 * 60 * 60)
{
  return "yesterday";
}
if (delta < 30 * 24 * 60 * 60)
{
  return ts.Days + " days ago";
}
if (delta < 12 * 30 * 24 * 60 * 60)
{
  int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
  return months <= 1 ? "one month ago" : months + " months ago";
}
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
return years <= 1 ? "one year ago" : years + " years ago";

Vorschläge? Kommentare? Wie kann man diesen Algorithmus verbessern?

114 Stimmen

"< 48*60*60s" ist eine recht unkonventionelle Definition für "gestern". Wenn es am Mittwoch 9 Uhr morgens ist, würden Sie dann wirklich 9:01 Uhr am Montag als "gestern" betrachten. Ich hätte gedacht, dass ein Algorithmus für "gestern" oder "vor n Tagen" vor/nach Mitternacht berücksichtigen sollte.

141 Stimmen

Compiler sind in der Regel recht gut darin, konstante Ausdrücke wie 24 * 60 * 60 vorzuberechnen, so dass Sie diese direkt verwenden können, anstatt selbst 86400 zu berechnen und den ursprünglichen Ausdruck in die Kommentare einzufügen

11 Stimmen

@bzlm Ich glaube, das habe ich für ein Projekt getan, an dem ich gearbeitet habe. Meine Motivation war es, andere darauf aufmerksam zu machen, dass in diesem Codebeispiel Wochen ausgelassen wurden. Wie man das macht, schien mir ziemlich einfach zu sein.

100voto

DevelopingChris Punkte 38437
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 Möglichkeit, neue Tickpunkte hinzuzufügen. Dies könnte mit einer Latest() Erweiterung von Timespan anstelle dieses langen Einzeilers, aber um der Kürze willen wird dies genügen. Dies behebt das "vor einer Stunde", "vor 1 Stunde", indem eine Stunde bereitgestellt wird, bis 2 Stunden verstrichen sind

0 Stimmen

Ich bin immer alle Arten von Problemen mit dieser Funktion, zum Beispiel, wenn Sie mock 'theDate = DateTime.Now.AddMinutes(-40);' Ich erhalte '40 Stunden vor', aber mit Michaels refactormycode Antwort, es gibt korrekt auf '40 Minuten vor'?

0 Stimmen

Ich glaube, Ihnen fehlt eine Null, versuchen Sie: long since = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;

9 Stimmen

Dieser Code mag zwar funktionieren, aber es ist falsch und ungültig anzunehmen, dass die Reihenfolge der Schlüssel im Wörterbuch in einer bestimmten Reihenfolge sein wird. Das Dictionary verwendet die Funktion Object.GetHashCode(), die keinen long, sondern einen int! zurückgibt. Wenn Sie wollen, dass diese sortiert sind, sollten Sie eine SortedList<long, string> verwenden. Was ist falsch daran, wenn 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 long.MaxValue ist der gleiche wie der für int.MinValue!

76voto

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

0 Stimmen

Das ist sehr schön IMO :) Dies könnte auch als eine Erweiterungsmethode überarbeitet werden? könnte das Wörterbuch statisch werden, so dass es nur einmal erstellt und von da an referenziert wird?

0 Stimmen

5 Stimmen

Sie würden wahrscheinlich wollen, dass Wörterbuch in ein Feld zu ziehen, so dass Sie Instanziierung und GC Churn reduzieren. Sie müssen Folgendes ändern Func<string> a Func<double> .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X