Is there a way to dehumanize a duration string as follows to get the actual utc timestamp?
For example: "1.hour.ago 25.minute.ago 1.second.ago" convert back Fri, 18 Oct 2019 16:09:10 UTC +00:00
the site info has it as "17h 21m 1s ago" need to convert it to db datetime utc i manage to do this:
"17h 21m 1s ago".gsub("h", '.hour.ago').gsub("m", '.minute.ago').gsub("s ago", '.second.ago')
# ==> "17.hour.ago 21.minute.ago 1.second.ago"
But how to remove the string portion and convert to method datetime? Trying datetime parse did not work.
Assuming the "17h 21m 1s ago" format is what you're working with, then you can use Time#advance to get the job done:
def when_was(string)
time = Time.now.utc
string.split(/\s+/).each do |part|
case (part)
when /\A(\d+)h\z/
time = time.advance(hours: -$1.to_i)
when /\A(\d+)m\z/
time = time.advance(minutes: -$1.to_i)
when /\A(\d+)s\z/
time = time.advance(seconds: -$1.to_i)
end
end
time
end
p when_was("17h 21m 1s ago")
# => 2019-10-18 01:31:36 UTC
You can also do this with Time.now.utc - 17.hours - 21.minutes - 1.second but that involves dynamic dispatching with send so it's more messy.
A prepackaged solution using chronic_duration gem:
require 'chronic_duration'
time_in_seconds = ChronicDuration.parse('17h 21m 1s ago')
p Time.now - time_in_seconds # => 2019-10-18 03:50:47 +0200
It can understand m, min, etc out of the box.
I assume that the hours, minutes and seconds are sequential.
time_in_string = "1.hour.ago 25.minute.ago 1.second.ago"
time_info = time_in_string.scan(/\d+/).map(&:to_i)
#=> [1, 25, 1]
time_in_seconds = time_info.zip([3600, 60, 1]).sum{ |x, y| x * y }
=> 5101
Time.now
#=> 2019-10-18 22:56:14 +0300
Time.now - time_in_seconds.seconds
#=> 2019-10-18 21:31:13 +0300
As a method:
def time_from_string(time_in_string)
time_info = time_in_string.scan(/\d+/).map(&:to_i)
time_in_seconds = time_info.zip([3600, 60, 1]).sum{ |x, y| x * y }
Time.now - time_in_seconds.seconds
end
p time_from_string(time_in_string)
#=> 2019-10-18 21:37:10 +0300
Related
I am trying to write a method that takes todays date and if the provided date falls in between DST dates for 2018 (DST is true if date is 03/11/18 2:00am and 11/04/18 2:00am), then it would return the date of the next respective DST change.
I actually have no idea how to approach this other than taking the provided date and writing a case statement around it that iterates through the provided date's given year. and each when holds a different year
# method to take todays date and if the date falls in between DST dates,
# then the method will return the date of the next DST change
def dst_date_change(date)
return case
when date.include? ='2018'
if (date > Time.parse('March 11, 2018 2:00am') && (date < Time.parse('November 4, 2018 2:00am'))
end
when date.include? ='2019'
if
end
when date.include? ='2020'
if
end
when date.include? ='2020'
if
end
else
this is what i currently have. clearly unfinished..
I have interpreted the question as follows: "Given a date (possibly today's date), if at no time on that day it is DST, return nil. Otherwise, return the next date (possibly the same date) that begins as DST and ends as non-DST".
require 'date'
def next_dst_date_change(base_date)
base_date_time = base_date.to_time
next_date_time = (base_date+1).to_time
if base_date_time.dst? || next_date_time.dst?
base_date + 1.step.find { |n| (base_date + n).to_time.dst? == false } - 1
end
end
See Date#to_time, Time#dst?, Numeric#step and Enumerable#find.
Examples
d0 = Date.today
#=> #<Date: 2018-09-24 ((2458386j,0s,0n),+0s,2299161j)>
next_dst_date_change(d0)
#=> #<Date: 2018-11-04 ((2458427j,0s,0n),+0s,2299161j)>
d1 = Date.new(2018, 11, 04)
#=> #<Date: 2018-11-04 ((2458427j,0s,0n),+0s,2299161j)>
next_dst_date_change(d0)
#=> #<Date: 2018-11-04 ((2458427j,0s,0n),+0s,2299161j)>
d2 = d0 + 90
#=> #<Date: 2018-12-23 ((2458476j,0s,0n),+0s,2299161j)>
next_dst_date_change(d2)
#=> nil
d3 = d0 + 365
#=> #<Date: 2019-09-24 ((2458751j,0s,0n),+0s,2299161j)>
next_dst_date_change(d3)
#=> #<Date: 2019-11-03 ((2458791j,0s,0n),+0s,2299161j)>
d4 = Date.new(2018, 3, 10)
#=> #<Date: 2018-03-10 ((2458188j,0s,0n),+0s,2299161j)>
next_dst_date_change(d4)
#=> nil
d5 = Date.new(2018, 3, 11)
next_dst_date_change(d5)
#=> #<Date: 2018-11-04 ((2458427j,0s,0n),+0s,2299161j)>
To speed things up one could do a binary search to determine the next DST-to-non_DST change date, using Range#bsearch. I've assumed that DST ends some time before December 1 of each year.
def next_dst_date_change(base_date)
base_date_time = base_date.to_time
next_date_time = (base_date+1).to_time
if base_date_time.dst? || next_date_time.dst?
diff = (Date.new(base_date.year, 12, 1) - base_date).to_i
base_date + (1..diff).bsearch { |n| !(base_date + n).to_time.dst? } - 1
end
end
I'm very new to Ruby and I don't understand the answer to How can i check whether the current time in between tonight 9pm and 9am(tomorrow) in Ruby on Rails.
I have a time and I want to see if it is between 9am the day before and 9am today - how do I do that?
I've tried using that answer, but I'm not sure if I did it correctly-
updated_at = #some time that I access
if (0..8).cover? updated_at.hour
a = updated_at - 1.day
else
a = updated_at
end
start = Time.new updated_at.year, updated_at.month, updated_at.day, 9, 0, 0
b = a + 1.day
stop = Time.new b.year, b.month, b.day, 9, 0, 0
puts (start..stop).cover? updated_at
yesterday_at_nine = Time.parse('9:00') - 1.day
today_at_nine = Time.parse('9:00')
now = Time.now
(yesterday_at_nine..today_at_nine).cover? now
This will determine if the current time is in between 9pm and 9am:
time = Time.now
time.hour > 20 or time.hour < 9
For example:
time = Time.now
#=> 2016-08-01 20:23:32 +0000
time.hour > 20 or time.hour < 9
#=> false
I am attempting to round UNIX timestamps in Ruby to the nearest whole month. I have the following UNIX timestamps which I'd like to convert as shown--basically if the day of the month is the 15th and onward, it should round up to the next month (e.g. February 23rd rounds up to March 1st; February 9th rounds down to February 1st).
Here are the timestamps I have and the result I need help achieving:
1455846925 (Feburary 19th, 2016) => 1456790400 (March 1st, 2016)
1447476352 (November 14th, 2015) => 1446336000 (November 1st, 2015)
1242487963 (May 16th, 2009) => 1243814400 (June 1st, 2009).
I am okay solely relying on the logic of 1-14 (round down) / 15+ (round up). I realize this won't always take into account the days in a month and I can accept that for this if needed (although a solution that always takes into account the days in a given month is a bonus).
Ruby's DateTime module may be able to do it in combination with modulo of the number of seconds in a month but I'm not quite sure how to put it all together. If I can convert the UNIX timestamp directly without first translating it to a Ruby Date, that is perfectly fine too.
Thank you in advance for your assistance.
This rounds to the nearest second.
require 'time'
def round_to_month(secs)
t1 = Time.at secs
t2 = (t1.to_datetime >> 1).to_time
s1 = Time.new(t1.year, month=t1.month)
s2 = Time.new(t2.year, month=t2.month)
(t1-s1) < (s2-t1) ? s1 : s2
end
round_to_month(1455846925) # round 2016-02-18 17:55:25 -0800
#=> 2016-03-01 00:00:00 -0800
round_to_month(1447476352) # round 2015-11-13 20:45:52 -0800
#=> 2015-11-01 00:00:00 -0700
round_to_month(1242487963) # round 2009-05-16 08:32:43 -0700
#=> 2009-05-01 00:00:00 -0700
Consider
secs = 1455846925
The calculations are as follows:
t1 = Time.at secs
#=> 2016-02-18 17:55:25 -0800
dt = t1.to_datetime
#=> #<DateTime: 2016-02-18T17:55:25-08:00 ((2457438j,6925s,0n),-28800s,2299161j)>
dt_next = dt >> 1
#=> #<DateTime: 2016-03-18T17:55:25-08:00 ((2457467j,6925s,0n),-28800s,2299161j)>
t2 = dt_next.to_time
#=> 2016-03-18 18:55:25 -0700
s1 = Time.new(t1.year, month=t1.month)
#=> Time.new(2016, month=2)
#=> 2016-02-01 00:00:00 -0800
s2 = Time.new(t2.year, month=t2.month)
# Time.new(2016, month=3)
#=> 2016-03-01 00:00:00 -0800
(t1-s1) < (s2-t1) ? s1 : s2
#=> 1533325.0 < 972275.0 ? 2016-02-18 17:55:25 -0800 : 2016-03-01 00:00:00 -0800
#=> 2016-03-01 00:00:00 -0800
It would be easy to convert it to Time object and then convert it back to timestamp
If you're using Rails, this method should do, what you want:
def nearest_month(t)
time = Time.at(t).utc
time = time.next_month if time.day >= 15
time.beginning_of_month.to_i
end
I don't know if this is as accurate as #CarySwoveland's solution, but I like it:
require 'time'
FIFTEEN_DAYS = 15 * 24 * 60 * 60
def round_to_month(secs)
t1 = Time.at(secs + FIFTEEN_DAYS)
Time.new(t1.year, t1.month)
end
p round_to_month(1455846925) # round 2016-02-18 17:55:25 -0800
# => 2016-03-01 00:00:00 -0800
p round_to_month(1447476352) # round 2015-11-13 20:45:52 -0800
# => 2015-11-01 00:00:00 -0700
p round_to_month(1242487963) # round 2009-05-16 08:32:43 -0700
# => 2009-05-01 00:00:00 -0700
If you want it to return a UNIX timestamp instead just tack .to_i onto the last line in the method.
Something like this will work if you use ActiveSupport in Rails:
require 'date'
def round_to_nearest_month(timestamp)
# Convert the unix timestamp into a Ruby DateTime object
datetime = timestamp.to_datetime
# Get the day of the month from the datetime object
day_of_month = datetime.mday
if day_of_month < 15
datetime.at_beginning_of_month
else
datetime.at_beginning_of_month.next_month
end
return datetime
end
In ruby, how can I get every 14th day of the year, going backwards and forwards from a date.
So consider I'm billed for 2 weeks of recycling on today, 6-16-2015. How can I get an array of every recycling billing day this year based on that date.
Date has a step method:
require 'date'
d = Date.strptime("6-16-2015", '%m-%d-%Y') # strange date format
end_year = Date.new(d.year, -1, -1)
p d.step(end_year, 14).to_a
# =>[#<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>, #<Date: 2015-06-30 ((2457204j,0s,0n),+0s,2299161j)>, ...
# Going backward:
begin_year = Date.new(d.year, 1, 1)
p d.step(begin_year,-14).to_a
# =>[#<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>, #<Date: 2015-06-02 ((2457176j,0s,0n),+0s,2299161j)>,...
A more descriptive and easy to understand solution:
require 'date'
current_date = Date.parse "16-june-15"
start_date = Date.parse '1-jan-15'
end_date = Date.parse '31-dec-15'
interval = 14
result = current_date.step(start_date, -interval).to_a
result.sort!.pop
result += current_date.step(end_date, interval).to_a
You could do that as follows:
require 'date'
date_str = "6-16-2015"
d = Date.strptime(date_str, '%m-%d-%Y')
f = Date.new(d.year)
((f + (f-d).abs % 14)..Date.new(d.year,-1,-1)).step(14).to_a
#=> [#<Date: 2015-01-13 ((2457036j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-01-27 ((2457050j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2015-12-29 ((2457386j,0s,0n),+0s,2299161j)>]
Based on the second sentence of your question, I assume you simply want an array of all dates in the given year that are two-weeks apart and include the given day.
I attempted a mathy modulus biased approach which turned out unexpectedly confusing.
require 'date'
a_recycle_date_string = "6-17-2015"
interval = 14
a_recycle_date = Date.strptime(a_recycle_date_string, '%m-%d-%Y')
current_year = a_recycle_date.year
end_of_year = Date.new(current_year, -1, -1)
# Find out which index of the first interval's days is the first recycle day
# of the year the (1 indexed)
remainder = (a_recycle_date.yday) % interval
# => 0
# make sure remainder 0 is treated as interval-1 so it doesn't louse
# the equation up
n_days_from_first_recycling_yday_of_year = (remainder - 1) % interval
first_recycle_date_this_year = Date.new(current_year,
1,
1 + n_days_from_first_recycling_yday_of_year)
first_recycle_date_this_year.step(end_of_year, interval).to_a
I simply want to return a less precise, truncated version of Time.now.
When I run Time.now I get a very precise object 2014-10-02 14:49:47 -0400tim. I only want
YYYY-MM-DD HH:MM:MM.
How can I (temporarily) cast the precision of a Time.now return object?
strftime() allows you to provide a format string for your time object. A list of format flags can be found in the documentation
Example from Docs:
t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
t.strftime("at %I:%M%p") #=> "at 08:37AM"
Your Example:
t.strftime("%Y-%m-%d %T") #=> YYYY-MM-DD HH:MM:MM.
You could create a new Time object that is the original time rounded to the nearest minute:
require 'time'
t = Time.now
#=> 2014-10-02 12:40:22 -0700
new_time = t + (t.sec >= 30 ? 60-t.sec : -t.sec)
#=> 2014-10-02 12:40:00 -0700
t += 30
#=> 2014-10-02 12:40:52 -0700
new_time = t + (t.sec >= 30 ? 60-t.sec : -t.sec)
#=> 2014-10-02 12:41:00 -0700