Usage of Time.now or Time.now.to_s or Time.now.to_datetime is throwing error.
Usage of Date.today doesn't help as it stores the date with time as 12.00 AM and not the current time stamp.
Please help me in resolving this issue.
I'm not sure why and what is throwing an error, but if you're trying to generate a date string that is correct SQL from Ruby (specifically Rails here) you can use Time.now.to_s(:db).
I used CURRENT_TIMESTAMP to get the current date with timestamp. It worked.
thanks for your help.
Did you try a statement of the kind (with now() in the update statement):
INSERT INTO table1 (field1, field2, field3, timestampfield) VALUES ('value1', 'value2', 'value3', now())
[EDIT:] Have a look here for an explanation of "now()"
For update try something like:
UPDATE table1 SET mytime = now();
Another option for you if you want to put proper time on insert, it is just set function now() for the datetime field default.
Time.now.to_formatted_s :db this should give you db friendly time stamp.
http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Time/Conversions.html#M001093
Related
Using Rails 6.
I have an ElectricityUsage model, with a Date field, date. I want to extract all the values for amount only for the current month. How would I accomplish this?
What I immediately attempted was the following:
ElectricityUsage.where(habitat: current_user.reservations[0].room.habitat).where(date.month: Date.today.month)
But that obviously doesn't work, and it wouldn't even account for the year, either. My DB is running on PostgreSQL, if that makes a difference.
You can use where with Date.current.all_month, which basically is just translated into a query using BETWEEN where the start date is the first day of the month and end date is the last one:
ElectricityUsage.where(habitat: current_user.reservations[0].room.habitat, date: Date.current.all_month)
This should work for you
ElectricityUsage.where(habitat: current_user.reservations[0].room.habitat).where("EXTRACT(MONTH FROM date) = ?", Date.current.month)
PostgreSQL Date/Time Functions and Operators
Might be a delayed response but you can use date_queries gem
model ElectricityUsage < ActiveRecord::Base
date_queries_for :date
end
Then you can simply use ElectricityUsage.dates_in_this_month to find all the records that false in current month
Consider the following case. I have a Voucher model with a datetime activation_due_date field and user model that has up-to-date information about his location (timezone, UTC offset).
I want to check if he requests voucher activation before due date in any of available time zones. For instance, If a due date is set to 28.08.2018 23:59 UTC
I want my scope before_activation_due to check if he requests something before 28.08.2018 - 23:59 in his current time zone so my due date is not something fixed - it depends on users location - In one place it can be after due date and in the other before.
I have tried the following approach.
models/voucher.rb
scope :before_activation_due, lambda { |user|
where('activation_due_date > ? ', Time.current.to_utc + user.utc_offset)
}
My questions are:
Is this a right approach? If not, what is the proper way for dealing with such cases?
How to test such a scope? The current timestamp is probably taken from a database server when comparing datetimes during query execution so I am not sure how to mock it in my specs.
Thanks in advance.
You can store just the timezone of the user, not the offset, then do:
where('activation_due_date > ? ', Time.now.utc.in_time_zone(user.timezone))
where timezone is any valid timezone shown in
rake time:zones
That'd be the more rails-y way to do things at least. But I don't think storing an offset then manually adding it to the time is a bad approach.
To test this, you can manually insert any date you want in to your database. Then you can use a gem like https://github.com/travisjeffery/timecop to travel in time to that point, and test your scope:
Voucher.create(activation_due_date: '2018-01-02 00:00:00')
format = '%Y-%m-%d %H:%M:%S %Z'
time = DateTime.strptime("2018-01-02 00:00:00 Central Time (US & Canada)",format)
Timecop.travel(time)
Voucher.before_activation_due.all ...
One approach is to convert the activation_due_date into the timezone of the user. As you say "my due date is not something fixed - it depends on users location".
To do this as a scope the easiest thing would be to use your databases timezone functions. This depends on which database you are using, but in PostgreSQL it will be something like:-
where('activation_due_date AT TIME ZONE ? > NOW() ', user.timezone)
An even simpler way would be to do a string comparison
where('to_char(activation_due_date, 'YYYYMMDDHH24MISS') > ?', Time.current.in_time_zone(user.timezone).strftime('%Y%m%d%H%M%S');
In this case we are saying what is the time on the wall for the user, and is it less than time in the database (which is "stored" in UTC).
cloud somebody explain to me why casting timestamp to date in psql gives me wrong value? I have in my db stored time stamp value 2016-12-04 00:05:09.748000 and my machine time is in UTC, and datatype in psql is TIMESTAMP WITH TIME ZONE. If I did this,
orders.completed::date
it gives me 2016-12-03. Problem is that if i have some orders around midnight aggregate functions gives me wrong values. Is there some way how to solve it? I would appreciate any help!
Try this :
(orders.completed::timestamp at time zone 'UTC' at time zone 'America/Los_Angeles')::date;
You need to insert your timezone in query
Hm, I can't reproduce it:
b=# set timezone to "GMT+10";
SET
b=# create table edge (t timestamptz);
CREATE TABLE
b=# insert into edge select now();
INSERT 0 1
b=# select t::date, t from edge;
t | t
------------+-------------------------------
2016-12-08 | 2016-12-08 00:18:30.740132-10
(1 row)
I want to be able to test the behaviour of a scheduler component across different timezones. However, the functionality to trigger scheduled behaviour uses time based queries within postgres:
e.g.
# find reminders which have not been sent for "today" in the local date
Schedule.where('evening_reminder_last_sent_on_local_date !=
DATE( NOW() AT TIME ZONE time_zone )')
I would like to be able to test this behaviour in RSpec and ensure that it plays correctly through the day and that if I send a Japanese user a reminder at 1am UTC on the 25th Dec, then at 10pm UTC, their reminder for "today" will show up as not have been sent (since it's about 7am the next day in Japan).
However, in order to do this I need to be able to set the datetime in postgres. Is this possible?
Please note... this is not about stubbing Rails' time
The challenge is not to stub the time in Rails - I know how to do that. The problem is how to set the time in Postgres.
Postgres uses the date / time setting of the underlying OS (at least on UNIX-like systems). To stage various timestamps, you would have to set the time of the OS, with the date command.
I would certainly not recommend that on a system that does anything else of importance. cronjobs can run haywire and other unpleasant side effects.
Instead, replace the function now() with a user-defined server-side function like:
CREATE OR REPLACE FUNCTION now_test()
RETURNS timestamptz AS $func$SELECT '2013-12-01 12:34'::timestamptz$func$ LANGUAGE SQL;
(The above cast assumes the current time zone setting of the session. Alternatively, you can provide a time zone or time offset with the literal.)
Then you can test your code with:
Schedule.where('evening_reminder_last_sent_on_local_date !=
DATE(now_test() AT TIME ZONE time_zone)')
Modify the above SQL function above with various timestamps and you are good to go.
The previous suggestion is indeed very good but as I wanted to easily test my query with Delorean gem, I came up with this workaround:
Schedule.where('evening_reminder_last_sent_on_local_date !=
DATE( ?::timestamp AT TIME ZONE time_zone )', Time.now)
I took the previous example just for the sake, but I had to manipulate times in Postgres with the now() function. I couldn't just inject my ruby time instead without casting it with ::timestamp.
Moreover, in your case, maybe you could use timestamptz.
I had a similar case except instead of modifying the timezones, I wanted to apply an offset. This let me synchronize postgres's responses to NOW() with my appliction's responses to datetime.now() (tampered via libfaketime).
I ended up with one statement that renamed now() to system_now(), but only if system_now() didn't already exist:
DO $$
DECLARE
found_func pg_proc%rowtype;
BEGIN
SELECT * FROM pg_proc WHERE proname = 'system_now' INTO found_func;
IF FOUND THEN
RAISE NOTICE 'DB Already Faked';
ELSE
RAISE NOTICE'Faking DB Time';
ALTER FUNCTION now RENAME TO system_now;
END IF;
END $$;
...and another one that redefined NOW() to include the offset:
CREATE OR REPLACE FUNCTION now() RETURNS timestamptz
AS $func$
SELECT system_now() + INTERVAL '1225288 seconds';
$func$ LANGUAGE SQL;
I'm having a lot of trouble with a query. I don't really know how to explain this well, but I'm going to try.
Basically, we have several objects with a 'posted_at' field that keeps the date and time something was posted, with the time zone, in a datetime field. I need to query and get a range by date with those objects.
Previously, I was converting that to Date and comparing it to another Date object. The query was something like this:
Date(posted_at) >= :start_date AND Date(posted_at) <= :end_date
However, when Postgre converted it to Date, it lost the timezone info which caused innacurate results to the query.
So, I changed to this:
if start_date then
start_time = Time.zone.parse("#{start_date.year}-#{start_date.month}-#{start_date.day}")
conditions << "posted_at >= :start"
hash[:start] = start_time
end
if end_date then
end_time = Time.zone.parse("#{end_date.year}-#{end_date.month}-#{end_date.day}").end_of_day
conditions << "posted_at <= :end"
hash[:end] = end_time
end
While this gets me the accurate results, it also has horrible performance and is causing some timeouts in my application.
I couldn't find any other way to do this query and still keep the accurate results. Would anyone have some advice or ideas?
Thank you in advance.
You never want to store timezone information in your database.
Here's a read that discusses some of the pitfalls:
http://derickrethans.nl/storing-date-time-in-database.html
You'll get better results as tadman suggests: add a new field with your timestamp at time zone 'utc', and index it. You'll then be able to grab stuff using posted_at between ? and ?.
You may have more luck converting your start and end times to UTC which would render the time-zone mostly irrelevant when making the query itself. This is done easily enough:
start_date.to_time.to_datetime.beginning_of_day.utc
end_date.to_time.to_datetime.end_of_day.utc
You can also adjust your query to be:
posted_at BETWEEN :start AND :end
Be sure to have an index on the fields you're searching, too, or you will get horrible performance.