Hitung waktu relatif dalam C #

1514

Diberi DateTimenilai tertentu , bagaimana cara menampilkan waktu relatif, seperti:

  • 2 jam lalu
  • 3 hari yang lalu
  • sebulan yang lalu
Jeff Atwood
sumber
80
Bagaimana jika Anda ingin menghitung waktu relatif dari sekarang ke Masa Depan?
Jhonny D. Cano -Leftware-
2
moment.js adalah parsing library tanggal yang sangat bagus .. Anda dapat mempertimbangkan untuk menggunakannya (sisi server atau sisi klien), tergantung pada kebutuhan Anda. hanya fyi karena tidak ada yang menyebutkannya di sini
Matej
1
Ada paket .net github.com/NickStrupat/TimeAgo yang cukup banyak melakukan apa yang diminta.
Rossco
Proyek ini cukup apik untuk memformat tanggal github.com/Humanizr/Humanizer#humanize-datetime
Aaron Hudon

Jawaban:

988

Jeff, kode Anda bagus tetapi bisa lebih jelas dengan konstanta (seperti yang disarankan dalam Kode Lengkap).

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
sumber
220
Aku benci konstanta seperti itu dengan hasrat. Apakah ini terlihat salah bagi siapa pun? Thread.Sleep(1 * MINUTE)? Karena itu salah dengan faktor 1000.
Roman Starkov
31
const int SECOND = 1;Jadi aneh satu detik adalah satu detik.
seriousdev
62
Jenis kode ini hampir tidak mungkin dilokalisasi. Jika aplikasi Anda hanya perlu tetap dalam bahasa Inggris, maka baiklah. Tetapi jika Anda melompat ke bahasa lain, Anda akan membenci diri sendiri karena melakukan logika seperti ini. Asal tahu saja ...
Nik Reiman
73
Saya pikir jika konstanta diganti untuk menggambarkan secara akurat nilai yang ada di dalamnya, akan lebih mudah untuk dipahami. Jadi SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour; dll. Hanya menyebutnya MINUTE = 60 tidak memungkinkan pembaca menentukan nilainya.
slolife
14
Mengapa tidak ada orang (kecuali Joe) yang peduli dengan nilai 'Kemarin' atau 'hari yang lalu' yang salah ??? Kemarin bukan perhitungan jam, tapi perhitungan hari ke hari. Jadi ya, ini adalah kode yang salah setidaknya dalam dua kasus yang sering.
CtrlX
363

plugin jquery.timeago

Jeff, karena Stack Overflow menggunakan jQuery secara ekstensif, saya merekomendasikan plugin jquery.timeago .

Manfaat:

  • Hindari cap waktu bertanggal "1 menit yang lalu" meskipun halaman dibuka 10 menit yang lalu; timeago menyegarkan secara otomatis.
  • Anda dapat memanfaatkan sepenuhnya cache halaman dan / atau fragmen di aplikasi web Anda, karena cap waktu tidak dihitung di server.
  • Anda bisa menggunakan Microformats seperti anak-anak keren.

Cukup lampirkan ke stempel waktu di DOM ready:

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

Ini akan mengubah semua abbrelemen dengan kelas timeago dan cap waktu ISO 8601 pada judul:

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

menjadi sesuatu seperti ini:

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

yang menghasilkan: 4 bulan lalu. Seiring waktu berlalu, cap waktu akan secara otomatis diperbarui.

Penafian: Saya menulis plugin ini, jadi saya bias.

