Year query returns no results - ruby-on-rails

I have a Holiday model, with a holiday_date attribute of DateTime.
I added a new Holiday (New Years Day) with a date of 1/1/2019.
When I do in the console Holiday.last, I see this:
#<Holiday id: 50, name: "New Years Day", holiday_date: "2018-12-31 23:00:00", created_at: "2018-11-13 13:15:54", updated_at: "2018-11-13 13:15:54">
So it is saved in UTC time, a day earlier. When I then do Holiday.last.holiday_date I get this:
Tue, 01 Jan 2019 00:00:00 CET +01:00
Great, the date is converted to our CET date and time. But when I query for a year like this:
Holiday.where("extract(year from holiday_date) = '2019'")
It returns no results. So it seems that there is no conversion to CET time with this query. How can I make sure that the query returns the holiday I added?

You'll have to cast timezones twice:
Holiday.where(
"extract(year from holiday_date AT TIME ZONE 'UTC' AT TIME ZONE 'CET') = '2019'"
)
This will work, but it would be nice to use indices for your query, we'll just have to prepare it better:
year = Time.zone.parse("2019-01-01")
Holiday.where("holiday_date BETWEEN ? AND ?", year.beginning_of_year, year.end_of_year)
# SELECT "holidays".* FROM "holidays" WHERE (holiday_date BETWEEN '2018-12-31 23:00:00' AND '2019-12-31 22:59:59.999999')
I would really think whether you need datetimes for your holiday_date column, perhaps dates would be enough, so that you don't have to deal with timezones.

You can query by timezone like this
Holiday.where("extract(year from holiday_date AT TIME ZONE 'CET') = '2019'")

Related

Convert timestamp string without timezone to Time object in known timezone

I have some timestamp strings (e.g. "23/06/2021 13:46") that I know to be in the UK local time (either GMT or BST depending on time of year), but they have no timezone indication as part of the string.
Within rails what is a sensible way to turn them into Time objects in the correct timezone?
I can do it in a really roundabout way:
time_string = "23/06/2021 13:46"
base_time = Time.parse(time_string)
Time.use_zone("Europe/London") do
Time.zone.now.change(year: base_time.year, month: base_time.month, day: base_time.day, hour: base_time.hour, min: base_time.min, sec: base_time.sec)
end
=> Wed, 23 Jun 2021 13:46:00 BST +01:00
But there must be a better way!
I have read loads of different sources and all seem to be about how to convert an existing time object into a different timezone, or cast a Time object to a string.
Thanks!
You can use the in_time_zone method to convert a timestamp to your server's time zone:
time_string = "23/06/2021 13:46"
base_time = Time.parse(time_string)
new_time = base_time.in_time_zone
I ended up making use of a gem called "Time of Day", it will take a string and give you a time of day object, you can then convert that to local time in your current Time.zone on any date you chose:
Time.zone = "Europe/London"
time_string = "23/06/2021 13:46"
parts = time_string.split(" ")
date = Date.parse(parts[0])
tod = Tod::TimeOfDay.parse(parts[1])
tod.on date # => Wed, 23 Jun 2021 13:46:00 BST +01:00

get time until hour in user timezone

I'm trying to get the time until 12:00 AM in the users timezone. How can I do this in rails?
I want to get the time until 12:00 AM and then add that time to the current time in rails to store it in the database because I want to have a field with the GMT time at is the equivalent to 12:00 AM in the users timezone
I'm using this gem:
gem 'time_difference', '~> 0.5.0'
In order to get the time difference between two timestamps
TimeDifference.between(DateTime.now, created_at)
But I'm not sure how to get the time until 12:00 AM in the users timezone.
Thanks
Given a particular timezone, you can use Rails' tools for dealing with timezones to just directly find out when midnight is for a particular timezone. This example supposes you have a time_zone column on your User model with an appropriate value (e.g., America/Chicago, or anything else Rails supports):
midnight_for_user = ActiveSupport::TimeZone[user.time_zone].now.midnight.tomorrow
For example, I can use the same logic to find when midnight is for a person in New York:
pry(main)> Time.current
=> Thu, 05 Jan 2017 10:34:02 CST -06:00
pry(main)> ActiveSupport::TimeZone['America/New_York'].now.midnight.tomorrow
=> Fri, 06 Jan 2017 00:00:00 EST -05:00
Note that I'm looking for midnight tomorrow; remember that "midnight" for a given day is actually the very first minute of the day, not the last.

Time in DB compared to current time

