C #에서 상대 시간 계산


1513

특정 DateTime값이 주어지면 다음 과 같이 상대 시간을 어떻게 표시합니까?

  • 2 시간 전
  • 3 일 전
  • 한 달 전

80
지금부터 미래까지의 상대 시간을 계산하려면 어떻게해야합니까?
Jhonny D. Cano -Leftware-

2
moment.js는 아주 좋은 날짜 파싱 라이브러리입니다. 필요에 따라 서버 쪽 또는 클라이언트 쪽을 사용하는 것을 고려할 수 있습니다. 아무도 언급하지 않았기 때문에 그냥 fyi
Matej

1
.net 패키지 github.com/NickStrupat/TimeAgo 가 있으며 요청되는 내용을 거의 수행합니다.
Rossco

이 프로젝트는 날짜 형식이 매우 매끄 럽습니다. github.com/Humanizr/Humanizer#humanize-datetime
Aaron Hudon

답변:


988

Jeff, 코드 는 훌륭하지만 상수로 더 명확 할 수 있습니다 (Code Complete에서 제안한대로).

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
나는 열정으로 그러한 상수를 싫어합니다. 누구에게나 잘못 보입니까? Thread.Sleep(1 * MINUTE)? 1000 배로 잘못 되었기 때문입니다.
Roman Starkov

31
const int SECOND = 1;그래서 1 초가 이상합니다.
seriousdev

62
이 유형의 코드는 현지화하기가 거의 불가능합니다. 앱이 영어로만 남아 있으면 괜찮습니다. 그러나 다른 언어로 넘어 가면 이와 같은 논리를 수행하는 것을 싫어합니다. 그냥 아시다시피 ...
Nik Reiman

73
상수의 이름을 정확하게 설명하기 위해 상수의 이름을 바꾸면 이해하기가 더 쉽다고 생각합니다. 따라서 SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour; MINUTE = 60이라고 부르는 것만으로는 독자가 그 값이 무엇인지 결정할 수 없습니다.
slolife

14
왜 '어제'나 '일 전'의 가치에 대해 아무도 신경을 쓰지 않는가? 어제는 시간 계산이 아니라 매일 계산합니다. 예, 이것은 적어도 두 번의 빈번한 경우에 잘못된 코드입니다.
CtrlX

363

jquery.timeago 플러그인

Jeff는 Stack Overflow가 jQuery를 광범위하게 사용하므로 jquery.timeago 플러그인을 권장합니다 .

혜택:

  • 10 분 전에 페이지를 열었더라도 "1 분 전"의 타임 스탬프를 피하십시오. timeago가 자동으로 새로 고침됩니다.
  • 타임 스탬프는 서버에서 계산되지 않으므로 웹 응용 프로그램에서 페이지 및 / 또는 조각 캐싱을 최대한 활용할 수 있습니다.
  • 멋진 아이들처럼 마이크로 포맷을 사용하게됩니다.

DOM에서 타임 스탬프에 첨부하면됩니다.

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

제목 abbr에 timeago 클래스와 ISO 8601 타임 스탬프가있는 모든 요소 가 바뀝니다 .

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

이런 식으로 :

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

4 개월 전입니다. 시간이 지남에 따라 타임 스탬프가 자동으로 업데이트됩니다.

면책 조항 : 나는이 플러그인을 작성 했으므로 편견이 있습니다.


39
Seb, Javascript가 비활성화되어 있으면 원래 abbr 태그 사이에 넣은 문자열이 표시됩니다. 일반적으로 이것은 원하는 형식의 날짜 또는 시간입니다. Timeago가 정상적으로 저하됩니다. 훨씬 간단하지 않습니다.
Ryan McGeary

23
Ryan, 나는 얼마 전에 timeago를 사용하라고 제안했다. Jeff의 응답으로 울부 짖었습니다. stackoverflow.uservoice.com/pages/1722-general/suggestions/…
Rob Fonseca-Ensor

7
고마워 Rob. 괜찮아요. SO 페이지에는 많은 타임 스탬프가 있지만 특히 전환 중에 하나의 숫자 만 변경되는 경우에는 거의 눈에 띄지 않습니다. 비록 그가 자동 업데이트를 피하도록 선택하더라도 페이지 캐싱의 이점을 적어도 감사했을 것이라고 생각했을 것입니다. Jeff가 플러그인 개선을 위해 피드백을 제공했을 것이라고 확신합니다. 나는 arstechnica.com 과 같은 사이트가 그것을 사용 한다는 것을 알고 위안을 느낍니다 .
Ryan McGeary 2009

19
@Rob Fonseca-Ensor-이제 나도 울고 있습니다. 초당 1 회 깜박임과 관련된 방식으로 정확한 정보를 표시하기 위해 분당 한 번 업데이트 하는 방법
Daniel Earwicker

