C # 'daki bağıl süreyi hesapla


1513

Belirli bir DateTimedeğer verildiğinde , aşağıdaki gibi göreli zamanı nasıl görüntüleyebilirim:

  • 2 saat önce
  • 3 gün önce
  • bir ay önce

80
Bundan sonra geleceğe göreceli bir zaman hesaplamak isterseniz ne olur?
Jhonny D. Cano -Yazılım-

2
moment.js çok güzel bir tarih ayrıştırma kütüphanesidir .. İhtiyaçlarınıza bağlı olarak (sunucu tarafı veya istemci tarafı) kullanmayı düşünebilirsiniz. sadece fyi çünkü kimse burada bahsetmedi
Matej

1
Sorulan şeyi hemen hemen yapan .net paketi github.com/NickStrupat/TimeAgo var.
Rossco

Bu proje tarih biçimlendirme için oldukça kaygan github.com/Humanizr/Humanizer#humanize-datetime
Aaron Hudon

Yanıtlar:


988

Jeff, kodun güzel ama sabitlerle daha açık olabilir (Kod Tamamlama'da önerildiği gibi).

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

220
Bu sabitlerden bir tutkuyla nefret ediyorum. Bu kimse için yanlış mı görünüyor? Thread.Sleep(1 * MINUTE)? Çünkü bu 1000 katına göre yanlış.
Roman Starkov

31
const int SECOND = 1;Çok garip bir saniye bir saniyedir.
seriousdev

62
Bu tür kodların yerini belirlemek neredeyse imkansızdır. Uygulamanızın yalnızca İngilizce kalması gerekiyorsa, sorun değil. Ancak diğer dillere atlarsanız, böyle bir mantık yaptığınız için kendinizden nefret edersiniz. Hepiniz biliyorsunuz ...
Nik Reiman

73
Eğer sabitler, içlerindeki değeri doğru bir şekilde tanımlayacak şekilde yeniden adlandırılsaydı, anlaşılması daha kolay olurdu. Yani SecondsPerMinute = 60; DakikaPerHour = 60; SecondsPerHour = DakikaPerHour * SecondsPerHour; MINUTE = 60 olarak adlandırmak, okuyucunun değerin ne olduğunu belirlemesine izin vermez.
slolife

14
Neden kimse (Joe hariç) yanlış 'Dün' veya 'gün önce' değerini umursamıyor ??? Dün bir saatlik hesaplama değil, bir günlük hesaplamadır. Yani evet, bu en az iki sıklıkta yanlış bir koddur.
CtrlX

363

jquery.timeago eklentisi

Jeff, Stack Overflow yaygın olarak jQuery kullandığından, jquery.timeago eklentisini öneririm .

Yararları:

  • Sayfa 10 dakika önce açılmış olmasına rağmen "1 dakika önce" tarihli zaman damgalarından kaçının; timeago otomatik olarak yenilenir.
  • Zaman damgaları sunucuda hesaplanmadığından, web uygulamalarınızda sayfa ve / veya parça önbelleğe almanın tüm avantajlarından yararlanabilirsiniz.
  • Havalı çocuklar gibi mikro biçimler kullanabilirsiniz.

DOM hazırlığındaki zaman damgalarınıza eklemeniz yeterlidir:

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

Bu , başlığında abbrbir timeago sınıfı ve ISO 8601 zaman damgası olan tüm öğeleri döndürür:

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

böyle bir şeye:

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

bu da: 4 ay önce. Zaman geçtikçe zaman damgaları otomatik olarak güncellenir.

Yasal Uyarı: Bu eklentiyi yazdım, bu yüzden önyargılıyım.


39
Seb, Javascript'i devre dışı bıraktıysanız, başlangıçta abbr etiketleri arasına koyduğunuz dize görüntülenir. Genellikle, bu yalnızca istediğiniz herhangi bir biçimde bir tarih veya saattir. Timeago incelikle bozulur. Çok daha basit olamaz.
Ryan McGeary

23
Ryan, SO'ya bir süre önce timeago kullanmasını önerdim. Jeff'in yanıtı beni ağlattı, oturmanı öneririm: stackoverflow.uservoice.com/pages/1722-general/suggestions/…
Rob Fonseca-Ensor

7
Heh, teşekkürler Rob. Sorun yok. SO sayfalarında çok fazla zaman damgası olmasına rağmen, özellikle geçiş sırasında yalnızca bir sayı değiştiğinde zar zor fark edilir. Otomatik güncellemelerden kaçınmayı seçse bile, en azından sayfa önbelleğe almanın faydalarını takdir edeceğini düşünürdüm. Eminim Jeff eklentiyi geliştirmek için geri bildirimde bulunmuş olabilir. Arstechnica.com gibi siteleri bilerek teselli alıyorum .
Ryan McGeary

19
@Rob Fonseca-Ensor - şimdi beni de ağlatıyor. Nasıl Dakikada, doğru bilgi göstermek için bir kez bir güncelleme olduğu herhangi bir şekilde bir saniyenin kez yanıp metniyle ilgili?
Daniel Earwicker

25
Soru C # ile ilgili, nasıl bir jQuery eklentisi alakalı göremiyorum.
BartoszKP

331

İşte nasıl yaparım

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

Öneriler? Yorumlar? Bu algoritmayı geliştirmenin yolları?


112
"<48 * 60 * 60s", "dün" için oldukça alışılmadık bir tanımdır. Çarşamba günü 09:00 ise, gerçekten Pazartesi günü 9: 01'i "dün" olarak düşünür müsünüz? Dün veya "n gün önce" için bir algoritmanın gece yarısından önce / sonra düşünmesi gerektiğini düşünürdüm.
Joe

139
Derleyiciler genellikle 24 * 60 * 60 gibi sabit ifadeleri önceden hesaplamada oldukça iyidir, bu yüzden 86400 olarak kendiniz hesaplamak ve orijinal ifadeyi yorumlara koymak yerine doğrudan kullanabilirsiniz
zvolkov

11
@bzlm Sanırım üzerinde çalıştığım bir proje için yaptım. Buradaki motivasyonum, haftaların bu kod örneğinden çıkarıldığı konusunda başkalarını uyarmaktı. Bunu nasıl yapacağım konusunda, benim için oldukça basit görünüyordu.
jray

9
Algoritmayı geliştirmenin iyi bir yolunun, doğruluğu artırmak için "2 ay 21 gün önce", "1 saat 40 dakika önce" gibi 2 birim görüntülediğini düşünüyorum.
Evgeny Levin

5
@ Jeffy, artık yıl ve ilgili kontroller için hesaplama kaçırdınız
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 "";
}

