在任意时间范围内找到最佳日/月/年间隔的算法?

如果您有时间表,请说:

March 19, 2009 - July 15, 2011

是否有一种算法可以将时间框架分解为:

 March 19, 2009 - March 31, 2009 # complete days April 1, 2009 - December 31, 2009 # complete months January 1, 2010 - December 31, 2010 # complete years January 1, 2011 - June 30, 2011 # complete months July 1, 2011 - July 15, 2011 # complete days 

更具体地说,给定任意时间帧,甚至到第二个时间帧,是否有一种算法可以将其划分为最大数量的任意大小的间隔?

因此,可能不是将上述日期范围划分为几天/几个月/几年,而是将其划分为5天和18个月的块,这是随机的。 这种算法有正式的名称吗? 一个ruby的例子会很棒,但任何语言都可以。

我已经能够破解一些硬编码的Ruby来处理日/月/年的例子:

  • https://gist.github.com/1165914

…但似乎应该有一个算法来抽象它来处理任何间隔细分。 也许它只是归结为简单的数学。

这可能是作弊,但您可以使用active_support提供的日期函数大大简化代码。 下面是我使用active_support提出的一些代码。 算法非常简单。 弄清楚第一个月的最后一天,弄清楚上个月的第一天。 然后打印第一个月,将中间分成几年并打印出来,然后打印上个月。 当然,由于边缘情况,这种简单的算法在许多方面都有所下降。 以下算法尝试优雅地处理每个边缘情况。 我希望它有所帮助。

 require 'active_support/all' # Set the start date and end date start_date = Date.parse("March 19, 2009") end_date = Date.parse("July 15, 2011") if end_date < start_date # end date is before start date, we are done elsif end_date == start_date # end date is the same as start date, print it and we are done print start_date.strftime("%B %e, %Y") elsif start_date.year == end_date.year && start_date.month == end_date.month # start date and end date are in the same month, print the dates and we # are done print start_date.strftime("%B %e, %Y"), " - ", end_date.strftime("%B %e, %Y"), "\n" else # start date and end date are in different months first_day_of_next_month = Date.new((start_date + 1.month).year, (start_date + 1.month).month, 1); first_day_of_end_month = Date.new(end_date.year, end_date.month, 1); # print out the dates of the first month if (first_day_of_next_month - 1.day) == start_date print start_date.strftime("%B %e, %Y"), "\n" else print start_date.strftime("%B %e, %Y"), " - ", (first_day_of_next_month - 1.day).strftime("%B %e, %Y"), "\n" end # now print the inbetween dates if first_day_of_next_month.year == (first_day_of_end_month - 1.day).year && (first_day_of_end_month - 1.day) > first_day_of_next_month # start date and end date are in the same year, just print the inbetween # dates print first_day_of_next_month.strftime("%B %e, %Y"), " - ", (first_day_of_end_month - 1.day).strftime("%B %e, %Y") + "\n" elsif first_day_of_next_month.year < (first_day_of_end_month - 1.day).year # start date and end date are in different years so we need to split across # years year_iter = first_day_of_next_month.year # print out the dates from the day after the first month to the end of the # year print first_day_of_next_month.strftime("%B %e, %Y"), " - ", Date.new(first_day_of_next_month.year, 12, 31).strftime("%B %e, %Y"), "\n" year_iter += 1 # print out the full intermediate years while year_iter < end_date.year print Date.new(year_iter, 1, 1).strftime("%B %e, %Y"), " - ", Date.new(year_iter, 12, 31).strftime("%B %e, %Y"), "\n" year_iter += 1 end # print from the begining of the last year until the last day before the the # end month print Date.new(first_day_of_end_month.year, 1, 1).strftime("%B %e, %Y"), " - ", (first_day_of_end_month - 1.day).strftime("%B %e, %Y"), "\n" end # finally print out the days of the last month if first_day_of_end_month == end_date print end_date.strftime("%B %e, %Y"), "\n" else print first_day_of_end_month.strftime("%B %e, %Y"), " - ", end_date.strftime("%B %e, %Y"), "\n" end end 

从某种意义上讲,原始问题非常简单,因为一年是几个月,一个月是几天。 因此,你可以看看你的范围的几个块是完整的年份,把它们拿出来,并迭代地查看剩下的内容,以便完成下一个较小单元的完整实例。

在任意情况下,这不一定是真的。 在您的示例中,并非每个18个月的跨度都可以分为五天。 这使事情变得非常复杂,因为您不能再依赖于这种单位层次结构。

任意情况的另一个复杂因素是并非每个范围都有可行的故障。 在您的示例中,2011年1月1日 – 2011年1月4日不能分解为5天18个月的块。

因此我建议你尝试制定一个更受限制的问题版本:)

一旦你将不同大小的月份加入到混合中,这会稍微复杂一些,但是如果有一个好的约会库,那么相同的概念应该可以工作(并且可能使“进步”更容易)。

最初的psudocode以python结尾 – 抱歉。

 intervals = [1, 60, 3600] # second minute hour epoc = 0 # simple example case start = 4 # 4 seconds past epoc end = 7292 # 2:1:32 i = start while i < end: for iidx, interval in enumerate(intervals): nIval = 0 if(iidx+1 < len(intervals)): nIval = intervals[iidx+1] # wind down - find the next smallest interval that doesn't push us past END if (i-epoc) % interval == 0 and (i + nIval > end or not nIval): # move as close to end as possible pre = i while i+interval <= end: i += interval print "Stepped down %i to %i via %i" % (pre, i, interval) # wind up - find the next biggest interval boundary not past END elif nIval and (i-epoc) % nIval != 0: # advance to that interval boundry pre = i while (i-epoc) % nIval != 0 and i+interval <= end: i += interval print "Stepped up %i to %i via %i" % (pre, i, interval) if i == end: break