Алгоритм нечеткой даты - PullRequest
32 голосов
/ 05 мая 2009

Я ищу алгоритм нечеткой даты. Я только начал писать один и понял, что это утомительное задание. Он быстро выродился во множество ужасных кодов, чтобы справиться с особыми случаями, такими как разница между «вчера», «прошлой неделей» и «концом прошлого месяца», которые могут (в некоторых случаях) относиться к одному и тому же дню, но индивидуально верны на основании сегодняшней даты.

Я уверен, что должен быть нечеткий форматер даты с открытым исходным кодом, но я не могу его найти. В идеале я хотел бы что-то, используя NSDate (OSX / iPhone) и его средства форматирования, но это не сложный момент. Кто-нибудь знает, что средство форматирования нечеткой даты берет любой период времени относительно текущего и возвращает строку, подобную (но не ограничивающуюся):

  • несколько минут назад
  • за последние пять минут
  • ранее сегодня
  • сегодня утром
  • прошлой ночью
  • на прошлой неделе
  • в прошлую среду
  • в начале прошлого месяца
  • июнь прошлого года
  • пару лет назад

В идеальном мире я бы хотел, чтобы строка была как можно более насыщенной (т. Е. Возвращать случайные варианты слова «Только минуту назад», например, «только сейчас»).

Разъяснение. Я ищу что-то более тонкое, чем обычные клюшки и веревки. Я хочу, чтобы что-то, что знает, что «вчера» и «последняя среда» могут относиться к одному и тому же периоду, но только один является правильным, когда сегодня четверг.

Ответы [ 14 ]

12 голосов
/ 22 августа 2010

В NSDateFormatter есть свойство - "doRelativeDateFormatting". Он появляется только в 10.6 / iOS4.0 и более поздних версиях, но форматирует дату в относительную дату в правильной локали.

Из Документация Apple :

Если форматировщик даты использует относительную дату форматирование, где это возможно, он заменяет компонент даты его вывода с фраза, такая как «сегодня» или «Завтра» - это указывает на родственника Дата. Доступные фразы зависят от локаль для форматера даты; в то время как для дат в будущем, Английский допускает только «завтра» Французский может позволить «на следующий день после послезавтра », как показано на следующий пример.

код

Ниже приведен код, который выведет большое количество соответствующих строк для данной локали.

NSLocale *locale = [NSLocale currentLocale];
//    NSLocale *locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"] autorelease];

NSDateFormatter *relativeDateFormatter = [[NSDateFormatter alloc] init];
[relativeDateFormatter setTimeStyle:NSDateFormatterNoStyle];
[relativeDateFormatter setDateStyle:NSDateFormatterMediumStyle];
[relativeDateFormatter setDoesRelativeDateFormatting:YES];
[relativeDateFormatter setLocale:locale];

NSDateFormatter *normalDateFormatter = [[NSDateFormatter alloc] init];
[normalDateFormatter setTimeStyle:NSDateFormatterNoStyle];
[normalDateFormatter setDateStyle:NSDateFormatterMediumStyle];
[normalDateFormatter setDoesRelativeDateFormatting:NO];
[normalDateFormatter setLocale:locale];

NSString * lastUniqueString = nil;

for ( NSTimeInterval timeInterval = -60*60*24*400; timeInterval < 60*60*24*400; timeInterval += 60.0*60.0*24.0 )
{
    NSDate * date = [NSDate dateWithTimeIntervalSinceNow:timeInterval];

    NSString * relativeFormattedString = [relativeDateFormatter stringForObjectValue:date];
    NSString * formattedString = [normalDateFormatter stringForObjectValue:date];

    if ( [relativeFormattedString isEqualToString:lastUniqueString] || [relativeFormattedString isEqualToString:formattedString] )
        continue;

    NSLog( @"%@", relativeFormattedString );
    lastUniqueString = relativeFormattedString;
}