25
질문은 C #에 관한 것입니다 .jQuery 플러그인이 어떻게 관련되는지 알 수 없습니다.
BartoszKP

331

내가하는 방법은 다음과 같습니다

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

제안? 코멘트? 이 알고리즘을 개선하는 방법은 무엇입니까?


112
"<48 * 60 * 60s"는 "어제"에 대한 다소 비 전통적인 정의입니다. 수요일 오전 9시이면 월요일 오전 9시 01 분을 "어제"라고 생각하십니까? 어제 또는 "n 일 전"자정 전후에 알고리즘을 고려해야한다고 생각했을 것입니다.
Joe

139
컴파일러는 일반적으로 24 * 60 * 60과 같은 상수 표현식을 미리 계산하는 데 능숙하므로 직접 86400으로 계산하고 주석에 원래 표현식을 넣는 대신 직접 사용할 수 있습니다.
zvolkov

11
@bzlm 나는 내가 작업하고있는 프로젝트를했다고 생각한다. 이 코드 샘플에서 몇 주가 생략되었다는 사실을 다른 사람들에게 알리는 것이 저의 동기였습니다. 그것을하는 방법에 관해서는, 그것은 나에게 꽤 직설적이었습니다.
jray

9
알고리즘을 개선하는 좋은 방법은 정확도를 높이기 위해 "2 개월 21 일 전", "1 시간 40 분 전"과 같은 2 개의 단위를 표시하는 것입니다.
Evgeny Levin

5
@ Jeffy, 윤년 및 관련 점검에 대한 계산을 놓쳤습니다
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 "";
}

나는 간결함과 새로운 틱 포인트를 추가하는 기능으로이 버전을 선호합니다. 이것은 Latest()긴 1 라이너 대신 Timespan 으로 확장되어 캡슐화 될 수 있지만 게시의 간결성을 위해 그렇게 할 것입니다. 2 시간이 경과 할 때까지 1 시간을 제공하여 1 시간 전, 1 시간 전을 수정합니다.


예를 들어 'theDate = DateTime.Now.AddMinutes (-40);'를 조롱하는 경우이 함수를 사용하여 모든 종류의 문제가 발생합니다. 나는 '40 시간 전 '을 받고 있지만 Michael의 refactormycode 응답을 사용하면 '40 분 전'에 올바르게 반환됩니다.
GONeale

나는 당신이 0이 없다고 생각합니다. long since = (DateTime.Now.Ticks-theDate.Ticks) / 10000000;
robnardo

8
흠,이 코드는 작동 할 수 있지만 사전의 키 순서가 특정 순서라고 가정하는 것은 올바르지 않으며 유효하지 않습니다. Dictionary는 Long이 아니라 int!를 반환하는 Object.GetHashCode ()를 사용합니다. 이들을 정렬하려면 SortedList <long, string>을 사용해야합니다. if / else if /.../ else 집합에서 임계 값을 평가하는 데 어떤 문제가 있습니까? 같은 수의 비교를 얻습니다. 참고로 long.MaxValue의 해시는 int.MinValue와 동일합니다!
CodeMonkeyKing

OP는 t.Days> 30을 잊었습니까? t.Days / 30 :
Lars Holm Jensen

@CodeMonkeyKing이 언급 한 문제를 해결하기 위해 SortedDictionary일반 대신을 사용할 수 있습니다 Dictionary. 사용법은 동일하지만 키가 정렬되도록합니다. 그러나 사용하고 있는 사전 유형에 관계없이 "95 개월 전"을RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3)) 반환 하기 때문에 알고리즘에 결함 이 있습니다. 어떤 임계 값에 따라 "3 개월 전"또는 "4 개월 전"을 반환해야합니다. -다시 사용) -3이 지난해 날짜를 만들지 않더라도 (12 월에 테스트 했으므로이 경우에는 발생하지 않습니다).
Matt

71

PHP 용 Jeffs Script를 다음과 같이 다시 작성하십시오.

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
질문은 C # 태그입니다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

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

이것은 매우 좋은 IMO입니다 :) 이것은 확장 방법으로 리팩터링 될 수 있습니까? 사전이 정적이 될 수 있으므로 한 번만 생성되고 그 이후에 참조 될 수 있습니까?
Pure.Krome


5
인스턴스화와 GC 이탈을 줄이기 위해 해당 사전을 필드로 가져와야 할 것입니다. 로 변경 Func<string>해야 Func<double>합니다.
Drew Noakes

49

다음은 미래 날짜와 과거 날짜를 모두 처리하고 원하는 세부 정보 수준을 지정할 수있는 근사 옵션 ( "3 시간 전"대 "3 시간, 23 분, 12 초 전 ") :

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

