Ruby Time.now and Other dates - ruby-on-rails

I need to get three dates in variables
Today Since Midnight which I have as
t = Time.now.strftime("%Y-%m-%d 00:00:01") # 2012-11-19 00:00:01
Yesterday Since Midnight (i.e. 00:00:00 to 23:59:59)
y1 = 2012-11-18 00:00:01
y2 = 2012-11-19 23:59:59
it is specifically the y1 & y2 variables I need to create as strings for use in a gem. being new to ruby I am a little confused as Time.yesterday doesn't seem to do what I need
EDIT
For this be sure to include
require 'active_support/all'
and ensure the gem is bundled for your application.
Used:
#current = (Time.now).beginning_of_day.utc.strftime("%Y-%m-%d")
#yesterday = (Time.now - 1.day).beginning_of_day.utc.strftime("%Y-%m-%d")
#everything = (Time.now - 2.day).end_of_day.utc.strftime("%Y-%m-%d")

You can do,
t=Time.now
y1=t-1.day
y2=t+1.day

t = Time.now
t - 1.day # => yesterday
t + 1.day # => tomorrow

convert t to date first,
t = t.to_date
t - 1.day # => yesterday
t + 1.day # => tomorrow

Since you are running Rails there's help to get from ActiveSupport
1.day.ago.midnight
Time.new.beginning_of_day
You should also consider not using 2012-11-19 00:00:01 or 2012-11-19 23:59:59.
Instead you could use 00:00:00 and compare with >= or < depending on what you want to achieve. If you always round to seconds then 2012-11-19 00:00:00 to 2012-11-19 23:59:59 would work.

Related

How to get the future nearest time in ruby

Suppose we have current time and then add the 60 munites to the current time.
Time.now + 1.hours
# => 2022-11-01 16:47:02.965861 +0500
after that we get the next half hour like 17:00:00
I'm able to get the previous half hour time from this code, but unable to find the next half hour time.
time = Time.now - 30.minutes
# => 2022-11-01 15:22:59.942013 +0500
Time.at((time.to_time.to_i/1800).round * 1800).to_datetime
# => Tue, 01 Nov 2022 15:00:00 +0500
If I understand you correctly, you want to map:
15:00-15:29 to 17:00
15:30-15:59 to 17:30
You could do so with a conditional and advance and change:
t = Time.current
if t.min < 30
t.advance(hours: 2).change(min: 0)
else
t.advance(hours: 1).change(min: 30)
end
From what I understand you want to round up the time to the next 30 min mark i.e if it is between 5:01 and 5:29 you want to make it 5:30
For that you can just do
time = Time.now + 1.hours
ceil_minutes = 30.minutes
Time.at((time.to_f / ceil_minutes).ceil * ceil_minutes)

Number of hours between two dates - Ruby

Say I want the difference between tomorrow and now (in hours).
What I've tried:
t = (DateTime.tomorrow - DateTime.now)
(t / 3600).to_i
=> 0
Why does it give 0?
What am I doing wrong?
This is because DateTime.tomorrow does not have any time value. Here:
DateTime.tomorrow
# => Wed, 22 Apr 2015
If you go through official document for DateTime you can see there is no method tomorrow. Its basically Date#tomorrow.
You can use .to_time to get default localtime 00:00:00
DateTime.tomorrow.to_time
# => 2015-04-22 00:00:00 +0530
(DateTime.tomorrow.to_time - DateTime.now) / 1.hours
# => 9.008116581638655
To get exact hour difference between dates:
(DateTime.tomorrow.to_time - Date.today.to_time) / 1.hours
# => 24.0
Try this
t = (DateTime.tomorrow.to_time - Date.today.to_time)
t = (t / 3600).to_i
It returns rational number. You can take days number if you'll use round method:
>> (DateTime.tomorrow - DateTime.now).round
1
Or if you want to take value in hours from now, use Time class:
>> (Date.tomorrow.to_time - Time.now) / 1.hour
11.119436663611111
if you have two dates like
start_time = Time.new(2015,1, 22, 35, 0)
end_time = Time.new(2015,2, 22, 55, 0)
Try Time Difference gem for Ruby at https://rubygems.org/gems/time_difference
def timediff(start, end)
TimeDifference.between(start, end).in_hours
end
and call it like:
timediff(start_time, end_time)
It will work.
Cheers!
There's DateTime#seconds_until_end_of_day:
seconds = DateTime.now.seconds_until_end_of_day
#=> 41133
seconds / 3600
#=> 11
distance_of_time_in_words(seconds)
=> "about 11 hours"