Примечания:

  • Локаль не требуется
  • Есть не так много замен Английский. На момент написания там являются: «Вчера, сегодня, завтра». Apple может включить больше в будущем.
  • Интересно сменить локаль и посмотреть что доступно на других языках (Французский имеет немного больше, чем английский, например)
  • Если на iOS вы можете подписаться на UIApplicationSignificantTimeChangeNotification

Интерфейсный Разработчик

Вы можете установить свойство didRelativeDateFormatting в Интерфейсном Разработчике:

  • Выберите NSDateFormatter и выберите вкладку «Identity Inspector» палитры инспекторов (последний один [команда-6]).
  • В подразделе «Пользователь» Определенные атрибуты времени выполнения », вы можете добавьте свое собственное значение для ключа на выбранный объект (в данном случае, ваш экземпляр NSDateFormatter). добавлять "doRelativeDateFormatting", выберите тип «логический», и убедитесь, что это проверено.
  • Помните : Может показаться, что он вообще не работает, но это может произойти, потому что для вашей локали есть только несколько замененных значений. Попробуйте хотя бы дату «Вчера», «Сегодня» и «Завтра», прежде чем решить, правильно ли она настроена.
9 голосов
/ 05 мая 2009

Этот вопрос должен помочь вам начать работу. У него есть код, который этот сайт использует для расчета своего относительного времени. Он может не иметь определенных диапазонов, которые вы хотите, но их достаточно легко добавить, как только вы его настроите.

7 голосов
/ 05 мая 2009

Возможно, вы захотите посмотреть на distance_of_time_in_words функцию Rail в date_helper.rb , которую я вставил ниже.

# File vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb, line 59
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
 from_time = from_time.to_time if from_time.respond_to?(:to_time)
 to_time = to_time.to_time if to_time.respond_to?(:to_time)
 distance_in_minutes = (((to_time - from_time).abs)/60).round
 distance_in_seconds = ((to_time - from_time).abs).round

 I18n.with_options :locale => options[:locale], :scope => 'datetime.distance_in_words''datetime.distance_in_words' do |locale|
   case distance_in_minutes
     when 0..1
       return distance_in_minutes == 0 ?
              locale.t(:less_than_x_minutes, :count => 1) :
              locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds

       case distance_in_seconds
         when 0..4   then locale.t :less_than_x_seconds, :count => 5
         when 5..9   then locale.t :less_than_x_seconds, :count => 10
         when 10..19 then locale.t :less_than_x_seconds, :count => 20
         when 20..39 then locale.t :half_a_minute
         when 40..59 then locale.t :less_than_x_minutes, :count => 1
         else             locale.t :x_minutes,           :count => 1
       end

     when 2..44           then locale.t :x_minutes,      :count => distance_in_minutes
     when 45..89          then locale.t :about_x_hours,  :count => 1
     when 90..1439        then locale.t :about_x_hours,  :count => (distance_in_minutes.to_f / 60.0).round
     when 1440..2879      then locale.t :x_days,         :count => 1
     when 2880..43199     then locale.t :x_days,         :count => (distance_in_minutes / 1440).round
     when 43200..86399    then locale.t :about_x_months, :count => 1
     when 86400..525599   then locale.t :x_months,       :count => (distance_in_minutes / 43200).round
     when 525600..1051199 then locale.t :about_x_years,  :count => 1
     else                      locale.t :over_x_years,   :count => (distance_in_minutes / 525600).round
   end
 end
end
6 голосов
/ 05 сентября 2011

Это основано на коде в симпатичных и гуманных темах даты и времени. Я добавил обработку для «в прошлый понедельник, 5 вечера», потому что мне это нравится больше, чем x дней назад. Это касается прошлого и будущего вплоть до столетий. Я заинтересован в аспекте интернационализации, так что в конечном итоге это требует гораздо больше работы. Расчеты в местном часовом поясе.

public static class DateTimePretty
{
    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 WEEK = 7 * DAY;
    private const int MONTH = 30 * DAY;

    private const int YEAR = 365;