클라이언트 측에서도 이것을 계산하는 것이 좋습니다. 서버 작업이 줄어 듭니다.

다음은 내가 사용하는 버전입니다 (Zack 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);
                });
        };
}

4
질문은 C # 태그입니다.Javascript 코드 입니까?
Kiquenet

36

Nuget 에는 Humanizr 이라는 패키지가 있으며 실제로는 실제로 잘 작동하며 .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은 블로그 에 글을 올렸습니다.


3
참고 : .net 4.5 이상에서는 완전한 Humanizer를 설치하지 마십시오. Humanizer.Core의 일부만 설치하십시오. 다른 언어 패키지는이 버전에서 지원되지 않습니다.
Ahmad

매우 유용합니다! 이 답변은이 목록에서 훨씬 높아야합니다. 내가 100 표를 받았다면, 이것에 투표 할 것입니다. 분명히 (JS-land에서 온)이 패키지를 찾는 것은 쉽지 않았습니다.
kumarharsh 2016 년

29

@jeff

IMHO는 조금 길어 보입니다. 그러나 "어제"와 "년"에 대한 지원으로 조금 더 강력 해 보입니다. 그러나 이것이 사용 된 경험에 따르면 사람은 처음 30 일 동안 내용을 볼 가능성이 큽니다. 그 뒤에 오는 것은 정말 하드 코어 사람들입니다. 그래서 나는 보통 이것을 짧고 단순하게 유지하기로 선택합니다.

이것이 현재 내 웹 사이트 중 하나에서 사용중인 방법입니다. 이것은 상대 요일, 시간, 시간 만 반환합니다. 그런 다음 사용자는 출력에서 ​​"ago"를 두 드려야합니다.

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

파티에 몇 년 늦었지만 과거와 미래의 날짜 모두에 대해이 작업을 수행해야했기 때문에 JeffVincent를 결합 했습니다. 그것은 삼원적인 사치입니다! :)

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로 이것을 쉽게 할 수있는 방법이 있습니까? java.util.Date클래스는 오히려 제한 보인다.

빠르고 더러운 Java 솔루션은 다음과 같습니다.

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
질문은 C # 태그입니다.Java 코드 입니까?
Kiquenet

20

아이폰 오브젝티브 -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];
    }
}

19

세계와 그녀의 남편이 코드 샘플을 게시하는 것으로 보이므로 여기에 몇 가지 답변을 바탕으로 작성한 내용이 있습니다.

이 코드를 현지화 할 수 있어야합니다. 따라서 Grammar지역화 가능한 용어를 지정하는 클래스 와 FuzzyDateExtensions확장 메소드를 보유하는 두 개의 클래스가 있습니다. 미래의 날짜 시간을 처리 할 필요가 없으므로이 코드로 처리하려고 시도하지 않았습니다.

나는 XMLdoc의 일부를 소스에 남겨 두었지만 간결성을 위해 대부분 (명료 한 곳)을 제거했습니다. 나는 또한 모든 반원을 여기에 포함시키지 않았다 :

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

FuzzyDateString클래스가 포함되어 있습니다

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

나는뿐만 아니라 현지화 달성하고 싶은 주요 사항 중 하나는, 그 "오늘"이었다 것에만 평균 "이 달력의 하루"는 그래서 IsToday, IsThisMonth, IsThisYear방법은 다음과 같이 :

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

반올림 방법은 다음과 같습니다 ( RoundedMonths약간 다르므로 포함 시켰습니다 ).

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

나는 사람들이 이것이 유용하고 흥미 롭기를 바랍니다 : o)


17

PHP에서는 다음과 같이합니다.

<?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
질문은 C # 태그 입니다. 왜이 PHP 코드 입니까? IMHO, C # 코드 만 적용
Kiquenet

17

사용 유창함 날짜 시간을

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

나는 클래스와 다형성을 사용 하여이 샷을 줄 것이라고 생각했습니다. 하위 클래스를 사용하여 오버 헤드가 너무 많은 이전 반복이있었습니다. 나는 훨씬 더 유연한 델리게이트 / 퍼블릭 프로퍼티 객체 모델로 전환했다. 내 코드는 약간 더 정확합니다. 너무 엔지니어링되지 않은 "개월 전"을 생성하는 더 좋은 방법을 생각해 낼 수 있기를 바랍니다.

나는 코드가 적고 간단하기 때문에 Jeff의 if-then 캐스케이드를 계속 고수한다고 생각합니다 (예상대로 작동하도록 확실히하는 것이 더 쉽습니다).

아래 코드에서 PrintRelativeTime.GetRelativeTimeMessage (TimeSpan ago) 는 상대 시간 메시지 (예 : "어제")를 반환합니다.

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

