Подтвердить что ты не робот

Нечеткий алгоритм даты

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

В NSDateFormatter есть свойство - "doesRelativeDateFormatting" . Он появляется только в 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

Конструктор интерфейсов

В конструкторе интерфейсов вы можете установить свойство "doesRelativeDateFormatting" :

  • Выберите NSDateFormatter и выберите вкладку "Идентификационный инспектор" палитры инспекторов (последняя один [команда-6]).
  • В разделе под названием "Пользователь Определенные атрибуты времени выполнения ", вы можете добавьте свое собственное значение для ключа на выбранном объекте (в данном случае ваш экземпляр NSDateFormatter). Добавить "doesRelativeDateFormatting" , выберите тип "Boolean", и убедитесь, что это проверено.
  • Запомнить. Может показаться, что он вообще не работает, но это может быть связано с тем, что для вашего языка есть только несколько замещенных значений. Попробуйте по крайней мере дату для Вчера, Сегодня и Завтра, прежде чем вы решите, если она не настроена правильно.

Ответ 3

Вы можете посмотреть функцию Rail distance_of_time_in_words в 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

Ответ 4

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

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

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

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

жаль, что так долго было опубликовано это...

Ответ 5

Это основано на коде в потоках даты и времени Pretty и Humane. Я добавил обработку для "последнего понедельника, 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

Я не уверен, почему вы говорите, что это будет ужасная практика кодирования. Каждая из строк возврата на самом деле является подмножеством родительского набора, поэтому вы можете довольно элегантно сделать это в цепочке 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"

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

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

- EDIT: взглянув ближе на ваш вопрос, вы, похоже, в некоторой степени избегаете этого, ссылаясь на время дня вместо "X назад", но, с другой стороны, вы можете давать ложное впечатление, если пользователи находятся в разных часовых поясах, так как ваше "это утро" может быть посреди ночи для соответствующего пользователя.

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

Ответ 10

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

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

Ответ 11

Это почти всегда выполняется с помощью гигантского оператора switch и тривиально реализуется.

Помните следующее:

  • Всегда проверяйте минимальный промежуток времени
  • Не забудьте сохранить ваши строки локализуемыми.

Ответ 12

Вы можете найти источник из timeago. Описание плагина представляет собой "плагин jQuery, который упрощает поддержку автоматического обновления нечетких временных меток (например," 4 минуты назад "или" около 1 дня назад ").

Это, по сути, функция JavaScript-порт функции Rail distance_of_time_in_words, переполненная в плагин jQuery.

Ответ 13

В моей компании эта библиотека .NET, которая делает некоторые из того, что вы хотите, в том, что она очень гибко анализирует время на дату (включая некоторые относительные форматы), но он делает только относительные выходы.

Ответ 14

Отметьте Chrono для эвристического синтаксического анализа Javascript.

Chrono поддерживает большинство форматов даты и времени, например:

Today, Tomorrow, Yesterday, Last Friday, etc
17 August 2013 - 19 August 2013
This Friday from 13:00 - 16.00
5 days ago
Sat Aug 17 2013 18:40:39 GMT+0900 (JST)
2014-11-30T08:15:30-05:30

https://github.com/wanasit/chrono