    const string now = "just now";
    const string secondsFuture = "in {0} seconds", secondsPast = "{0} seconds ago";
    const string minuteFuture = "in about a minute", minutePast = "about a minute ago";
    const string minutesFuture = "in about {0} minutes", minutesPast = "about {0} minutes ago";
    const string hourFuture = "in about an hour", hourPast = "about an hour ago";
    const string hoursFuture = "in about {0} hours", hoursPast = "about {0} hours ago";
    const string tomorrow = "tomorrow, {0}", yesterday = "yesterday, {0}";
    const string nextDay = "{0}", nextWeekDay = "next {0}", lastDay = "last {0}";
    //const string daysFuture = "in about {0} days", daysPast = "about {0} days ago";
    const string weekFuture = "in about a week", weekPast = "about a week ago";
    const string weeksFuture = "in about {0} weeks", weeksPast = "about {0} weeks ago";
    const string monthFuture = "in about a month", monthPast = "about a month ago";
    const string monthsFuture = "in about {0} months", monthsPast = "about {0} months ago";
    const string yearFuture = "in about a year", yearPast = "about a year ago";
    const string yearsFuture = "in about {0} years", yearsPast = "about {0} years ago";
    const string centuryFuture = "in about a century", centuryPast = "about a century ago";
    const string centuriesFuture = "in about {0} centuries", centuriesPast = "about {0} centuries ago";

    /// <summary>
    /// Returns a pretty version of the provided DateTime: "42 years ago", or "in 9 months".
    /// </summary>
    /// <param name="dateTime">DateTime in local time format, not Utc</param>
    /// <returns>A pretty string</returns>
    public static string GetPrettyDate(DateTime dateTime)
    {
        DateTime dateTimeNow = DateTime.Now;
        bool isFuture = (dateTimeNow.Ticks < dateTime.Ticks);
        var ts = isFuture ? new TimeSpan(dateTime.Ticks - dateTimeNow.Ticks) : new TimeSpan(dateTimeNow.Ticks - dateTime.Ticks);

        double delta = ts.TotalSeconds;

        if (delta < 10)
            return now;
        if (delta < 1 * MINUTE)
            return isFuture ? string.Format(secondsFuture, ts.Seconds) : string.Format(secondsPast, ts.Seconds);
        if (delta < 2 * MINUTE)
            return isFuture ? minuteFuture : minutePast;
        if (delta < 45 * MINUTE)
            return isFuture ? string.Format(minutesFuture, ts.Minutes) : string.Format(minutesPast, ts.Minutes);
        if (delta < 2 * HOUR)
            return isFuture ? hourFuture : hourPast;
        if (delta < 7 * DAY)
        {
            string shortTime = DateTimeFormatInfo.CurrentInfo.ShortTimePattern;
            string shortWeekdayTime = "dddd, " + shortTime;
            int dtDay = (int) dateTime.DayOfWeek;
            int nowDay = (int) dateTimeNow.DayOfWeek;
            if (isFuture)
            {
                if (dtDay == nowDay)
                {
                    if (delta < DAY)
                        return string.Format(hoursFuture, ts.Hours);
                    else
                        return string.Format(nextWeekDay, dateTime.ToString(shortWeekdayTime));
                }
                else if (dtDay - nowDay == 1 || dtDay - nowDay == -6)
                    return string.Format(tomorrow, dateTime.ToString(shortTime));
                else
                    return string.Format(nextDay, dateTime.ToString(shortWeekdayTime));
            }
            else
            {
                if (dtDay == nowDay)
                {
                    if (delta < DAY)
                        return string.Format(hoursPast, ts.Hours);
                    else
                        return string.Format(lastDay, dateTime.ToString(shortWeekdayTime));
                }
                else if (nowDay - dtDay == 1 || nowDay - dtDay == -6)
                    return string.Format(yesterday, dateTime.ToString(shortTime));
                else
                    return string.Format(lastDay, dateTime.ToString(shortWeekdayTime));
            }
        }
        //if (delta < 7 * DAY)
        //    return isFuture ? string.Format(daysFuture, ts.Days) : string.Format(daysPast, ts.Days);
        if (delta < 4 * WEEK)
        {
            int weeks = Convert.ToInt32(Math.Floor((double) ts.Days / 30));
            if (weeks <= 1)
                return isFuture ? weekFuture : weekPast;
            else
                return isFuture ? string.Format(weeksFuture, weeks) : string.Format(weeksPast, weeks);
        }
        if (delta < 12 * MONTH)
        {
            int months = Convert.ToInt32(Math.Floor((double) ts.Days / 30));
            if (months <= 1)
                return isFuture ? monthFuture : monthPast;
            else
                return isFuture ? string.Format(monthsFuture, months) : string.Format(monthsPast, months);
        }

        // Switch to days to avoid overflow
        delta = ts.TotalDays;
        if (delta < 100 * YEAR)
        {
            int years = Convert.ToInt32(Math.Floor((double) ts.TotalDays / 365.25));
            if (years <= 1)
                return isFuture ? yearFuture : yearPast;
            else
                return isFuture ? string.Format(yearsFuture, years) : string.Format(yearsPast, years);
        }
        else
        {
            int centuries = Convert.ToInt32(Math.Floor((double) ts.TotalDays / 365.2425));
            if (centuries <= 1)
                return isFuture ? centuryFuture : centuryPast;
            else
                return isFuture ? string.Format(centuriesFuture, centuries) : string.Format(centuriesPast, centuries);
        }
    }
}
6 голосов
/ 29 июня 2011