Ruby Year-Month-Date of last Sunday

I need to calculate in the format of 2014-01-20 the last two Sundays.
So, last_sunday = 2014-01-20 and most_recent_sunday = 2014-01-26.
I am using Rails 4.0 and Ruby 2.0. How can I get these dates?
In rails 4:
most_recent_sunday = Time.now.sunday.to_s
last_sunday = Time.now.last_week.sunday.to_s
To get it to the format you are after:
DateTime.parse(most_recent_sunday).strftime("%Y-%m-%d")
http://apidock.com/rails/v4.0.2/DateAndTime/Calculations/sunday
http://apidock.com/rails/v4.0.2/DateAndTime/Calculations/last_week
I suggest you take a look at the chronic gem
require 'chronic'
Chronic.parse("last sunday")
=> 2014-01-26 12:00:00 +0200
You can also use rail's active_support to subtract 7.days from the date computed above =)
[11] pry(main)> last_sunday = Time.now - (Time.now.wday + 7) * 86400
=> 2014-01-19 17:17:19 -0600
[12] pry(main)> most_recent_sunday = Time.now - (Time.now.wday) * 86400
=> 2014-01-26 17:18:03 -0600
A pure Ruby solution:
require 'date'
last_two_weeks = (Date.today-13)..Date.today
last_two_sundays = last_two_weeks.select &:sunday?
puts last_two_sundays #=> 2014-01-19, 2014-01-26

Ruby / Rails - Change the timezone of a Time, without changing the value