Ryan McGeary
sumber
39
Seb, Jika Anda menonaktifkan Javascript, maka string yang semula Anda masukkan di antara tag abbr ditampilkan. Biasanya, ini hanya tanggal atau waktu dalam format apa pun yang Anda inginkan. Timeago terdegradasi dengan anggun. Itu tidak menjadi jauh lebih sederhana.
Ryan McGeary
23
Ryan, saya menyarankan agar SO menggunakan timeago beberapa waktu yang lalu. Tanggapan Jeff membuat saya menangis, saya sarankan Anda duduk: stackoverflow.uservoice.com/pages/1722-general/suggestions/…
Rob Fonseca-Ensor
7
Heh, terima kasih Rob. Tidak apa-apa. Ini hampir tidak terlihat, terutama ketika hanya satu perubahan nomor selama transisi, meskipun halaman SO memiliki banyak cap waktu. Saya akan berpikir dia akan setidaknya menghargai manfaat dari caching halaman, meskipun ia memilih untuk menghindari pembaruan otomatis. Saya yakin Jeff bisa memberikan umpan balik untuk meningkatkan plugin juga. Saya senang mengetahui situs seperti arstechnica.com menggunakannya.
Ryan McGeary
19
@Rob Fonseca-Ensor - sekarang membuat saya menangis juga. Bagaimana cara pembaruan sekali per menit, untuk menampilkan informasi yang akurat, dengan cara apa pun yang terkait dengan teks yang berkedip sekali dalam satu detik?
Daniel Earwicker
25
Pertanyaannya adalah tentang C #, saya gagal melihat bagaimana plugin jQuery relevan.
BartoszKP
331

Begini cara saya melakukannya

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

Saran? Komentar? Cara meningkatkan algoritme ini?

Jeff Atwood
sumber
112
"<48 * 60 * 60s" adalah definisi yang agak tidak konvensional untuk "kemarin". Jika jam 9 pagi pada hari Rabu, apakah Anda benar-benar menganggap jam 9:01 pagi pada hari Senin sebagai "kemarin". Saya akan berpikir algoritma untuk kemarin atau "n hari yang lalu" harus dipertimbangkan sebelum / sesudah tengah malam.
Joe
139
Compiler biasanya cukup baik di ekspresi konstan pra-menghitung, seperti 24 * 60 * 60, sehingga Anda langsung dapat menggunakan mereka sebagai gantinya menghitung sendiri menjadi 86.400 dan menempatkan ekspresi asli dalam komentar
zvolkov
11
@ bzlm Saya pikir saya lakukan untuk proyek yang sedang saya kerjakan. Motivasi saya di sini adalah untuk mengingatkan orang lain bahwa minggu dihilangkan dari contoh kode ini. Mengenai cara melakukan itu, bagiku itu tampak cukup lurus.
jray
9
Saya pikir cara yang baik untuk meningkatkan algoritma adalah menampilkan 2 unit seperti "2 bulan 21 hari yang lalu", "1 jam 40 menit yang lalu" untuk meningkatkan akurasi.
Evgeny Levin
5
@ Jeffy, Anda melewatkan perhitungan untuk tahun kabisat dan cek terkait
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 "";
}

Saya lebih suka versi ini karena keringkasannya, dan kemampuan untuk menambahkan titik centang baru. Ini bisa dienkapsulasi dengan Latest()ekstensi ke Timespan alih-alih 1 liner yang panjang itu, tetapi demi singkatnya dalam posting, ini bisa dilakukan. Ini memperbaiki satu jam yang lalu, 1 jam yang lalu, dengan menyediakan satu jam sampai 2 jam telah berlalu