I have a couple of stores that I'd like to display if they're open or not.
The issue is that I have my current time.
Time.current
=> Sat, 11 Jun 2016 11:57:41 CEST +02:00
and then if I for example take out the open_at for a store I get:
2000-01-01 00:00:00 +0000
so what I have now is:
def current_business_hour(current_time: Time.current)
business_hours.where('week_day = ? AND open_at <= ? AND close_at >= ?',
current_time.wday, current_time, current_time).first
end
and then I check if a current_business_hour is present. However this is calculating it wrong by what seems like two hours. The open_at and close_at should be within the time zone of Time.current.
In Rails, dates and times are normally saved in UTC in the database and Rails automatically converts the times to/from the local time zone when working with the record.
However, for pure time type columns, Rails doesn't do such automatic conversion if the time is specified as a string only. It must be specified as a Time object instead, which includes the local time zone.
So, for example, if you wanted to store the open_at time as 14:00 local time, you should not set the attribute with a plain string, because it will be saved to the db verbatim, not converted to UTC:
business_hour.open_at = '14:00'
business_hour.save
# => UPDATE `business_hours` SET `open_at` = '2000-01-01 14:00:00.000000', `updated_at` = '2016-06-11 15:32:14' WHERE `business_hours`.`id` = 1
business_hour.open_at
# => 2000-01-01 14:00:00 UTC
When Rails reads such record back, it indeed thinks it's '14:00' UTC, which is off by 2 hours in the CEST zone.
You should convert the time from string to a Time object instead, because it will contain the proper local time zone:
business_hour.open_at = Time.parse('14:00')
business_hour.save
# => UPDATE `business_hours` SET `open_at` = '2000-01-01 12:00:00.000000', `updated_at` = '2016-06-11 15:32:29' WHERE `business_hours`.`id` = 1
business_hour.open_at
# => 2016-06-11 14:00:00 +0200
Note that the column is now stored in UTC time. Now, you can safely compare the time columns with any other rails datetime objects, such as Time.current.

Rails query find result with partiular day with where cluase

I want to orders which are confirmed particular day.
This is particular day:
today = Time.zone.now
Wed, 10 Jun 2015 13:31:16 IST +05:30
This is range of particular day
value = today.beginning_of_day()..today.end_of_day()
Wed, 10 Jun 2015 00:00:00 IST +05:30..Wed, 10 Jun 2015 23:59:59 IST +05:30
But when I execute this, SQL query date range is changed,
orders = Order.where(confirmed_at: value)
SELECT "orders".* FROM "orders" WHERE "orders"."confirmed_at" BETWEEN '2015-06-09 18:30:00.000000' AND '2015-06-10 18:29:59.999999' ORDER BY orders.confirmed_at DESC
In this orders confiremed between '2015-06-09 18:30:00.000000' AND '2015-06-10 18:29:59.999999'
But I wants confirmed between 'Wed, 10 Jun 2015 00:00:00 IST +05:30..Wed, 10 Jun 2015 23:59:59 IST +05:30'
You can use Time.zone.now.to_time for today.
today = Time.zone.now.to_time
value = today.beginning_of_day()..today.end_of_day()
I hope this helps.
Which DB are you using?
ActiveRecord will declare time columns using data types that do not support timezones. For example, in Postgres it will use timestamp without time zone.
This means that all Time, DateTime and TimeWithZone values set on an ActiveRecord object will be written to the DB as UTC.
Rails will then take care to apply and remove the right offsets each time the values are read or written, using the current value of Time.zone (e.g. for the current web request).
Just check the docs for TimeWithZone and think about what should go in your WHERE clause.
It probably depends on the specific context, but you might have to use UTC values in your query.

How do I add future dates to table?

I'm building a schedule in rails and I need to see if today's date matches the date of an event in the future. I have an event model with a date field and I know the dates of all the future events, but how do I add those dates to the table in a way rails will understand?
I'd like to do something like:
if Time.now is equal to next event
# do this
Time.now outputs
2014-03-18 11:05:14 -0500
How do I add that sort of format to the dates in the table so that I can compare Time.now to the date of the future event?
EDIT
Say I'm creating a new Event in the rails console, And I want the date of the Event to be April 1, 2014. I'm trying to figure out what syntax I need to enter the date in the console. For example, to create an Event in console I would do something like
Event.create(name: "Event1", date: ??)
What do I put in place of the question marks for the date of April 1, 2014 in syntax understood by the computer? I need April 1, 2014 in a datetime format, not a string.
Thank you!
seeds.rb is—as the file extension implies—just Ruby.
You can supply the date in any form that an available Ruby library can parse into a Time or Date object.
If you want to keep it human-readable, you can use Date.parse:
Event.create(name: 'Event1', date: Date.parse('Apr 1 2014'))
Event.create(name: 'Event2', date: Date.parse('April 1, 2014'))
Event.create(name: 'Event3', date: Date.parse('1-Apr-14'))
Or, if you need human-readable times as well, Time.parse:
Event.create(name: 'Event4', date: Time.parse('09:15 1 Apr 2014'))
Event.create(name: 'Event5', date: Time.parse('April 1, 2014, 9:15am'))
Event.create(name: 'Event6', date: Time.parse('1-Apr-14, 09:15 -0700'))
If you prefer to use UNIX timestamps, Time.at creates a new Time object from them (and offers a mild performance benefit over parsing textual dates):
Event.create(name: 'Event7', date: Time.at(1395166977))
If you have defined next_event as a datetime column in database, rails will automatically initialize it as a date_time object when you load it from database. All you need is this
if Time.now.to_date == next_event.to_date
For adding the future days, you can use days.from_now method
For eg
1.days.from_now #Wed, 19 Mar 2014 16:29:26 UTC +00:00
2.days_from_now #Thu, 20 Mar 2014 16:30:16 UTC +00:00
Or you can use normal addition
Time.now + 2.days
Time.now + 2.hours
Time.now + 10.minutes
Event.create(name: "Event1", date: (Time.now + 1.day))

Resources