Итак, вот категория, которую я написал на NSDate для тех, кто все еще заинтересован. Проблема - одна из тех, которые становятся немного причудливыми. Это в основном огромный параметр переключения (хотя я реализовал его в виде последовательности каскадных операций if (), чтобы сделать его более читабельным.

Для каждого периода времени я затем выбираю из случайного набора способов определения времени.

В целом, это порадовало некоторых наших пользователей, но я не уверен, что оно того стоило.

NSTimeInterval const kTenSeconds = (10.0f );
NSTimeInterval const kOneMinute = (60.0f);
NSTimeInterval const kFiveMinutes = (5.0f*60.0f);
NSTimeInterval const kFifteenMinutes = (15.0f*60.0f) ;
NSTimeInterval const kHalfAnHour = (30.0f*60.0f) ;
NSTimeInterval const kOneHour = 3600.0f;    // (60.0f * 60.0f);
NSTimeInterval const kHalfADay = (3600.0f * 12.0f);
NSTimeInterval const kOneDay = (3600.0f * 24.0f);
NSTimeInterval const kOneWeek = (3600.0f * 24.0f * 7.0f);

@implementation NSDate (Fuzzy)

-(NSString*)fuzzyStringRelativeToNow;
{
    static NSArray* secondsStrings;
    static NSArray* minuteStrings;
    static NSArray* fiveMinuteStrings;
    static NSArray* halfHourStrings;
    static NSArray* earlyMonthStrings;

    NSTimeInterval timeFromNow = [self timeIntervalSinceNow];
    if((timeFromNow < 0))       // In the past
    {
        timeFromNow = - timeFromNow;

        if ( (timeFromNow <  kTenSeconds))
        {
            if(!secondsStrings)
            {   
                secondsStrings = [[NSArray arrayWithObjects:@"just now",
                                                            //@"a few seconds ago",
                                                            //@"right this instant",
                                                            @"moments ago",
                                                            nil] retain];
            }

            unsigned int index = random() % ([secondsStrings count] - 1);
            return [secondsStrings objectAtIndex:index];
        }

        if ( (timeFromNow < kOneMinute))
        {
            if(!minuteStrings)
            {

                minuteStrings = [[NSArray arrayWithObjects:@"just now",
                                  @"very recently",
                                  @"in the last minute",
                                  nil] retain];
            }

            unsigned int index = random() % ([minuteStrings count] - 1);
            return [minuteStrings objectAtIndex:index];
        }

        if (timeFromNow < kFiveMinutes)
        {
            if(!fiveMinuteStrings)
            {
                fiveMinuteStrings = [[NSArray arrayWithObjects:@"just now",
                                      @"very recently",
                                      //@"in the last minute",
                                      @"a few minutes ago",
                                      //@"in the last five minutes",
                                      nil] retain];
            }
            unsigned int index = random() % ([fiveMinuteStrings count] - 1);
            return [fiveMinuteStrings objectAtIndex:index];
        }

        if (timeFromNow < kFifteenMinutes)
        {
            return @"in the last 15 minutes";
        }

        if (timeFromNow < kHalfAnHour)
        {
            if(!halfHourStrings)
            {
                halfHourStrings = [[NSArray arrayWithObjects:@"in the last half hour",
                                                            //@"in the last half an hour",
                                                            @"in the last 30 minutes",
                                                            //@"about half an hour ago",
                                                            @"fairly recently",
                                                            nil] retain];
            }
            unsigned int index = random() % ([halfHourStrings count] - 1);
            return [halfHourStrings objectAtIndex:index];
        }

        if (timeFromNow < kOneHour)
        {
            return @"in the last hour";
        }

        if ((timeFromNow < (kOneHour + kFiveMinutes)) && (timeFromNow > (kOneHour - kFiveMinutes)))
        {
            return @"about an hour ago";
        }

        if((timeFromNow < ((kOneHour*2.0f) + kFiveMinutes ))&& (timeFromNow > ((kOneHour*2.0f) - kFiveMinutes)))
        {
            return @"a couple of hours ago";
        }

        // Now we're over an hour, we need to calculate a few specific dates to compare against

        NSDate *today = [NSDate date];

        NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];

        NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
        NSDateComponents* todayComponents = [gregorian components:unitFlags fromDate:today];

        todayComponents.hour = 12;

        NSDate* noonToday = [gregorian dateFromComponents:todayComponents];


        NSTimeInterval timeSinceNoonToday = [self timeIntervalSinceDate:noonToday];

        if (timeSinceNoonToday > 0)                         // sometime since noon
        {
            if (timeSinceNoonToday > kOneHour * 9)          // i.e. after 9pm today
                return @"earlier tonight";
            if (timeSinceNoonToday > kOneHour * 7)          // i.e. after 7pm today
                return @"earlier this evening";
            if (timeSinceNoonToday < kOneHour * 1)          // between noon and 1pm
                return @"early this afternoon";

            return @"this afternoon";
        }


        NSTimeInterval timeSinceMidnight = kHalfADay -timeSinceNoonToday;   // Note sign is reversed.   

        if ((timeSinceNoonToday < 0) & (timeSinceNoonToday > -kHalfADay))       // between midnight and noon today
        {
            if (timeSinceMidnight < kFiveMinutes)
                return @"around midnight";
            if (timeSinceMidnight < kOneHour * 2)           // up to 2am
                return @"very early this morning";
            if (timeSinceMidnight < kOneHour * 5)           // up to 5am
                return @"early this morning";
            else if (timeSinceMidnight < kOneHour * 11)
                return @"late this morning";
            else
                return @"this morning";
        }


        // NSTimeInterval timeSinceNoonYesterday = timeSinceNoonToday - kOneDay;

        // timeSinceMidnight = -timeSinceMidnight;

        if (timeSinceMidnight < kOneHour * 24)      // not the day before...
        {

            if (timeSinceMidnight < kFiveMinutes)
                return @"around midnight";
            if (timeSinceMidnight < kFifteenMinutes)
                return @"just before midnight";
            if (timeSinceMidnight < kOneHour * 2)           // after 10pm
                return @"late last night";
            if (timeSinceMidnight < kOneHour * 5)           // After 7
                return @"yesterday evening";
            else if (timeSinceMidnight < kOneHour * 7)
                return @"yesterday evening";                // after 5pm
            else if (timeSinceMidnight < kOneHour * 7)
                return @"yesterday evening";                // after 5pm
            else if (timeSinceMidnight < kOneHour * 10)
                return @"yesterday afternoon";              // after 5pm
            else if (timeSinceMidnight < kOneHour * 12)
                return @"early yesterday afternoon";        // before 1pm
            else if (timeSinceMidnight < kOneHour * 13)
                return @"late yesterday morning";           // after 11m
            else if (timeSinceMidnight < kOneHour * 17)
                return @"yesterday morning";                
            else 
                return @"early yesterday morning";
        }

        NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];

        int integerSeconds = timeSinceMidnight;
        int integerDay = kOneDay;
        int secondsIntoDay = integerSeconds % integerDay;
        NSString* formatString = @"last %@";

        if (timeFromNow < kOneWeek)
        {
            if (secondsIntoDay < kFifteenMinutes)
                formatString = @"around midnight on %@";
            //else if (secondsIntoDay < kFifteenMinutes)
            //  formatString = @"just before midnight on %@";
            else if (secondsIntoDay < kOneHour * 2)         // after 10pm
                formatString = @"late on %@ night";
            else if (secondsIntoDay < kOneHour * 5)         // After 7
                formatString = @"on %@ evening";
            else if (secondsIntoDay < kOneHour * 10)
                formatString = @"on %@ afternoon";              // after 5pm
            else if (secondsIntoDay < kOneHour * 12)
                formatString = @"early on %@ afternoon";        // before 1pm
            else if (secondsIntoDay < kOneHour * 13)
                formatString = @"late on %@ morning";           // after 11am
            else if (secondsIntoDay < kOneHour * 17)
                formatString = @"on %@ morning";                
            else if (secondsIntoDay < kOneHour * 24)        // not the day before...
                formatString = @"early on %@ morning";

            [formatter setDateFormat:@"EEEE"];  /// EEEE is long format of day of the week see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
            return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; 
        }

        //formatString = @"on %@ the week before last";
        /*if (secondsIntoDay < kOneHour * 2)            // after 10pm
            formatString = @"early on %@ the week before last";
        else if (timeSinceMidnight > kOneHour * 13)
            formatString = @"late on %@ the week before last";          // after 11m*/

        //if (timeFromNow < kOneWeek * 2)
        //{
        //  [formatter setDateFormat:@"EEE"];           /// EEE is short format of day of the week see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
        //  return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; 
        //}

        if (timeFromNow < kOneWeek * 2)
            {
            return @"the week before last";
            }

        NSDateComponents* myComponents = [gregorian components:unitFlags fromDate:self];

        int monthsAgo = myComponents.month - todayComponents.month;

        int yearsAgo = myComponents.year - todayComponents.year;
        if (yearsAgo == 0)
        {
            if (monthsAgo == 0)
            {
                if(myComponents.day > 22)
                    return @"late this month";
                if(myComponents.day < 7)
                {

                    if(!earlyMonthStrings)
                    {   
                        earlyMonthStrings = [[NSArray arrayWithObjects:@"earlier this month",
                                                                       //@"at the beginning of the month",
                                                                       @"early this month",
                                                                       nil] retain];
                    }

                    unsigned int index = random() % ([earlyMonthStrings count] - 1);
                    return [earlyMonthStrings objectAtIndex:index];
                }
                return @"earlier this month";
            }

            if (monthsAgo == 1)
            {
                if(myComponents.day > 22)
                    return @"late last month";
                if(myComponents.day < 7)
                    return @"early last month";
                return @"last month";
            }

            formatString  = @"in %@ this year";
            /*if(myComponents.day > 22)
                formatString  = @"late in %@ this year";
            if(myComponents.day < 7)
                formatString  = @"early in %@ this year";*/


            [formatter setDateFormat:@"MMMM"];          /// MMM is longformat of month see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
            return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]];  
        }

        if (yearsAgo == 1)
        {
            formatString  = @"in %@ last year";
            /*if(myComponents.day > 22)
                formatString  = @"late in %@ last year";
            if(myComponents.day < 7)
                formatString  = @"late in %@ last year";*/


            [formatter setDateFormat:@"MMM"];           /// MMM is longformat of month see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
            return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]];  
        }

        // int daysAgo = integerSeconds / integerDay;

    // Nothing yet...
        [formatter setDateStyle:kCFDateFormatterMediumStyle];
        //[formatter setTimeStyle:kCFDateFormatterShortStyle];

        return [NSString stringWithFormat:@"on %@",[formatter stringFromDate: self]];

    }
    else
    if(timeFromNow > 0) // The future
    {
        AICLog(kErrorLogEntry, @"FuzzyDates: Time marked as in the future: referenced date is %@, local time is %@", self, [NSDate date]);
        return @"moments ago";

    }
    else
        return @"right now";    // this seems unlikely.

    return [self description];  // should never get here.
}

