需要举例说明如何计算按月划分的时间段内的天数。 例如:
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)"]