如何将时间段划分为多个时间段

时间:2016-10-10 12:37:25

标签: ruby-on-rails ruby

我有两个变量的时间段,从start_dateend_date。有没有一种简单的方法可以将它分成更小的时间段,将值四舍五入。

以下是一个例子:

我有从15.01.201610.06.2016的句点。我想把它分成几个月,所以我将有六个时期:

01.01.2016 - 31.01.2016  
01.02.2016 - 31.02.2016   
01.03.2016 - 31.03.2016  
01.04.2016 - 31.04.2016  
01.05.2016 - 31.05.2016  
01.06.2016 - 31.06.2016 

我希望将01.01.201615.01.2016之间的时间包括在内,不管它是否在原始时期内。

我一直在寻找一些想法,但此时似乎唯一的方法是使用开始日期并使用DateAndTime :: Calculations迭代来确定间隔的边界,直到达到结束日期。 / p>

3 个答案:

答案 0 :(得分:4)

from = Date.parse('15.01.2016')
to   = Date.parse('10.06.2016')
(from..to).group_by(&:month).map do |group|
  group.last.first.beginning_of_month..group.last.last.end_of_month
end
# => [Fri, 01 Jan 2016..Sun, 31 Jan 2016,
#  Mon, 01 Feb 2016..Mon, 29 Feb 2016,
#  Tue, 01 Mar 2016..Thu, 31 Mar 2016,
#  Fri, 01 Apr 2016..Sat, 30 Apr 2016,
#  Sun, 01 May 2016..Tue, 31 May 2016,
#  Wed, 01 Jun 2016..Thu, 30 Jun 2016]

或者,如果需要字符串表示,请将日期映射到字符串:

(from..to).group_by(&:month).map do |group|
  "#{group.last.first.beginning_of_month} - #{group.last.last.end_of_month}"
end

# => ["2016-01-01 - 2016-01-31",
#     "2016-02-01 - 2016-02-29",
#     "2016-03-01 - 2016-03-31",
#     "2016-04-01 - 2016-04-30",
#     "2016-05-01 - 2016-05-31",
#     "2016-06-01 - 2016-06-30"]

为了准确获得您想要的内容,您可以格式化日期的字符串表示形式:

(from..to).group_by(&:month).map do |group|
  "#{group.last.first.beginning_of_month.strftime('%d-%m-%Y')} - #{group.last.last.end_of_month.strftime('%d-%m-%Y')}"
end
#=> ["01-01-2016 - 31-01-2016",
#    "01-02-2016 - 29-02-2016",
#    "01-03-2016 - 31-03-2016",
#    "01-04-2016 - 30-04-2016",
#    "01-05-2016 - 31-05-2016",
#    "01-06-2016 - 30-06-2016"]

修改

为了确保范围不会因年份不同而混淆,请将其与月份一起分组:

from = Date.parse('15.01.2016')
to   = Date.parse('10.02.2017')
(from..to).group_by {|a| [a.year, a.month]}.map do |group|
  "#{group.last.first.beginning_of_month.strftime('%d-%m-%Y')} - #{group.last.last.end_of_month.strftime('%d-%m-%Y')}"
end
# => ["01-01-2016 - 31-01-2016",
#  "01-02-2016 - 29-02-2016",
#  "01-03-2016 - 31-03-2016",
#  "01-04-2016 - 30-04-2016",
#  "01-05-2016 - 31-05-2016",
#  "01-06-2016 - 30-06-2016",
#  "01-07-2016 - 31-07-2016",
#  "01-08-2016 - 31-08-2016",
#  "01-09-2016 - 30-09-2016",
#  "01-10-2016 - 31-10-2016",
#  "01-11-2016 - 30-11-2016",
#  "01-12-2016 - 31-12-2016",
#  "01-01-2017 - 31-01-2017",
#  "01-02-2017 - 28-02-2017"]

答案 1 :(得分:1)

这种方法避免了在日期范围内每天检查并返回所需格式的字符串数组的需要。

Date#>>将日期向前移动参数给出的月数。在这里,我将日期转移了一个月(例如Date.parse("14-01-2016") >> 1 #=> #<Date: 2016-02-14...>)。如果下个月的最后一天大于给定月份的最后一天,则会返回下个月的最后一天(例如Date.parse("30-01-2016") >> 1 #=> #<Date: 2016-02-29...>)。

<强>代码

require 'datetime'

def convert(sdate, edate)
  dfirst  = Date.strptime(sdate, "%d.%m.%Y")
  dlast   = Date.strptime(edate, "%d.%m.%Y")
  nmonths = 12*dlast.year + dlast.month - 12*dfirst.year - dfirst.month + 1
  dlast   = dfirst.end_of_month
  nmonths.times.map { |i|       
    "%s - %s" % [(dfirst >> i).strftime("01.%m.%Y"), (dlast >> i).strftime("%d.%m.%Y")]}
end

示例

convert "15.01.2016", "10.06.2016"
  #=> ["01.01.2016 - 31.01.2016", "01.02.2016 - 29.02.2016",
  #    "01.03.2016 - 31.03.2016", "01.04.2016 - 30.04.2016",
  #    "01.05.2016 - 31.05.2016", "01.06.2016 - 30.06.2016"]

<强>解释

步骤如下。

sdate = "15.01.2016"
edate = "10.06.2016"

dfirst  = Date.strptime(sdate, "%d.%m.%Y")
  #=> #<Date: 2016-01-15 ((2457403j,0s,0n),+0s,2299161j)> 
dlast   = Date.strptime(edate, "%d.%m.%Y")
  #=> #<Date: 2016-06-10 ((2457550j,0s,0n),+0s,2299161j)> 
nmonths = 12*dlast.year + dlast.month - 12*dfirst.year - dfirst.month + 1
  #=>       12*24192    +     6       -   12*24192     -      1       + 1
  #=> 6 

dlast更改为第一个月的最后一天。

dlast   = d.first.end_of_month
  #=> #<Date: 2016-01-31 ((2457419j,0s,0n),+0s,2299161j)>

将每个月映射到所需的格式。

nmonths.times.map { |i|       
  "%s - %s" % [(dfirst >> i).strftime("01.%m.%Y"), (dlast >> i).strftime("%d.%m.%Y")]}
  #=> ["01.01.2016 - 31.01.2016", "01.02.2016 - 29.02.2016",
  #    "01.03.2016 - 31.03.2016", "01.04.2016 - 30.04.2016",
  #    "01.05.2016 - 31.05.2016", "01.06.2016 - 30.06.2016"]

答案 2 :(得分:0)

如本主题所述:Is there a standard for inclusive/exclusive ends of time intervals? 我们应该更喜欢包含开始日期和不包含结束日期。

我们可以使用以下尾递归方法将 DateRange 拆分为天、周、月或年:

  class DateRange
    def initialize(since_date, until_date)
      @since_date = since_date
      @until_date = until_date
    end

    def split_to(timespan)
      return [] if @since_date == @until_date
      end_date = [@since_date.send("next_#{timespan.to_s.singularize}"), @until_date].min
      [DateRange.new(@since_date, end_date)] + DateRange.new(end_date, @until_date).split_to(timespan)
    end
  end

此方法以 :days:weeks:months:years 作为参数。请注意,我们依赖于 activesupport。