【问题标题】:Fuzzy date algorithm模糊日期算法
【发布时间】:2010-10-23 18:07:20
【问题描述】:

我正在寻找一种模糊日期算法。我刚开始写一个,就意识到这是一项多么乏味的任务。它很快退化成许多可怕的代码来处理特殊情况,例如“昨天”、“上周”和“上个月末”之间的差异,所有这些都可以(在某些情况下)指同一天,但单独正确基于今天的日期。

我确信肯定有一个开源的模糊日期格式化程序,但我找不到它。理想情况下,我想要使用 NSDate (OSX/iPhone) 及其格式化程序的东西,但这并不难。有谁知道模糊日期格式化程序采用相对于现在的任何时间段并返回一个字符串,例如(但不限于):

  • 几分钟前
  • 最后五分钟
  • 今天早些时候
  • 今天早上
  • 昨晚
  • 上周
  • 上周三
  • 上月初
  • 去年六月
  • 几年前

在理想情况下,我希望字符串尽可能丰富(即返回“刚才”的随机变体,例如“刚刚”)。

澄清。我正在寻找比基本的buckts和string更微妙的东西。我想要知道“昨天”和“上周三”都可以指代同一时期但今天是星期四时只有一个是正确的。

【问题讨论】:

  • 你开始实施了吗?有出版作品的计划吗?
  • 我完成了一半,完成后可能会发布它。这对我的项目来说不是一个高优先级,但我很想把它做好。
  • 我也很想看到一些结果,如果你有机会发表你已经拥有的东西,也许有人会捡起来?
  • 所以我确实写了算法。核心概念是,我们谈论日期的方式会随着与我们的距离的变化而变化。我们现在刚刚发布我们的产品,但如果我有时间清理它以使其通常有用,我可能会开源它。
  • NSDate+timeAgo 类别/库,支持请求的日期格式和多种本地化:github.com/kevinlawler/NSDate-TimeAgo

标签: algorithm date formatting nsdate fuzzy


【解决方案1】:

This question should get you started. 它有这个站点用来计算其相对时间的代码。它可能没有您想要的特定范围,但一旦设置好它们就很容易添加。

【讨论】:

  • 谢谢,我想要得到我想要的,我需要在其中一个解决方案中添加一些额外的代码。
【解决方案2】:

根据我的经验,这些类型的日期生成器根本不是“模糊”的。事实上,它们只是一堆基于时间段的 if 语句。例如,任何小于 30 秒的时间是“片刻前”,360 到 390 天是“一年前”等。其中一些将使用目标日期来计算特殊名称(六月、星期三等)。 很抱歉打破你的幻想。

【讨论】:

  • 您说的有一部分是对的,但还有更多的微妙之处。就像您描述早上 5 点的方式一样,如果您从同一天早上 6 点、第二天早上 6 点或几个月后早上 6 点开始描述它。
【解决方案3】:

这几乎总是使用巨大的 switch 语句来完成,而且实现起来很简单。

请记住以下几点:

  • 始终首先测试最小的时间跨度
  • 不要忘记让您的字符串可本地化。