извините, что это заняло так много времени ...

3 голосов
/ 05 мая 2009

Я не уверен, почему вы говорите, что это будет ужасная практика кодирования. Каждая из возвращаемых строк на самом деле является подмножеством родительского набора, поэтому вы можете сделать это элегантно в цепочке if / elseif.

if timestamp < 5sec
    "A moment ago"
elseif timestamp < 5min 
    "Few minutes ago"
elseif timestamp < 12hr && timestamp < noon
    "Today Morning"
...
elseif timestamp < 1week 
    "Few days ago"
elseif timestamp < 1month
    "Few weeks ago"
elseif timestamp < 6month
    "Few Months ago" 
...
else
    "Really really long time ago"
1 голос
/ 09 апреля 2012

Я не был доволен решением в другом вопросе. Так что сделал мой собственный, используя класс Date time. ИМО, его чище. В моих тестах это работало так, как я хотел. Надеюсь, это кому-нибудь поможет.

DateTime now = DateTime.Now;

long nowticks = now.Ticks;
long thenticks = dt.Ticks;

long diff = nowticks - thenticks;

DateTime n = new DateTime(diff);

if (n.Year > 1)
{
    return n.Year.ToString() + " years ago";
}
else if (n.Month > 1)
{
    return n.Month.ToString() + " months ago";
}
else if (n.Day > 1)
{
    return n.Day.ToString() + " days ago";
}
else if (n.Hour > 1)
{
    return n.Hour.ToString() + " hours ago";
}
else if (n.Minute > 1)
{
    return n.Minute.ToString() + " minutes ago";
}
else
{
    return n.Second.ToString() + " seconds ago";
}
1 голос
/ 05 мая 2009