DevelopingChris
sumber
Saya mendapatkan segala macam masalah menggunakan fungsi ini, misalnya jika Anda mengejek 'theDate = DateTime.Now.AddMinutes (-40);' Saya mendapatkan '40 jam yang lalu ', tetapi dengan respons refactormycode Michael, hasilnya benar pada '40 menit yang lalu'?
GONeale
saya pikir Anda kehilangan nol, coba: lama sejak = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
robnardo
8
Hmm, sementara kode ini bisa berfungsi, itu tidak benar dan tidak valid untuk menganggap bahwa urutan tombol dalam Kamus akan berada dalam urutan tertentu. Kamus menggunakan Object.GetHashCode () yang tidak mengembalikan panjang tapi int !. Jika Anda ingin ini diurutkan maka Anda harus menggunakan SortedList <long, string>. Apa yang salah dengan ambang yang dievaluasi dalam satu set if / else jika /.../ else? Anda mendapatkan jumlah perbandingan yang sama. FYI hash untuk long.MaxValue ternyata sama dengan int.MinValue!
CodeMonkeyKing
OP lupa t. Hari> 30? t.Days / 30:
Lars Holm Jensen
Untuk memperbaiki masalah yang disebutkan oleh @CodeMonkeyKing, Anda bisa menggunakan SortedDictionaryalih - alih polos Dictionary: Penggunaannya sama, tetapi memastikan bahwa kunci diurutkan. Tetapi meskipun demikian, algoritmanya memiliki kekurangan, karena RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))mengembalikan "95 bulan yang lalu" , terlepas dari jenis kamus yang Anda gunakan, yang tidak benar (harus mengembalikan "3 bulan lalu" atau "4 bulan lalu" tergantung pada ambang mana Anda kembali menggunakan) - bahkan jika -3 tidak membuat tanggal dalam satu tahun terakhir (saya telah menguji ini pada bulan Desember, jadi dalam kasus ini seharusnya tidak terjadi).
Matt
71

Berikut penulisan ulang dari Jeffs Script untuk 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
sumber
7
Pertanyaannya adalah C # tagged Mengapa kode PHP ?
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

Versi C # 6:

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
sumber
ini IMO yang sangat bagus :) Ini juga bisa di refactored sebagai metode ekstensi? dapatkah kamus menjadi statis sehingga hanya dibuat sekali dan direferensikan sejak saat itu?
Pure.Krome
5
Anda mungkin ingin menarik kamus itu ke bidang sehingga Anda mengurangi instantiasi dan churn GC. Anda harus berubah Func<string>menjadi Func<double>.
Drew Noakes
49

Berikut ini adalah implementasi yang saya tambahkan sebagai metode ekstensi ke kelas DateTime yang menangani tanggal di masa depan dan masa lalu dan menyediakan opsi perkiraan yang memungkinkan Anda menentukan tingkat detail yang Anda cari ("3 jam yang lalu" vs "3 jam, 23 menit, 12 detik yang lalu "):

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
sumber
38

Saya akan merekomendasikan menghitung ini di sisi klien juga. Lebih sedikit pekerjaan untuk server.

Berikut ini adalah versi yang saya gunakan (dari 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
sumber
4
Pertanyaannya adalah C # tagged Mengapa kode Javascript ?
Kiquenet
36

Ada juga paket yang disebut Humanizr di Nuget, dan itu benar-benar berfungsi dengan baik, dan ada di .NET Foundation.

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 memiliki tulisan di blognya

Karl-Henrik
sumber
3
catatan ramah: Pada .net 4.5 atau di atas tidak menginstal Humanizer lengkap ... hanya instal Humanizer.Core bagian dari itu .. menyebabkan paket bahasa lain tidak didukung pada versi ini
Ahmad
Sangat bermanfaat! Jawaban ini harus jauh lebih tinggi dalam daftar ini. Jika saya memiliki 100 suara, saya akan memberikan ini. Ternyata (berasal dari JS-land), mencari paket ini tidak mudah.
kumarharsh
29

@ Jeff

IMHO Anda tampaknya agak panjang. Namun tampaknya sedikit lebih kuat dengan dukungan untuk "kemarin" dan "tahun". Tetapi dalam pengalaman saya ketika ini digunakan orang tersebut kemungkinan besar akan melihat konten dalam 30 hari pertama. Hanya orang-orang yang benar-benar hardcore yang datang setelah itu. Jadi itu sebabnya saya biasanya memilih untuk menjaga ini pendek dan sederhana.

Ini adalah metode yang saya gunakan saat ini di salah satu situs web saya. Ini hanya mengembalikan hari, jam, waktu relatif. Dan kemudian pengguna harus menampar "lalu" di output.

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
sumber
24

Beberapa tahun terlambat ke pesta, tetapi saya memiliki persyaratan untuk melakukan ini untuk tanggal masa lalu dan masa depan, jadi saya menggabungkan Jeff dan Vincent ke dalam ini. Ini ekstravaganza ternarytastic! :)

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";
            }
        }
    }
