按月计算期间天数的最佳方法



需要举例说明如何计算按月划分的时间段内的天数。 例如:

Wed, 25 Nov 2020 : Tue, 15 Dec 2020 => [6 (nov), 15(dec)]

谢谢!

这将是tally_by的工作,但这还没有添加到Ruby中(还没有? 理货也有效:

require 'date'
range = Date.parse("Wed, 25 Nov 2020") .. Date.parse("Tue, 15 Dec 2020")
p month_counts = range.map{|d| Date::ABBR_MONTHNAMES[d.month] }.tally
# => {"Nov"=>6, "Dec"=>15}
date1 = Date.new(2020, 11, 25)
date2 = Date.new(2020, 12, 15)
(date1..date2).group_by { |date| [date.year, date.month] }
.map { |(year, month), dates| ["#{year}/#{month}", dates.length] }
=> [["2020/11", 6], ["2020/12", 15]] 

间隔这么长,你月份相同但年份不同怎么办?由于这种情况,我增加了年份。

这也适用于纯红宝石,您只需要require 'date'

代码

require 'date'
def count_days_by_month(str)
Range.new(*str.split(/ +: +/).
map { |s| Date.strptime(s, '%a, %d %b %Y') }).
slice_when { |d1,d2| d1.month != d2.month }.
with_object({}) do |a,h|
day1 = a.first
h[[day1.year, Date::ABBR_MONTHNAMES[day1.month]]] = a.size
end
end

请参阅 Range::new、Date::strptime 和 Enumerable#slice_when。

例子

count_days_by_month "Wed, 25 Nov 2020 : Tue, 15 Dec 2020"
#=> {[2020, "Nov"]=>6, [2020, "Dec"]=>15}
count_days_by_month "Wed, 25 Nov 2020 : Tue, 15 Dec 2021"
#=> {[2020, "Nov"]=>6, [2020, "Dec"]=>31, [2021, "Jan"]=>31,
#    ...
#    [2021, "Nov"]=>30, [2021, "Dec"]=>15} 

解释

对于第一个示例,步骤如下。

str = "Wed, 25 Nov 2020 : Tue, 15 Dec 2020"
b = str.split(/ +: +/)
#=> ["Wed, 25 Nov 2020", "Tue, 15 Dec 2020"] 
c = b.map { |s| Date.strptime(s, '%a, %d %b %Y') }
#=> [#<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)>,
#    #<Date: 2020-12-15 ((2459199j,0s,0n),+0s,2299161j)>] 
d = Range.new(*c)
#=> #<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)>..
#   #<Date: 2020-12-15 ((2459199j,0s,0n),+0s,2299161j)> 
e = d.slice_when { |d1,d2| d1.month != d2.month }
#=> #<Enumerator: #<Enumerator::Generator:0x00007fb1058abb10>:each> 

我们可以通过将此枚举器转换为数组来查看该枚举器生成的元素。

e.to_a
#=> [[#<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)>,
#     #<Date: 2020-11-26 ((2459180j,0s,0n),+0s,2299161j)>,
#     ...
#     #<Date: 2020-11-30 ((2459184j,0s,0n),+0s,2299161j)>],
#    [#<Date: 2020-12-01 ((2459185j,0s,0n),+0s,2299161j)>,
#     #<Date: 2020-12-02 ((2459186j,0s,0n),+0s,2299161j)>,
#     ...
#     #<Date: 2020-12-15 ((2459199j,0s,0n),+0s,2299161j)>]] 

继续

f = e.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator::Generator:0x00007fb1058abb10>
#     :each>:with_object({})>
f.each do |a,h| 
day1 = a.first
h[[day1.year, Date::ABBR_MONTHNAMES[day1.month]]] = a.size 
end
#=> {[2020, "Nov"]=>6, [2020, "Dec"]=>15}

f生成并传递给块的第一个元素,块变量通过数组分解规则赋值:

a,h = f.next
#=> [[#<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)>,
#     #<Date: 2020-11-26 ((2459180j,0s,0n),+0s,2299161j)>,
#     ...
#     #<Date: 2020-11-30 ((2459184j,0s,0n),+0s,2299161j)>],
#     {}]
a #=> [#<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)>,
#    #<Date: 2020-11-26 ((2459180j,0s,0n),+0s,2299161j)>,
#    ...
#    #<Date: 2020-11-30 ((2459184j,0s,0n),+0s,2299161j)>],
h #=> {}

键值对将在计算过程中添加到h。请参阅枚举器#下一个。现在执行块计算。

day1 = a.first
#=> #<Date: 2020-11-25 ((2459179j,0s,0n),+0s,2299161j)> 
g = day1.year
#=> 2020 
i = day1.month
#=> 11 
j = Date::ABBR_MONTHNAMES[day1.month]
#=> "Nov" 
k = a.size
#=> 6 
h[[g,j]] = k
#=> 6 

导致:

h #=> {[2020, "Nov"]=>6}

其余步骤类似。

我会从将整个时期分成每个月的各个时期开始。 由于 Ruby 有范围,我会编写一个帮助程序方法,该方法采用日期范围并生成月份范围:

def each_month(range)
return enum_for(__method__, range) unless block_given?
date = range.begin.beginning_of_month
loop do
from = date.clamp(range)
to   = (date.end_of_month).clamp(range)
yield from..to
date = date.next_month
break unless range.cover?(date)
end
end

clamp可确保在计算每个月的范围时考虑范围的范围。对于 2.7 之前的 Ruby 版本,您必须单独传递边界:

from = date.clamp(range.begin, range.end)
to   = (date.end_of_month).clamp(range.begin, range.end)

用法示例:

from = '25 Nov 2020'.to_date
to   = '13 Jan 2021'.to_date
each_month(from..to).to_a
#=> [
#     Wed, 25 Nov 2020..Mon, 30 Nov 2020
#     Tue, 01 Dec 2020..Thu, 31 Dec 2020
#     Fri, 01 Jan 2021..Wed, 13 Jan 2021
#   ]

现在我们所需要的只是一种计算每个月范围内的天数的方法:(例如通过jd)

def days(range)
range.end.jd - range.begin.jd + 1
end

和一些格式:

each_month(from..to).map { |r| format('%d (%s)', days(r), r.begin.strftime('%b')) }
#=> ["6 (Nov)", "31 (Dec)", "13 (Jan)"]

相关内容

最新更新