Я знаю, что выражение таких времен стало довольно популярным в последнее время, но, пожалуйста, подумайте над тем, чтобы сделать возможным переключение между относительными «нечеткими» датами и обычными абсолютными датами.

Например, полезно знать, что комментарий был сделан 5 минут назад, но менее полезно говорить мне комментарий A был 4 часа назад, а комментарий B был 9 часов назад, когда сейчас 11 часов утра, и я бы лучше знал, что комментарий A был написан, когда кто-то проснулся этим утром, и комментарий B был написан кем-то, кто ложился спать поздно (предполагая, что я знаю, что они в моем часовом поясе).

- РЕДАКТИРОВАТЬ: глядя на ваш вопрос, вы, кажется, избежали этого в некоторой степени, ссылаясь на время суток вместо «X назад», но с другой стороны, вы можете создавать ложное впечатление, если пользователи находятся в другом часовом поясе, так как ваше «это утро» может быть среди ночи для соответствующего пользователя.

Возможно, было бы здорово увеличить время с относительным временем суток в зависимости от часового пояса другого пользователя, но это предполагает, что пользователи готовы предоставить его, и это правильно.

1 голос
/ 05 мая 2009

Само собой разумеется (но я все равно это скажу) не используйте цикл where, который уменьшается на 365 дней в году даже на 366 дневных високосных годах (или вы окажетесь в рядах разработчиков Zune)

вот версия c #:

http://tiredblogger.wordpress.com/2008/08/21/creating-twitter-esque-relative-dates-in-c/

1 голос
/ 05 мая 2009

По моему опыту, эти типы генераторов дат вовсе не "нечеткие". На самом деле, они просто куча временных групп, основанных на утверждениях. Например, любое время менее 30 секунд - это «моменты назад», 360–390 дней - «всего год назад» и т. Д. Некоторые из них будут использовать целевую дату для расчета специальных имен (июнь, среда и т. Д.). Извините, что разбил ваши иллюзии.

...