I have a record foo in the database which has :start_time and :timezone attributes.
The :start_time is a Time in UTC - 2001-01-01 14:20:00, for example.
The :timezone is a string - America/New_York, for example.
I want to create a new Time object with the value of :start_time but whose timezone is specified by :timezone. I do not want to load the :start_time and then convert to :timezone, because Rails will be clever and update the time from UTC to be consistent with that timezone.
Currently,
t = foo.start_time
=> 2000-01-01 14:20:00 UTC
t.zone
=> "UTC"
t.in_time_zone("America/New_York")
=> Sat, 01 Jan 2000 09:20:00 EST -05:00
Instead, I want to see
=> Sat, 01 Jan 2000 14:20:00 EST -05:00
ie. I want to do:
t
=> 2000-01-01 14:20:00 UTC
t.zone = "America/New_York"
=> "America/New_York"
t
=> 2000-01-01 14:20:00 EST
Sounds like you want something along the lines of
ActiveSupport::TimeZone.new('America/New_York').local_to_utc(t)
This says convert this local time (using the zone) to utc. If you have Time.zone set then you can of course to
Time.zone.local_to_utc(t)
This won't use the timezone attached to t - it assumes that it's local to the time zone you are converting from.
One edge case to guard against here is DST transitions: the local time you specify may not exist or may be ambiguous.
I've just faced the same problem and here is what I'm going to do:
t = t.asctime.in_time_zone("America/New_York")
Here is the documentation on asctime
If you're using Rails, here is another method along the lines of Eric Walsh's answer:
def set_in_timezone(time, zone)
Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) }
end
You need to add the time offset to your time after you convert it.
The easiest way to do this is:
t = Foo.start_time.in_time_zone("America/New_York")
t -= t.utc_offset
I am not sure why you would want to do this, though it is probably best to actually work with times the way they are built. I guess some background on why you need to shift time and timezones would be helpful.
Actually, I think you need to subtract the offset after you convert it, as in:
1.9.3p194 :042 > utc_time = Time.now.utc
=> 2013-05-29 16:37:36 UTC
1.9.3p194 :043 > local_time = utc_time.in_time_zone('America/New_York')
=> Wed, 29 May 2013 12:37:36 EDT -04:00
1.9.3p194 :044 > desired_time = local_time-local_time.utc_offset
=> Wed, 29 May 2013 16:37:36 EDT -04:00
Depends on where you are going to use this Time.
When your time is an attribute
If time is used as an attribute, you can use the same date_time_attribute gem:
class Task
include DateTimeAttribute
date_time_attribute :due_at
end
task = Task.new
task.due_at_time_zone = 'Moscow'
task.due_at # => Mon, 03 Feb 2013 22:00:00 MSK +04:00
task.due_at_time_zone = 'London'
task.due_at # => Mon, 03 Feb 2013 22:00:00 GMT +00:00
When you set a separate variable
Use the same date_time_attribute gem:
my_date_time = DateTimeAttribute::Container.new(Time.zone.now)
my_date_time.date_time # => 2001-02-03 22:00:00 KRAT +0700
my_date_time.time_zone = 'Moscow'
my_date_time.date_time # => 2001-02-03 22:00:00 MSK +0400
Here's another version that worked better for me than the current answers:
now = Time.now
# => 2020-04-15 12:07:10 +0200
now.strftime("%F %T.%N").in_time_zone("Europe/London")
# => Wed, 15 Apr 2020 12:07:10 BST +01:00
It carries over nanoseconds using "%N". If you desire another precision, see this strftime reference.
The question's about Rails but it seems, like me, not everyone here is on the ActiveSupport train, so yet another option:
irb(main):001:0> require "time"
=> true
irb(main):003:0> require "tzinfo"
=> true
irb(main):004:0> t = Time.parse("2000-01-01 14:20:00 UTC")
=> 2000-01-01 14:20:00 UTC
irb(main):005:0> tz = TZInfo::Timezone.get("America/New_York")
=> #<TZInfo::DataTimezone: America/New_York>
irb(main):008:0> utc = tz.local_to_utc(t)
=> 2000-01-01 19:20:00 UTC
irb(main):009:0> tz.utc_to_local(utc)
=> 2000-01-01 14:20:00 -0500
irb(main):010:0>
local_to_utc not doing the opposite of utc_to_local might look like a bug but it is at least documented: https://github.com/tzinfo/tzinfo says:
The offset of the time is ignored - it is treated as if it were a local time for the time zone
I managed to do this by calling change with the desired time zone:
>> t = Time.current.in_time_zone('America/New_York')
=> Mon, 08 Aug 2022 12:04:36.934007000 EDT -04:00
>> t.change(zone: 'Etc/UTC')
=> Mon, 08 Aug 2022 12:04:36.934007000 UTC +00:00
https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html#method-i-change
def relative_time_in_time_zone(time, zone)
DateTime.parse(time.strftime("%d %b %Y %H:%M:%S #{time.in_time_zone(zone).formatted_offset}"))
end
Quick little function I came up with to solve the job. If someone has a more efficient way of doing this please post it!
I spent significant time struggling with TimeZones as well, and after tinkering with Ruby 1.9.3 realized that you don't need to convert to a named timezone symbol before converting:
my_time = Time.now
west_coast_time = my_time.in_time_zone(-8) # Pacific Standard Time
east_coast_time = my_time.in_time_zone(-5) # Eastern Standard Time
What this implies is that you can focus on getting the appropriate time setup first in the region you want, the way you would think about it (at least in my head I partition it this way), and then convert at the end to the zone you want to verify your business logic with.
This also works for Ruby 2.3.1.
I have created few helper methods one of which just does the same thing as is asked by the original author of the post at Ruby / Rails - Change the timezone of a Time, without changing the value.
Also I have documented few peculiarities I observed and also these helpers contains methods to completely ignore automatic day-light savings applicable while time-conversions which is not available out-of-the-box in Rails framework:
def utc_offset_of_given_time(time, ignore_dst: false)
# Correcting the utc_offset below
utc_offset = time.utc_offset
if !!ignore_dst && time.dst?
utc_offset_ignoring_dst = utc_offset - 3600 # 3600 seconds = 1 hour
utc_offset = utc_offset_ignoring_dst
end
utc_offset
end
def utc_offset_of_given_time_ignoring_dst(time)
utc_offset_of_given_time(time, ignore_dst: true)
end
def change_offset_in_given_time_to_given_utc_offset(time, utc_offset)
formatted_utc_offset = ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, false)
# change method accepts :offset option only on DateTime instances.
# and also offset option works only when given formatted utc_offset
# like -0500. If giving it number of seconds like -18000 it is not
# taken into account. This is not mentioned clearly in the documentation
# , though.
# Hence the conversion to DateTime instance first using to_datetime.
datetime_with_changed_offset = time.to_datetime.change(offset: formatted_utc_offset)
Time.parse(datetime_with_changed_offset.to_s)
end
def ignore_dst_in_given_time(time)
return time unless time.dst?
utc_offset = time.utc_offset
if utc_offset < 0
dst_ignored_time = time - 1.hour
elsif utc_offset > 0
dst_ignored_time = time + 1.hour
end
utc_offset_ignoring_dst = utc_offset_of_given_time_ignoring_dst(time)
dst_ignored_time_with_corrected_offset =
change_offset_in_given_time_to_given_utc_offset(dst_ignored_time, utc_offset_ignoring_dst)
# A special case for time in timezones observing DST and which are
# ahead of UTC. For e.g. Tehran city whose timezone is Iran Standard Time
# and which observes DST and which is UTC +03:30. But when DST is active
# it becomes UTC +04:30. Thus when a IRDT (Iran Daylight Saving Time)
# is given to this method say '05-04-2016 4:00pm' then this will convert
# it to '05-04-2016 5:00pm' and update its offset to +0330 which is incorrect.
# The updated UTC offset is correct but the hour should retain as 4.
if utc_offset > 0
dst_ignored_time_with_corrected_offset -= 1.hour
end
dst_ignored_time_with_corrected_offset
end
Examples which can be tried on rails console or a ruby script after wrapping the above methods in a class or module:
dd1 = '05-04-2016 4:00pm'
dd2 = '07-11-2016 4:00pm'
utc_zone = ActiveSupport::TimeZone['UTC']
est_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
tehran_zone = ActiveSupport::TimeZone['Tehran']
utc_dd1 = utc_zone.parse(dd1)
est_dd1 = est_zone.parse(dd1)
tehran_dd1 = tehran_zone.parse(dd1)
utc_dd1.dst?
est_dd1.dst?
tehran_dd1.dst?
ignore_dst = true
utc_to_est_time = utc_dd1.in_time_zone(est_zone.name)
if utc_to_est_time.dst? && !!ignore_dst
utc_to_est_time = ignore_dst_in_given_time(utc_to_est_time)
end
puts utc_to_est_time
Hope this helps.
This worked well for me
date = '23/11/2020'
time = '08:00'
h, m = time.split(':')
timezone = 'Europe/London'
date.to_datetime.in_time_zone(timezone).change(hour: h, min: m)
This changes the timezone to 'EST' without changing the time:
time = DateTime.current
Time.find_zone("EST").local(
time.year,
time.month,
time.day,
time.hour,
time.min,
time.sec,
)