Town
sumber
21

Apakah ada cara mudah untuk melakukan ini di Jawa? The java.util.Datekelas tampaknya agak terbatas.

Inilah solusi Java cepat dan kotor saya:

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
sumber
1
Pertanyaannya adalah C # tagged Mengapa kode Java ?
Kiquenet
20

Versi iPhone Objective-C

+ (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
sumber
19

Mengingat dunia dan suaminya tampak memposting contoh kode, inilah yang saya tulis beberapa waktu lalu, berdasarkan pada beberapa jawaban ini.

Saya memiliki kebutuhan khusus agar kode ini dapat dilokalisasi. Jadi saya punya dua kelas - Grammar, yang menentukan istilah yang dapat di-lokalisasi, dan FuzzyDateExtensions, yang memiliki banyak metode ekstensi. Saya tidak perlu berurusan dengan datetimes masa depan, jadi tidak ada upaya yang dilakukan untuk menangani mereka dengan kode ini.

Saya telah meninggalkan beberapa XMLdoc di sumbernya, tetapi menghapus sebagian besar (di mana mereka akan jelas) demi singkatnya. Saya juga tidak menyertakan setiap anggota kelas di sini:

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

The FuzzyDateStringkelas berisi:

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

Salah satu hal kunci yang saya ingin mencapai, serta lokalisasi, adalah bahwa "hari ini" hanya akan berarti "hari kalender ini", sehingga IsToday, IsThisMonth, IsThisYearmetode terlihat seperti ini:

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

dan metode pembulatannya seperti ini (saya sudah sertakan RoundedMonths, karena itu agak berbeda):

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

Saya harap orang-orang menemukan ini berguna dan / atau menarik: o)

Owen Blacker
sumber
17

Dalam PHP, saya melakukannya dengan cara ini:

<?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
sumber
5
Pertanyaannya adalah tag C # . Kenapa kode PHP ini ? IMHO, hanya berlaku kode C #
Kiquenet
17

menggunakan 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
sumber
14

Saya pikir saya akan mencoba ini menggunakan kelas dan polimorfisme. Saya memiliki iterasi sebelumnya yang menggunakan sub-klasifikasi yang akhirnya memiliki terlalu banyak overhead. Saya telah beralih ke model objek properti delegasi / publik yang lebih fleksibel yang jauh lebih baik. Kode saya sedikit lebih akurat, saya berharap saya bisa menemukan cara yang lebih baik untuk menghasilkan "bulan lalu" yang sepertinya tidak terlalu direkayasa.

Saya pikir saya masih akan tetap dengan kasing Jeff jika-maka karena lebih sedikit kode dan lebih sederhana (itu pasti lebih mudah untuk memastikan itu akan berfungsi seperti yang diharapkan).

Untuk kode di bawah ini PrintRelativeTime.GetRelativeTimeMessage (TimeSpan lalu) mengembalikan pesan waktu relatif (misalnya "kemarin").

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

Sama seperti jawaban lain untuk pertanyaan ini tetapi sebagai metode ekstensi dengan kamus statis.

Chris Charabaruk
sumber
Apa yang dibeli kamus di sini?
StriplingWarrior
StriplingWarrior: Kemudahan membaca dan memodifikasi dibandingkan dengan pernyataan switch atau tumpukan pernyataan if / else. Kamus menjadi statis berarti bahwa itu dan objek Func <,> tidak harus dibuat setiap kali kita ingin menggunakan ToRelativeDate; itu dibuat hanya sekali, dibandingkan dengan yang saya tautkan dalam jawaban saya.
Chris Charabaruk
Saya melihat. Saya hanya berpikir, karena dokumentasi tentang Dictionarymenyatakan bahwa "Urutan pengembalian barang tidak ditentukan," ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) mungkin itu bukan struktur data terbaik untuk digunakan ketika Anda tidak peduli tentang waktu pencarian sebanyak memiliki hal-hal yang tetap tertib.
StriplingWarrior
StriplingWarrior: Saya percaya LINQ memperhitungkannya saat digunakan dengan Dictionarys. Jika Anda masih tidak nyaman dengan itu, Anda dapat menggunakannya SortedDictionary, tetapi pengalaman saya sendiri menunjukkan bahwa itu tidak perlu.
Chris Charabaruk
12

