Angesichts einer bestimmten DateTime
Wert, wie zeige ich die relative Zeit an, etwa:
- vor 2 Stunden
- vor 3 Tagen
- vor einem Monat
Angesichts einer bestimmten DateTime
Wert, wie zeige ich die relative Zeit an, etwa:
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";
}
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.
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)"?
Jeff, da Stack Overflow jQuery ausgiebig nutzt, empfehle ich die jquery.timeago-Plugin .
Vorteile:
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.
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.
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.
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/
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?
"< 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.
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
@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.
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
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'?
Ich glaube, Ihnen fehlt eine Null, versuchen Sie: long since = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
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!
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;
}
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?
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.
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