【讨论】:

    【解决方案4】:

    我不知道你为什么说这将是一种可怕的编码实践。每个返回字符串实际上都是父集的一个子集,因此您可以非常优雅地在 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"
    

    【讨论】:

    • 不完全是,我的观点是这些时期不是彼此的子集。 “昨天”和“上周三”都可以指同一时期,但今天是星期四时只有一个是正确的
    • 不确定是否正确。昨天总是比“最后一个 Eednesday”更接近今天。如果今天是星期四,那么前 24 小时是昨天,前 24 小时是“前天”,前 24 小时是“上周一”,
    • 好吧,一个更好的例子:早上 5 点,“6 小时前”更准确地说是“昨晚”。
    【解决方案5】:

    您可能会发现source from timeago 很有用。该插件的描述是“一个 jQuery 插件,可以轻松支持自动更新模糊时间戳(例如“4 分钟前”或“大约 1 天前”)。”

    它本质上是 Rail 的 distance_of_time_in_words 函数的 JavaScript 端口,塞进了一个 jQuery 插件。

    【讨论】:

      【解决方案6】:

      不用说(但我还是会这么说)不要使用每年减少 365 天的 where 循环,即使是在 366 天闰年(否则你会发现自己处于 Zune 开发人员的行列)

      这是一个 c# 版本:

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

      【讨论】:

        【解决方案7】:

        您可能想查看date_helper.rb 中Rail 的distance_of_time_in_words 函数,我已将其粘贴在下面。

        # 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
        

        【讨论】:

          【解决方案8】:

          我的公司有 this .NET library,它可以做一些你想做的事情,因为它可以非常灵活地解析日期时间(包括一些相对格式),但它只做非相对输出。

          【讨论】:

            【解决方案9】:

            我知道这样的表达时间最近变得很流行,但请考虑将其作为切换相对“模糊”日期和正常绝对日期的选项。

            例如,知道一条评论是在 5 分钟前发表的很有用,但告诉我评论 A 是 4 小时前和评论 B 是 9 小时前的上午 11 点就没那么有用了,我宁愿知道评论 A 是今天早上有人醒来时写的,评论 B 是有人熬夜写的(假设我知道他们在我的时区)。

            -- 编辑:仔细查看您的问题,您似乎通过参考一天中的时间而不是“X 前”在某种程度上避免了这种情况,但另一方面,如果用户处于不同的时区,您可能会给人一种错误的印象,因为对于相关用户而言,您的“今天早上”可能是在半夜。

            根据其他用户的时区将时间增加为一天中的相对时间可能很酷,但前提是用户愿意提供它并且它是正确的。

            【讨论】:

            • 这就是我想要做的。我不想只说“n 小时前”,我希望能够以人类友好的格式写日期:“上周星期三下午晚些时候”。
            【解决方案10】:

            NSDateFormatter 中有一个属性——“doesRelativeDateFormatting”。它仅出现在 10.6/iOS4.0 及更高版本中,但它会将日期格式化为正确语言环境中的相对日期。

            来自Apple's Documentation

            如果日期格式化程序使用相对日期 格式化,在可能的情况下替换 其输出的日期部分 一个短语——例如“今天”或 “明天”——表示亲戚 日期。可用的短语取决于 日期格式化程序的语言环境; 而对于未来的日期, 英语可能只允许“明天”, 法国可能允许“在 后天”,如图所示 下面的例子。

            代码

            以下代码将打印出给定语言环境的大量相关字符串。

            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

            界面生成器

            您可以在 Interface Builder 中设置“doesRelativeDateFormatting”属性:

            • 选择您的 NSDateFormatter 并 选择“身份检查器”选项卡 检查员调色板(最后一个 一个 [command-6])。
            • 在名为“用户 定义的运行时属性”,您可以 为所选对象(在本例中为您的 NSDateFormatter 实例)上的键添加您自己的值。添加 “doesRelativeDateFormatting”,选择 “布尔”类型,并确保它是 检查。
            • 记住:它可能看起来根本不起作用,但这可能是因为您的语言环境只有几个替代值。在确定是否设置不正确之前,请至少尝试选择昨天、今天和明天的日期。

            【讨论】:

            • 请注意,doesRelativeDateFormatting 仅适用于日期,而不适用于时间。
            【解决方案11】:

            所以,这是我在 NSDate 上为那些仍然感兴趣的人写的类别。问题是其中一个变得有点不切实际的问题。它基本上是一个巨大的 switch 语句(尽管我在一系列级联 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.
            }
            

            抱歉这么久才发这个...

            【讨论】:

              【解决方案12】:

              这是基于 Pretty and 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);
                      }
                  }
              }
              

              【讨论】:

              • 感谢未来的日期。这段代码值得超过 2 分
              【解决方案13】:

              我对另一个问题的解决方案不满意。所以我自己使用日期时间类。海事组织,它的清洁剂。在我的测试中,它像我想要的那样工作。希望这对某人有所帮助。

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

              【讨论】:

              • 这对其他人没有帮助,因为您没有提供自定义类。
              • 哇,我以前没有注意到你的评论,我使用了内置的 Datetime 类,如果我们有一个工作的,为什么要重新发明轮子?
              【解决方案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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-07-16
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-03-10
                相关资源
                最近更新 更多