Calcular el tiempo relativo en C#

Resuelto Jeff Atwood asked hace 16 años • 42 respuestas

Dado un DateTimevalor específico, ¿cómo muestro el tiempo relativo, como:

  • 2 hours ago
  • 3 days ago
  • a month ago
Jeff Atwood avatar Aug 01 '08 06:08 Jeff Atwood
Aceptado

Jeff, tu código es bueno pero podría ser más claro con constantes (como se sugiere en Código completo).

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 avatar Aug 04 '2008 13:08 Vincent Robert

complemento jquery.timeago

Jeff, debido a que Stack Overflow usa jQuery ampliamente, recomiendo el complemento jquery.timeago .

Beneficios:

  • Evite las marcas de tiempo con fecha "hace 1 minuto" aunque la página se haya abierto hace 10 minutos; timeago se actualiza automáticamente.
  • Puede aprovechar al máximo el almacenamiento en caché de páginas y/o fragmentos en sus aplicaciones web, porque las marcas de tiempo no se calculan en el servidor.
  • Puedes usar microformatos como los chicos geniales.

Simplemente adjúntelo a sus marcas de tiempo en DOM listo:

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

Esto convertirá todos los abbrelementos con una clase de timeago y una marca de tiempo ISO 8601 en el título:

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

en algo como esto:

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

que rinde: hace 4 meses. A medida que pasa el tiempo, las marcas de tiempo se actualizarán automáticamente.

Descargo de responsabilidad: escribí este complemento, por lo que soy parcial.

Ryan McGeary avatar Sep 21 '2008 16:09 Ryan McGeary

Así es como lo hago

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

¿Sugerencias? ¿Comentarios? ¿Formas de mejorar este algoritmo?

Jeff Atwood avatar Jul 31 '2008 23:07 Jeff Atwood
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 "";
}

Prefiero esta versión por su concisión y su capacidad de agregar nuevos puntos de marca. Esto podría resumirse con una Latest()extensión de Timespan en lugar de esa larga línea 1, pero en aras de la brevedad en la publicación, esto servirá. Esto soluciona el problema de hace una hora, hace 1 hora, proporcionando una hora hasta que hayan transcurrido 2 horas.

DevelopingChris avatar Aug 14 '2008 05:08 DevelopingChris