Saat Anda mengetahui zona waktu pemirsa, mungkin lebih jelas untuk menggunakan hari kalender pada skala hari. Saya tidak terbiasa dengan perpustakaan .NET jadi saya tidak tahu bagaimana Anda akan melakukannya di C #, sayangnya.

Di situs-situs konsumen, Anda juga bisa menjadi lebih mudah dalam satu menit. "Kurang dari satu menit yang lalu" atau "barusan" cukup baik.

markpasc
sumber
11

Anda dapat mencoba ini. Saya pikir itu akan berfungsi dengan benar.

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
sumber
9

Java untuk penggunaan gwt sisi klien:

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
sumber
Pertanyaannya adalah tag C # . Kenapa kode Java ini ? IMHO, hanya berlaku kode C #
Kiquenet
9

@ Jeff

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

Lagi pula, lakukan pengurangan pada DateTimepengembalian TimeSpan.

Jadi Anda bisa melakukannya

(DateTime.UtcNow - dt).TotalSeconds

Saya juga terkejut melihat konstanta dikalikan dengan tangan dan kemudian komentar ditambahkan dengan perkalian masuk. Apakah itu beberapa optimasi yang salah arah?

Will Dean
sumber
8

Inilah algoritma yang digunakan stackoverflow tetapi ditulis ulang dengan lebih ringkas dalam pseudocode perlish dengan perbaikan bug (tidak ada "satu jam yang lalu"). Fungsi ini mengambil (positif) beberapa detik yang lalu dan mengembalikan string ramah-manusia seperti "3 jam yang lalu" atau "kemarin".

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
sumber
8

Anda dapat menggunakan ekstensi TimeAgo yang terlihat seperti berikut:

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

Atau gunakan plugin jQuery dengan ekstensi Razor dari Timeago.

Piotr Stapp
sumber
8

Anda dapat mengurangi beban sisi server dengan melakukan logika sisi klien ini. Lihat sumber pada beberapa halaman Digg untuk referensi. Mereka memiliki server memancarkan nilai waktu yang diproses oleh Javascript. Dengan cara ini Anda tidak perlu mengelola zona waktu pengguna akhir. Kode sisi server baru akan seperti:

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

Anda bahkan dapat menambahkan blok NOSCRIPT di sana dan hanya menjalankan ToString ().

J
sumber
8

Ini, saya dapatkan dari salah satu blog Bill Gates. Saya perlu menemukannya di riwayat browser saya dan saya akan memberikan Anda tautannya.

Kode Javascript untuk melakukan hal yang sama (seperti yang diminta):

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

Pada dasarnya, Anda bekerja dalam hitungan detik ...

Buhake Sindi
sumber
6

Saya pikir sudah ada sejumlah jawaban yang terkait dengan posting ini, tetapi orang dapat menggunakan ini yang mudah digunakan seperti plugin dan juga mudah dibaca oleh programmer. Kirim tanggal spesifik Anda, dan dapatkan nilainya dalam bentuk string:

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
sumber
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
sumber
5

Jika Anda ingin memiliki keluaran seperti "2 days, 4 hours and 12 minutes ago", Anda memerlukan rentang waktu:

TimeSpan timeDiff = DateTime.Now-CreatedDate;

Kemudian Anda dapat mengakses nilai yang Anda sukai:

timeDiff.Days
timeDiff.Hours

dll ...

Bgl86
sumber
4

Saya akan memberikan beberapa metode ekstensi yang berguna untuk ini dan membuat kode lebih mudah dibaca. Pertama, beberapa metode ekstensi untuk 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;
    }
}

Lalu, satu untuk DateTime.

public static class DateTimeExtensions {

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

        return dateTime - delta;
    }
}

Sekarang, Anda dapat melakukan sesuatu seperti di bawah ini:

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
sumber