Özlü olması ve yeni kene noktaları ekleme yeteneği için bu sürümü tercih ediyorum. Bu Latest(), o uzun 1 astar yerine Timespan'ın bir uzantısıyla kapsüllenebilir , ancak kayıttaki kısalık uğruna bunu yapacak. Bu, bir saat önce, 1 saat önce, 2 saat geçene kadar bir saat sağlayarak düzeltir


Örneğin, 'theDate = DateTime.Now.AddMinutes (-40);' '40 saat önce 'alıyorum, ama Michael'ın refactormycode yanıtıyla, '40 dakika önce' doğru geri dönüyor?
GONeale

Ben bir sıfır eksik olduğunu düşünüyorum, deneyin: uzun beri = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
robnardo

8
Hmm, bu kod çalışabilirken, Sözlük'teki anahtarların sırasının belirli bir sırada olacağını varsaymak yanlış ve geçersizdir. Sözlük, uzun değil, int! Döndüren Object.GetHashCode () kullanır. Bunların sıralanmasını istiyorsanız bir SortedList <long, string> kullanmalısınız. / İf./ else if / else kümesinde değerlendirilen eşiklerle ilgili sorun nedir? Aynı sayıda karşılaştırma alırsınız. FYI hash uzun süre.MaxValue int.MinValue ile aynı olduğu ortaya çıkıyor!
CodeMonkeyKing

OP t.Günleri> 30 mu? t.Gün / 30:
Lars Holm Jensen

@CodeMonkeyKing tarafından belirtilen sorunu gidermek için SortedDictionarydüz yerine kullanabilirsiniz Dictionary: Kullanım aynıdır, ancak anahtarların sıralanmasını sağlar. Ancak bu durumda bile algoritma, çünkü kusuru vardır RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))getiri "95 ay önce" yanlış olan kullandığınız sözlük türü ne olursa olsun, (o bağlı "4 ay önce" "3 ay önce" iade veya hangi eşik' on kullanıyoruz) - -3 geçen yıl bir tarih oluşturmasa bile (bunu Aralık ayında test ettim, bu durumda olmamalı).
Matt

71

PHP için Jeffs Script'ten bir yeniden yazma:

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

7
Soru C # etiketli Neden PHP kodu ?
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 sürümü:

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

Bu çok güzel IMO :) Bu aynı zamanda bir uzatma yöntemi olarak yeniden düzenlenebilir? Sözlük durağan hale gelebilir mi, bu yüzden sadece bir kez yaratılabilir ve bundan sonra referans verilebilir mi?
Pure.Krome


5
Büyük olasılıkla sözlüğü ve GC karmaşasını azaltmak için sözlüğü sözlüğe bir alandan çıkarmak isteyebilirsiniz. Sen değiştirmek zorunda kalacak Func<string>için Func<double>.
Drew Noakes

49

