Need to example how to calculate the count of days in a period splited by month.
For example:
Wed, 25 Nov 2020 : Tue, 15 Dec 2020 => [6 (nov), 15(dec)]
Thank you!
This would be a job for tally_by, but that is not added to Ruby (yet?).
tally works too:
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]]
What about the interval is so long that you have same months but of different years? I've added years because of this case.
This works in pure ruby too, you just need require 'date'
Code
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
See Range::new, Date::strptime and Enumerable#slice_when.
Examples
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}
Explanation
For the first example the steps are as follows.
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>
We can see the elements generated by this enumerator by converting it to an array.
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)>]]
Continuing,
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}
The first element generated by f and passed to the block, and the block variables are assign values by the rules of array decomposition:
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 #=> {}
Key-value pairs will be added to h over the course of the calculations. See Enumerator#next. The block calculation is now performed.
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
resulting in:
h #=> {[2020, "Nov"]=>6}
The remaining steps are similar.
I would start by breaking the whole period into periods for each month.
Since Ruby has ranges, I'd write a helper method that takes a date range and yields month-ranges:
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 ensures that the range's bounds are taken into account when calculating each month's range. For Ruby version prior to 2.7 you have to pass the bounds separately:
from = date.clamp(range.begin, range.end)
to = (date.end_of_month).clamp(range.begin, range.end)
Example usage:
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
# ]
Now all we need is a way to count the days in each month-range: (e.g. via jd)
def days(range)
range.end.jd - range.begin.jd + 1
end
and some formatting:
each_month(from..to).map { |r| format('%d (%s)', days(r), r.begin.strftime('%b')) }
#=> ["6 (Nov)", "31 (Dec)", "13 (Jan)"]
In rails console,
> time1 = DateTime.now
=> Thu, 23 Feb 2017 10:50:27 +0630
> time2 = "#{time1}"
=> "2017-02-23T10:50:27+06:30"
> time1 == time2
=> false
This is right. Because
> time1.class
=> DateTime
> time2.class
=> String
So I change time2 to datetime.
> time2.to_datetime.class
=> DateTime
> time2.to_datetime
=> Thu, 23 Feb 2017 10:50:27 +0630
And try to compare those time again.
> time1 == time2.to_datetime
=> false
> time1
=> Thu, 23 Feb 2017 10:50:27 +0630
> time2.to_datetime
=> Thu, 23 Feb 2017 10:50:27 +0630
The result is false.
Time 1 is datetime object while time 2 is string type object
Here you have to convert time2 to datetime
And to compare two date you will have to convert both in integer using following way
ยป time1.to_i == time2.to_datetime.to_i
=> true
It's better to compare two strings or two integers instead of two DateTime objects because DateTime holds information such as time zone etc.
time1.to_s == time2
#=> true
False because we had different minutes:
> time1 == time2.to_datetime
=> false
> time1
=> Thu, 23 Feb 2017 10:37:56 +0630
> time2.to_datetime
=> Thu, 23 Feb 2017 10:26:18 +0630
10:37:56 +0630 is greater than 10:26:18 +0630
So, if you compare as:
time1 > time2.to_datetime
=> true
> time1.to_i == time2.to_datetime.to_i
=> true
This works. But not an elegant solution.
You can try:
DateTime.strptime(time1, "%Y-%m-%dT%H:%M") == DateTime.strptime(time2.to_datetime, "%Y-%m-%dT%H:%M")
=>> true
I'm having some trouble with making a timestamp from a date and a time
What i'm trying to do:
date = "2016 2 21"
time = "03:00 UTC"
output = "Thu, 21 Feb 2016 03:00:00 UTC +00:00"
I'm getting the date from a form_for:
f.date_field(:date_first)
But I'm not sure of how should I pick up the time.
To give you a headstart:
dt = "2016-2-21"
time = "03:00 UTC"
dtime = DateTime.parse(dt + 'T' + time)
output = dtime.rfc2822
and the result is:
#=> "Sun, 21 Feb 2016 03:00:00 +0000"
Maybe you could do something like this
strftime("%d.%m.%Y. %H:%M:%S")
and also get your locale yml file from here
https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale
I need to convert the following raw string (date range) into ruby datetime datetype.
How to finish it on Rails ?
raw string
"2014 April/July 24-1"
convert to ruby datetime variable
start_date = 2014-04-24
end_date = 2014-07-01
raw string
"2015 April 06-20"
convert to ruby datetime variable
start_date = 2015-04-06
end_date = 2015-04-20
This may help
# In order to generate
# year = 2014
# months = "April/July"
# days = "24-1"
/(?<year>\d{4})\s*(?<months>\w+\/\w+)\s*(?<days>\d{1,2}\-\d{1,2})/ =~ "2014 April/July 24-1"
date1 = "#{year} #{months.split('/')[0]} #{days.split('-')[0]}"
date2 = "#{year} #{months.split('/')[1]} #{days.split('-')[1]}"
start_date = Date.strptime(date1, "%Y %b %d") #=> Thu, 24 Apr 2014
end_date = Date.strptime(date2, "%Y %b %d") #=> Tue, 01 Jul 2014
I'm trying to parse a string to DateTime and i'm experiencing an error when I try tp drop the year:
undefined method `year' for "Monday, Aug 25, 10:30":String
Controller
dates = []
temps = []
dt = []
#data['data'].flatten.each do |data|
dates << data.keys
temps << data.values
end
dates.flatten.each do |date|
dt << DateTime.parse(date).strftime("%A, %b %d, %H:%M")
end
json
{"status": "ok", "data": [{"2014-08-25 10:30:00": 12.6}]}
Your example isn't clear because you're not showing your use of the year method. But, note that you've left out %y from your strftime string. I would recommend playing around in rails console to get the methods you're looking for. For example:
[1] pry(main)> DateTime.parse("2014-08-25 10:30:00")
Mon, 25 Aug 2014 10:30:00 +0000
[2] pry(main)> DateTime.parse("2014-08-25 10:30:00").year
2014
[3] pry(main)> DateTime.parse("2014-08-25 10:30:00").strftime("%A, %b %d %y, %H:%M")
"Monday, Aug 25 14, 10:30"
[4] pry(main)> DateTime.parse("2014-08-25 10:30:00").strftime("%A, %b %d %y, %H:%M").year
NoMethodError: undefined method `year' for "Monday, Aug 25 14, 10:30":String
from (pry):4:in `<main>'