이 질문에 대한 다른 답변 과 동일 하지만 정적 사전을 사용하는 확장 방법입니다.


사전은 여기서 무엇을 사나요?
StriplingWarrior

StriplingWarrior : switch 문 또는 if / else 문 스택과 비교하여 읽기 및 수정이 쉽습니다. 정적이라는 사전은 ToRelativeDate를 사용하려고 할 때마다 사전과 Func <,> 객체를 만들 필요가 없음을 의미합니다. 그것은 내 대답에 링크 된 것과 비교하여 한 번만 만들어졌습니다.
Chris Charabaruk

내가 참조. Dictionary"항목이 반환되는 순서가 정의되어 있지 않습니다"( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) 문서에 따르면 사용하기에 가장 적합한 데이터 구조가 아니라고 생각했기 때문입니다. 항목을 순서대로 유지하는 것만 큼 조회 시간을 신경 쓰지 않을 때.
StriplingWarrior

StriplingWarrior : LINQ를 Dictionarys 와 함께 사용할 때이를 고려합니다 . 그래도 여전히 불편하다면을 사용할 수 SortedDictionary있지만 내 경험에 따르면 불필요합니다.
Chris Charabaruk

12

시청자의 시간대를 알면 요일 단위로 달력 날짜를 사용하는 것이 더 명확 할 수 있습니다. .NET 라이브러리에 익숙하지 않으므로 불행히도 C #에서 어떻게하는지 알 수 없습니다.

소비자 사이트에서는 1 분 안에 손으로 더 많은 돈을 벌 수도 있습니다. "분 전보다 적다"또는 "지금"은 충분할 수 있습니다.


11

시도해 볼 수 있습니다. 정확하게 작동한다고 생각합니다.

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

클라이언트 측 gwt 사용을위한 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;
 }

}

질문은 C # 태그 입니다. 왜이 Java 코드 입니까? IMHO, C # 코드 만 적용
Kiquenet

9

J

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

뺄셈을하면 어쨌든 DateTime리턴 TimeSpan됩니다.

그래서 당신은 할 수 있습니다

(DateTime.UtcNow - dt).TotalSeconds

또한 상수가 손으로 곱해진 다음 곱셈과 함께 주석이 추가 된 것을보고 놀랐습니다.


8

다음은 알고리즘 스택 오버 플로우가 사용하지만 버그 수정 ( "1 시간 전"없음)과 함께 의사 코드에서보다 간결하게 다시 작성되었습니다. 이 함수는 몇 초 전에 (양수) 걸리며 "3 시간 전"또는 "어제"와 같이 사람에게 친숙한 문자열을 반환합니다.

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

다음과 같은 TimeAgo 확장 을 사용할 수 있습니다 .

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

또는 Timeago의 Razor 확장 기능이있는 jQuery 플러그인 을 사용 하십시오.


8

이 로직 클라이언트 측을 수행하여 서버 측로드를 줄일 수 있습니다. 참조를 위해 일부 Digg 페이지에서 소스를보십시오. 서버는 Javascript에 의해 처리되는 에포크 시간 값을 방출합니다. 이렇게하면 최종 사용자의 시간대를 관리 할 필요가 없습니다. 새로운 서버 측 코드는 다음과 같습니다.

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

거기에 NOSCRIPT 블록을 추가하고 ToString ()을 수행 할 수도 있습니다.


8

이것은 Bill Gates의 블로그 중 하나에서 얻었습니다. 브라우저 기록에서 찾아야하며 링크를 제공합니다.

Javascript 코드는 요청대로 동일한 작업을 수행합니다.

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

기본적으로 당신은 초 단위로 일합니다 ...


6

나는이 게시물과 관련하여 이미 많은 답변이 있다고 생각하지만 플러그인처럼 사용하기 쉽고 프로그래머가 쉽게 읽을 수있는 이것을 사용할 수 있습니다. 특정 날짜를 보내고 문자열 형식으로 값을 얻으십시오.

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

과 같은 출력을 원하면 "2 days, 4 hours and 12 minutes ago"시간 범위가 필요합니다.

TimeSpan timeDiff = DateTime.Now-CreatedDate;

그런 다음 원하는 값에 액세스 할 수 있습니다.

timeDiff.Days
timeDiff.Hours

기타...


4

이에 대한 편리한 확장 방법을 제공하고 코드를 더 읽기 쉽게 만듭니다. 먼저에 대한 몇 가지 확장 방법입니다 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;
    }
}

그런 다음, 하나 DateTime.

public static class DateTimeExtensions {

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

        return dateTime - delta;
    }
}

이제 다음과 같은 작업을 수행 할 수 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.