DateTime sınıfına hem gelecekteki hem de geçmiş tarihleri ​​işleyen ve aradığınız ayrıntı düzeyini ("3 saat önce" vs "3 saat, 23 dakika, 12 saniye önce "):

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

38

İstemci tarafında da bu bilgi işlem tavsiye ederim. Sunucu için daha az iş.

Aşağıdaki kullandığım sürüm (Zach Leatherman'dan)

/*
 * 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);
                });
        };
}

4
Soru C # etiketli Neden Javascript kodu ?
Kiquenet

36

Nuget'te Humanizr adlı bir paket de var ve gerçekten çok iyi çalışıyor ve .NET Foundation'da.

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'ın blogunda bir yazı var


3
dost not: .net 4.5 veya üzeri tam Humanizer yüklemeyin ... sadece Humanizer yükleyin. bunun bir parçası .. çünkü bu sürümde diğer dil paketleri desteklenmez
Ahmad

Çok kullanışlı! Bu cevap bu listede çok daha yüksek olmalıdır. 100 oy alsaydım, buna verirdim. Görünüşe göre (JS-land'dan geliyor), bu paketi aramak kolay değildi.
kumarharsh

29

@jeff

IMHO sizinki biraz uzun görünüyor. Ancak, "dün" ve "yıllar" desteğiyle biraz daha sağlam görünüyor. Ancak benim deneyimime göre, bu kişi ilk 30 gün içinde içeriği görüntüleyebilir. Bundan sonra gelen sadece gerçekten hardcore insanlar. Bu yüzden genellikle bunu kısa ve basit tutmayı tercih ederim.

Şu anda web sitelerimden birinde kullandığım yöntem bu. Bu yalnızca göreceli bir gün, saat, zaman döndürür. Ve sonra kullanıcı çıktı "tokat" tokatlamak zorunda.

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

24

Partiye birkaç yıl geç kaldı, ama bunu hem geçmiş hem de gelecek tarihler için yapma zorunluluğum vardı, bu yüzden Jeff ve Vincent'ları birleştirdim . Üçlü bir ekstravaganza! :)

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

21

Java ile bunu yapmanın kolay bir yolu var mı? java.util.DateSınıf yerine sınırlı görünüyor.

İşte benim hızlı ve kirli Java çözümüm:

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

1
Soru C # etiketli Neden Java kodu ?
Kiquenet

20

iPhone Objective-C Sürümü

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

19

Dünya ve kocası kod örnekleri yayınlıyor gibi göz önüne alındığında, işte bir süre önce bu cevapların birkaçına dayanarak yazdıklarım.

Bu kodun yerelleştirilebilmesi için özel bir gereksinimim vardı. Böylece Grammar, yerelleştirilebilir terimleri belirten ve FuzzyDateExtensionsbir grup uzatma yöntemini içeren iki sınıfım var . Gelecek zamanlarla uğraşmak zorunda kalmadım, bu yüzden bu kodla başa çıkmak için hiçbir girişimde bulunulmadı.

XMLdoc'ın bir kısmını kaynakta bıraktım, ancak kısaca (açık oldukları yerde) kısaca kaldı. Ayrıca burada her sınıf üyesini dahil etmedim:

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

FuzzyDateStringSınıf içerir:

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

Bu yüzden elde etmek istediği önemli şeylerden biri, hem de yerelleştirme gibi, o "bugün" olur sadece ortalama "Bu takvimi gün" olduğunu IsToday, IsThisMonth,IsThisYear yöntemler şu şekilde görünür:

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

ve yuvarlama yöntemleri şöyle ( RoundedMonthsbiraz farklı olduğu için ekledim):

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

Umarım insanlar bunu faydalı ve / veya ilginç bulur: o)


17

PHP, ben bu şekilde yaparım:

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

} ?>

5
Soru C # etiketlidir . Neden bu PHP kodu ? IMHO, sadece C # kodunu uygular
Kiquenet

17

Akıcı DateTime kullanma

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

14

Sınıfları ve polimorfizmi kullanarak bunu deneyeceğim diye düşündüm. Çok fazla ek yüke sahip olan alt sınıflandırma kullanılan önceki bir yineleme vardı. Daha esnek bir delege / genel mülkiyet nesnesi modeline geçtim. Kodum biraz daha doğru, keşke "ay önce" oluşturmak için daha iyi bir yol ile gelebilir keşke çok fazla mühendislik değildi.

Bence Jeff'in if-then kaskadına sadık kalacağım çünkü daha az kod ve daha basit (beklendiği gibi çalışmasını sağlamak kesinlikle daha kolay).

Aşağıdaki kod için PrintRelativeTime.GetRelativeTimeMessage (TimeSpan ago) göreceli zaman mesajını döndürür (örn. "Dün").

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

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

Bu soruya verilen başka bir cevapla aynı, ancak statik sözlüğe sahip bir uzatma yöntemi ile aynı.


Sözlük sizi burada ne satın alıyor?
StriplingWarrior

StriplingWarrior: Bir switch ifadesine veya if / else ifadelerinin yığınına kıyasla okuma ve değiştirme kolaylığı. Sözlük statik olmak, ToRelativeDate'i her kullanmak istediğimizde onun ve Func <,> nesnelerinin oluşturulması gerekmediği anlamına gelir; cevabımda bağladığım ile karşılaştırıldığında sadece bir kez yaratıldı.
Chris Charabaruk

Anlıyorum. Ben sadece düşündüm, çünkü Dictionary"Öğelerin iade sırası tanımsız", " msdn.microsoft.com/en-us/library/xfhwa508.aspx ) belki de kullanmak için en iyi veri yapısı değildir Arama sürelerini umursamadığınızda, şeylerin düzenli kalmasını sağlayın.
StriplingWarrior

StriplingWarrior: Sanırım LINQ, Dictionarys ile birlikte kullanıldığında bunu dikkate alıyor . Hala rahatsızsanız, kullanabilirsiniz SortedDictionary, ancak kendi deneyimim bunun gereksiz olduğunu gösteriyor.
Chris Charabaruk

12

İzleyicinin saat dilimini bildiğinizde, takvim günlerini gün ölçeğinde kullanmak daha açık olabilir. Ne yazık ki C # bunu nasıl yapacağımı bilmiyorum bu yüzden .NET kitaplıkları ile aşina değilim.

Tüketici sitelerinde, bir dakikadan az bir sürede elle de olabilirsiniz. "Bir dakikadan daha kısa bir süre önce" veya "hemen şimdi" yeterince iyi olabilir.


11

bunu deneyebilirsin.

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

9

İstemci tarafı gwt kullanımı için Java:

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

}

Soru C # etiketlidir . Neden bu Java kodu ? IMHO, sadece C # kodunu uygular
Kiquenet

9

@Jeff

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

Çıkarma yapmak yine de DateTimedöndürür TimeSpan.

Böylece sadece

(DateTime.UtcNow - dt).TotalSeconds

Ayrıca, sabitlerin elle çarpıldığını ve sonra da çarpmalarla eklenen yorumları gördüğüme şaşırdım. Bu yanlış yönlendirilmiş bir optimizasyon muydu?


8

İşte stackoverflow algoritması kullanır ama bir hata düzeltmesi ile perlish pseudocode daha kısaca yeniden yazılmıştır ("bir saat önce"). İşlev birkaç saniye önce (pozitif) sürer ve "3 saat önce" veya "dün" gibi insan dostu bir dize döndürür.

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

Aşağıdakine benzeyen TimeAgo uzantısını kullanabilirsiniz :

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

Veya Timeago'dan Razor uzantılı jQuery eklentisini kullanın .


8

Bu mantık istemci tarafını gerçekleştirerek sunucu tarafı yükünü azaltabilirsiniz. Referans için bazı Digg sayfalarındaki kaynağı görüntüleyin. Onlar sunucu Javascript tarafından işlenir bir dönem zaman değeri yayarlar var. Bu şekilde son kullanıcının saat dilimini yönetmeniz gerekmez. Yeni sunucu tarafı kodu şuna benzer:

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

Hatta bir NOSCRIPT bloğu ekleyebilir ve sadece bir ToString () gerçekleştirebilirsiniz.


8

Bu, Bill Gates'in blog'undan birinden aldım. Tarayıcı geçmişimde bulmam gerekiyor ve size bağlantıyı vereceğim.

Aynı şeyi yapmak için Javascript kodu (istendiği gibi):

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

Temel olarak, saniyeler içinde çalışıyorsunuz ...


6

Ben zaten bu yazı ile ilgili bir takım cevaplar olduğunu düşünüyorum, ama bir eklenti gibi kullanımı kolay ve aynı zamanda programcılar için kolayca okunabilir olan bu kullanabilirsiniz. Belirli bir tarihinizi gönderin ve değerini dize biçiminde alın:

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

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

5

Gibi bir çıktıya sahip olmak istiyorsanız "2 days, 4 hours and 12 minutes ago", bir zaman aralığına ihtiyacınız vardır:

TimeSpan timeDiff = DateTime.Now-CreatedDate;

Ardından istediğiniz değerlere erişebilirsiniz:

timeDiff.Days
timeDiff.Hours

vb...


4

Bunun için bazı kullanışlı uzantı yöntemleri sağlar ve kodu daha okunabilir hale getiririm. İlk olarak, birkaç uzatma yöntemi 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;
    }
}

Sonra, biri için DateTime.

public static class DateTimeExtensions {

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

        return dateTime - delta;
    }
}

Şimdi, aşağıdaki gibi bir şey yapabilirsiniz:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.