Convert Ruby Date to Integer

How can I convert a Ruby Date to an integer?
t = Time.now
# => 2010-12-20 11:20:31 -0700
# Seconds since epoch
t.to_i
#=> 1292869231
require 'date'
d = Date.today
#=> #<Date: 2010-12-20 (4911101/2,0,2299161)>
epoch = Date.new(1970,1,1)
#=> #<Date: 1970-01-01 (4881175/2,0,2299161)>
d - epoch
#=> (14963/1)
# Days since epoch
(d - epoch).to_i
#=> 14963
# Seconds since epoch
d.to_time.to_i
#=> 1292828400
Date cannot directly become an integer. Ex:
$ Date.today
=> #<Date: 2017-12-29 ((2458117j,0s,0n),+0s,2299161j)>
$ Date.today.to_i
=> NoMethodError: undefined method 'to_i' for #<Date: 2017-12-29 ((2458117j,0s,0n),+0s,2299161j)>
Your options are either to turn the Date into a time then an Int which will give you the seconds since epoch:
$ Date.today.to_time.to_i
=> 1514523600
Or come up with some other number you want like days since epoch:
$ Date.today.to_time.to_i / (60 * 60 * 24) ### Number of seconds in a day
=> 17529 ### Number of days since epoch
Time.now.to_i
returns seconds since epoch format
Solution for Ruby 1.8 when you have an arbitrary DateTime object:
1.8.7-p374 :001 > require 'date'
=> true
1.8.7-p374 :002 > DateTime.new(2012, 1, 15).strftime('%s')
=> "1326585600"
I had to do it recently and took some time to figure it out but that is how I came across a solution and it may give you some ideas:
require 'date'
today = Date.today
year = today.year
month = today.mon
day = day.mday
year = year.to_s
month = month.to_s
day = day.to_s
if month.length <2
month = "0" + month
end
if day.length <2
day = "0" + day
end
today = year + month + day
today = today.to_i
puts today
At the date of this post, It will put 20191205.
In case the month or day is less than 2 digits it will add a 0 on the left.
I did like this because I had to compare the current date whit some data that came from a DB in this format and as an integer. I hope it helps you.

Resources