How to set datetime format in Rails - ruby-on-rails

I am using a js calendar that inserts a string into a text field for submission as a datetime value. Not sure if it matters but I am using SQLite for now.
I am setting the format as follows (in an initializer):
Time::DATE_FORMATS[:default] = '%m/%d/%Y %H:%M'
Time::DATE_FORMATS[:db] = '%m/%d/%Y %H:%M'
This isn't working because Rails is apparently validating the date using a different format.
For example, if I set it as follows:
myobject.my_datetime = '06/30/2012 00:00'
it ends up null (the SQL query itself generated by ActiveRecord sets it to NULL). If I set it to a day less than or equal to 12 the date part ends up correct (but the generated SQL query still shows the string with the day before the month, i.e. in the above example the SQL query has the string as '30/06/2012 05:00:000000').
As you can see it also seems to be adding 5 hours for UTC(?) even though I have config.time_zone set to eastern time.
Behavior is the same either from console or via form submission.
Any suggestions on what I am missing?
UPDATE:
When I follow jdoe's suggestion and do this:
myobject.my_datetime = Time.strptime('06/30/2012 08:00', '%m/%d/%Y %H:%M')
The generated query now looks correct (ignoring the 4 hour difference):
UPDATE "myjoin_table" SET "my_datetime" = '06/30/2012 04:00.000000' .....
and it winds up in the database that way.
But when I reload the object the value in the AR object is initialized to nil.
I guess I'll just parse the string piece by piece in my controller action and set the parameters to update_attributes manually. This is harder than it should be.

ActiveRecord stores date/time in UTC+00:00. If you're living in the +5 zone, ActiveRecord will subtract 5 hours on assigning automatically. So, your current time zone just says how many hours has to be added/subtracted from your local time.
About parsing. Did you try the following?
myobject.my_datetime = Time.strptime('06/30/2012 00:00', '%m/%d/%Y %H:%M')
Rails time/date settings are made for showing, not for assignment. It's not a brilliant idea to get strings from a user and assign them to your date/time attributes. You'd better use proper time objects in assignment instead of stings. The strptime method will raise the ArgumentError if some user is trying to be a hacker. Your record.date_time='string' fails silently.
UPD: Usage Example
p = Product.first
p.last_visited = Time.strptime('06/30/2012 00:00', '%m/%d/%Y %H:%M')
p.save # leads to UPDATE "products" SET "last_visited" = '2012-06-29 21:00:00.000000'
Product.last_visited.localtime.strftime('%m/%d/%Y %H:%M') # => "06/30/2012 00:00"
As you see, time is stored and retrieved w/o any problems.
UPD 2: I18n
Locate your config/locales/en.yml. Add the following to it:
en:
time:
formats:
default: '%m/%d/%Y %H:%M'
To parse your time use t(or translate) helper:
#time = Time.strptime('06/30/2012 00:00', t('time.formats.default'))
Use l(or localize) helper to show time in your format:
= l #time

Related

Timezone on Time Datatype not working as expected - Rails 5

I have model with an attribute (period_starting_at) of TIME datatype, running on Rails 5 with Postgres.
When my controller assigns a parameter value to that model attribute, something is changing the timezone to a wrong one. See the example:
model_one = Model.new
puts params['Model']['period_starting_at'] #Returns 10:00
puts Time.zone.parse(params['Model']['period_starting_at']) #Return 2018-07-16 10:00:00 -0300, which is with the correct timezone -3.
model_one.period_starting_at = Time.zone.parse(params['Model']['period_starting_at'])
puts model_one.period_starting_at # Return 2000-01-01 10:00:00 -0200, which is with the **wrong** timezone -2.
The timezone of the rails app is correct.
Time.zone.to_s => "(GMT-03:00) Brasilia"
Also, the timezone of the database (postgres) is correct.
What am I missing?
The problem was about DST - Daylight Saving Time. Rails don't actually have a true 'Time' datatype, it's a 'Datetime' datatype with a dummy date part. But depending on the manipulation that you apply, that dummy date part can affect the timezome, because of DST. I hope I was helpfull.

Date in free format into db

I have a model with :birth_date of type date.
I've tried to put a string like 3 janvier 1968 (French language) into that field and somehow in database I saw that PostgreSQL or someone else converted it into a date!
I also tried some other dates like 3 février 1968 or like 3 fevrier 1968 which didn't work and turned out to be NULL in db.
I can't find information about this feature anywhere. How does this work?
Rails knows that attribute is a Date from the database definition, so it converts the string you give it to a Date. If you create a new instance of your model in the Rails console and assign to birth_date, you can show that it's already a Date even before you save it to the database:
m = Model.new # Use your model name
m.birth_date = "3 février 1968"
m.birth_date.class
The console should tell you that m.birth_date is a Date.
So the conversion to Date is done before you save the model to the database. Rails defines a String::to_date method that calls the Ruby ::Date.parse method, which converts various human-readable date strings into a Date (https://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/Date.html#method-c-parse). In the Rails source, you'll see that whatever you assign to a Date attribute is converted to a Date with the to_date method. So when you assign a String, it happens via String::to_date which calls Date.parse.
As you mentioned in your comment, Date.parse seems to take a fairly loose approach to the months when they're spelled out. I tried a variety of English, French, and Spanish months in Date.parse, and as long as the first three letters of the non-English month are the same as the English month, Date.parse will convert them. But if the first three letters are different, then Date.parse throws an error.
if you have a column in the database as type 'date', it will only save as a date. Rails does it's best to convert a string into a recognized date if possible. You should always pass the 'birth_date' data as a date (i.e. use a date_field). Otherwise, if you REALLY want to store it as a string, the birth_date column must be of type string in the database

Ruby on Rails showing truncated date for each date

I am using RoR with MSSQL server as database, in a table I am saving date with 'date' datatype.
RoR saving the date correctly e.g if I am saving 2012-10-20 then it is saving 2012-10-20 but when I display it on front-hand it display like 2012-10-02 for 2013-10-20, 2012-10-01 for 2013-10-10, 2012-10-01 for 2013-10-15 etc.
What is going wrong... I can't figure it out
Edit
I am not asking for date formating technique. The issue is that date not fetched correctly as it saved in database, if I used datatime datatype instead of date datatype then all goes right.
You should do:
<%= #user.created_at.strftime("%Y-%m-%d") %>
Or if you want it to be more readable then:
<%= #user.created_at.to_date %>
You can try both which ever you like
EDIT
You have to do like mentioned above otherwise you have to create a new attribute to store date only. created_at is default timestamp type See this link. You can use any of both ways either convert created_at to date or introduce new column to table
How are you displaying it on your view?
For example the following would show "January 2010"
<%= #user.created_at.strftime("%B %Y") %>
You probably want year, month, date? So %Y %m %d
According to the github page of the adapter:
Date/Time Data Type Hinting
SQL Server 2005 does not include a native data type for just date or
time, it only has datetime. To pass the ActiveRecord tests we
implemented two simple class methods that can teach your models to
coerce column information to be cast correctly. Simply pass a list of
symbols to either the coerce_sqlserver_date or coerce_sqlserver_time
methods that correspond to 'datetime' columns that need to be cast
correctly.
class Topic < ActiveRecord::Base
coerce_sqlserver_date :last_read
coerce_sqlserver_time :bonus_time
end
This should point you in the right direction

ruby timezone conversion issues

I have a scenario in which i get a timestamp and i need to search for all bookings for that date in that timestamp. The timestamp is in users respective timezone and all the records in the database are stored in UTC. so naturally i need to convert that timestamp back to UTC and then search.
Here's something that i'm doing:
Booking.where("date_time >= '#{DateTime.parse(timestamp).in_time_zone('UTC').beginning_of_day}' and date_time <= '#{DateTime.parse(timestamp).in_time_zone('UTC').end_of_day}'")
which basically means to fetch all bookings from the beginning of day till the end
However, when i use the following query it gives me a different result:
Booking.where("date_time >= '#{DateTime.parse(timestamp).beginning_of_day.in_time_zone('UTC')}' and date_time <= '#{DateTime.parse(timestamp).end_of_day.in_time_zone('UTC')}'")
I'm wondering which one is actually the correct statement to use in my use case and i would appreciate some input here.
I wouldn't use either one.
This one:
DateTime.parse(timestamp).in_time_zone('UTC').beginning_of_day
gives you the beginning of the UTC day, not the beginning of the local-time-zone-day offset to UTC. In short, it is incorrect and won't give you what you're looking for.
This one:
DateTime.parse(timestamp).beginning_of_day.in_time_zone('UTC')
is correct as it changes the time to the beginning of the day in the local time zone and then converts the timestamp to UTC.
If you let ActiveRecord deal with the quoting using a placeholder, then it will apply the UTC adjustment itself.
I'd also use < t.tomorrow.beginning_of_day rather than <= t.end_of_day to avoid timestamp truncation and precision issues; the end of the day is considered to be at 23:59:59.999... and that could leave a little tiny window for errors to creep in. I'm being pretty pedantic here, you might not care about this.
I'd probably do it more like this:
t = DateTime.parse(timestamp)
Booking.where('date_time >= :start and date_time < :end',
:start => t.beginning_of_day,
:end => t.tomorrow.beginning_of_day
)

Generating a random date without time in Ruby

I have a date of birth column on my user table that takes a DATE. As this datatype appears as YYYY-MM-DD, I assume that when inputting a date to the database it must have the format, for example: 2013-12-26.
I have seen methods on StackOverflow for creating a random DateTime in Ruby, such as here. However, after much searching I can't find a way to generate a random date without the time, for example in the past 100 years, and have it properly formatted for the DATE datatype. In Rails, what is the best way to generate a random date without the time?
This seems to work:
def rand_date(days)
date = Date.today-rand(days)
date.to_s(:db)
end
But is there a more elegant solution that comes with Rails? I am new to Rails and programming, so any assistance would be most helpful!
Your method is correct. If you are using Rails, there are some trivial improvements such as
def rand_date(days)
rand(days).days.ago(Date.today)
end
which is mostly equivalent to
def rand_date(days)
rand(days).days.ago.to_date
end
The second version is less efficient because it creates more Date/Time objects during the internal conversions.
Apply to_s(:db) if you need the Date to be formatted as String.
A different approach would require you to construct a date passing the result of a rand to Date.new.
This is in core ruby:
1 #!usr/bin/ruby
2
3 require 'date'.
4
5 10.times do |t|
6 random_year = Random.new.rand(2000..2014) # custom range for years
7 random_month =Random.new.rand(1..12)
8 random_day = Random.new.rand(1..30)
9 puts "#{Date.new(random_year,random_month,random_day)}"
10 end
2014-11-29
2010-10-20
2006-02-23
2009-09-17
2006-01-14
2009-01-06
2002-07-06
2005-11-05
2013-06-20
2005-12-02
Here is something I used to generate random birth dates when populating a customer database. It works on days, and in this example, gives random dates between 1967-01-09 and 1993-01-12 by using the Date#jd method:
Date.jd(2439500 + rand(9500))
You can twiddle the dates generated by setting the base (in this case 2439500, which is 1967-01-09) and the random number to increase or decrease the range of dates produced.
Example:
irb(main):043:0> 10.times { puts Date.jd(2439500 + rand(9500)) }
1973-06-07
1973-11-09
1983-07-27
1990-11-03
1967-06-18
1967-06-20
1970-07-28
1990-05-13
1986-11-26
1989-02-15

Resources