Rails 3 ui datepicker - ruby-on-rails

I have some strange behavior in my rails3 app.
When I pick a date with jquery UI datepicker. I see this in my log
Parameters: {"commit"=>"Save", "log"=> {"log_date"=>"04/20/2011"} ....blah...b}
AREL (1.0ms) INSERT INTO "logs" ("log_date") VALUES ('2011-04-19 22:00:00.000000')
I've left out the irrelevant information.
As you see Rails does not translate the date inputted correct. It changes (in this case) 20.apr to the 19.apr.
When I later call
<%= log.log_date.strftime('%d.%m.%y') %>
I get the corect date, but when I do this query
#log_times = Log.group(:log_date)
The logs are all one day earlier than they sould be.
In the console the log in this example looks like this
irb(main):017:0> Log.last
=> [#<Log id: 246, log_date: "2011-04-19 22:00:00">]
So, its save one date to early, but when I show it in the view it's correct.
Why?
Could I use a getter and setter to fix this?

Rails is built to handle timezones by default. Times stored in the database are stored in UTC and Rails is supposed to handle translating that into the configured local time automatically.
Do you have config.time_zone set in your application.rb file? Also, I'd recommend using DateTime rather than Time when possible.

Related

Rails render a time form field in the user's time zone

I am re-writing this post for clarify and to show what I have so far and where exactly I am stuck.
So as you may know, the local_time gem only works when rendering the date and time attributes from the record itself, such as:
local_time(#events.start_time)
But I want to be able to render the datetime or time selects in the user's time zone, yet still save them to the database as UTC. I've been able to achieve this by hard coding it to Easter time by:
- Time.use_zone("Eastern Time (US & Canada)") do
%fieldset.border.border-dark.p-2
%legend Start Time
.input-group
= f.time_select :start_time, {ignore_date: true, minute_step: 15, prompt: true, ampm: true}, {class: "form-select form-select-lg border border-dark ms-2 me-2"}
.text-end.text-muted
= Time.zone
The above code will use Eastern Time (US & Canada) as the time zone the user sees, but at the same time is still saved to the database as UTC.
In order to make this more dynamic, I need to get the client's time zone, which according to this thread, you can obtain the client's IANA time zone by:
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
However, I need a way to post this to the events/new controller so that I can use it as a Ruby instance variable to change one line above:
- Time.use_zone(#my_zone) do
In my Events Controller I currently have:
def new
#my_zone = params[:my_zone]
#my_zone = ActiveSupport::TimeZone[TZInfo::Timezone.get('America/Vancouver').period_for_utc(Time.now.utc).utc_offset]
#event = Event.new
The second time that #my_zone is called, we are using an ActiveSupport that would convert the iANA value directly into the TZInfo value that the Time.use_zone uses, as discussed here.
Then once the controller has that value, it can plug it into Time.use_zone() method.
Getting Javascript to post to the controller is where I'm struggling. I attempted to use a similar AJAX call to this thread, such that at the bottom of my new.html.haml:
:javascript
$.post('/events/new', {my_zone: Intl.DateTimeFormat().resolvedOptions().timeZone}))
However, when I try to run that, the browser console says:
Syntax Error: Bare private name can only be used on the left hand side of an 'in' expression.
I can't figure out what's going on with that.
On the other hand, I'm actually on Rails 7 now, which I do know doesn't have UJS by default but rather uses Stimulus, Hotwire and Turbo. So, is there a way I could do the post to the controller via Stimulus, Hotwire and turbo?

How to correctly set default in time_zone_select based on Time.zone (abbreviated vs extended names) in Rails 5.2?

Recently we've been seeing that our time_zone_select defaults are incorrect. I think this might have been since our upgrade to Rails 5. In short, we have the following bit of code:
f.time_zone_select :timezone, nil, { default: Time.zone.name }
The problem here is that time_zone_select compiles an options list where the values are "abbreviated" names, by which I mean (e.g.) Amsterdam instead of Europe/Amsterdam.
This now goes wrong when using Time.zone.name to set the default, since that returns Europe/Amsterdam. As a result, the default is not selected.
It this a bug or am I overlooking something here?
Diving into this shows that in version 5.1.6.2 of ActiveSupport::TimeZone the time_zone_select works as documented, allowing us to simply select a valid default time zone with Time.zone.name. Albeit that if the default is (e.g.) "Europe/Amsterdam", the select options contain both "Amsterdam" as well as "Europe/Amsterdam".
So I guess the changes made to ActiveSupport::TimeZone in Rails 5.2.0 have insufficiently taken into account the impact these have on time_zone_select. For now, instead of using a the extended time zone name (like "Europe/Amsterdam") to select a time_zone_select default, I'm explicitly normalizing that to the abbreviated form as a workaround, like so:
# As of Rails 5.2.0 the time_zone_select helper does not accept time zones in format "Europe/Amsterdam"
# anymore. Instead, we should cast this to a selectable value, being "Amsterdam" in this example.
def selectable_time_zone_name(zone_name)
ActiveSupport::TimeZone.all.find{|tz| tz =~ zone_name }&.name
end
which we can then use like so (this includes migrating any extended name value that the timezone attribute might already have):
<% form.object.timezone = selectable_time_zone_name(selectable_time_zone_name(form.object.timezone) || Time.zone.name) %>
<%= form.time_zone_select :timezone %>

Working with and testing dates in rails

My application uses dates a lot. A lot of ajax calls and urls involve datetimes and I find the typical format '1920-10-10 18:30:00' to be unfriendly for these purposes. I work around this by creating a helper method that basically strips the unnecessary characters out of the date (192010101830) and another method for converting the string back in to a date object.
When I build a url it goes something like this:
=link_to "Send Date", thing_my_date_path(date_to_string(DateTime.now))
Then when the thing_date action receives, it converts the parameter back in to a datetime object
def my_date
#date = string_to_date(params[:mydate])
....
end
This works fine in development. However I am completely open to other suggestions.
The problem is when I go to test my application. Tests fail because the helper methods for date_to_string and string_to_date are not present. I could include them in the tests but I feel like they should be kept separate.
So I'm looking for
a) a better way to pass dates around, and more importantly
b) a method of testing an action that is dependent on helper methods.
There are built-in methods for that methinks.
> DateTime.now.to_s(:number)
=> "20110429162748"
> DateTime.parse("20110429162748")
=> Fri, 29 Apr 2011 16:27:48 +0000
Hope that helps..

why is the datetime in database different when I output it?

In my database on heroku, it shows contactemail.created_at = "2010-08-08 17:16:19"
However, when I use puts.contactemail.created_at I get something different. I get:
2010-08-08 10:11:13 -0700
I need to input that value through an API to another application, and I am pretty sure that the first format is what it wants. If it doesn't take that, it wants 08/08/10 17:16:19 -- in either case, I don't know how to format it properly.
This is in Ruby on Rails.
The display of date is based on your servers locale settings. If you are looking for the first format you could try
puts.contactemail.created_at.to_s(:db)
Have a look at strftime doc here http://ruby-doc.org/core/classes/Time.html#M000298 to get the second format
HTH
Ruby on rails can manage different time zones.
You can:
Make a direct sql consult (rails have some helpers)
Make that the two dates be equals, configuring config.active_record.default_timezone

How do I work with Time in Rails?

I've been pulling my hair out trying to work with Time in Rails. Basically I need to set all time output (core as well as ActiveSupport) to the server's local time -- no GMT, no UTC, etc. I've seen various posts relating to Time, but they usually involve someone's need to set it for each user. Mine isn't nearly as complex, I simply want consistency when I use any Time object. (I'd also appreciate not receiving errors every 3 seconds telling me that I can't convert a Fixnum (or some other type) to string -- it's Ruby, just do it!)
I also seem to be getting drastically different times for Time.new vs the ActiveSupport 1.second.ago. Anyway, does anyone have any quality suggestions as regards working with Time in Rails?
If you just want Time objects to be consistent, then why not stick with UTC? I just tried Time.new and 1.second.ago using script/console and I get the same output (give or take a second for typing the command). How are you doing it?
Somewhere in your initializers, define the format(s) that you want to use.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => '%m/%d/%Y %H:%M')
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:my_special_format => '%H:%M %p')
Then when you want to print a Time object, it works like the following example. Notice that the Time object in my console is already aware of my time zone. I'm not performing any magical transformations here.
>> t = Time.now
=> Wed Jul 15 18:47:33 -0500 2009
>> t.to_s
=> "07/15/2009 18:47"
>> t.to_s(:my_special_format)
=> "18:47 PM"
Calling Time#to_s uses the :default format, or you can pass in the name of the format you'd rather use like I did with :my_special_format.
You can see the various options for formatting a Time object here.
If u don't want to store each user time setting, the only solution is to use javascript time system because it work on user client time. For example i have an application that each time user try it, the app will create some example data with each data have a initial date value "today". At first time, it confuse me a lot because my host server is in australia and lot of user is on western part, so sometime the initial date value is not "today", it said "yesterday" because of different time region.
After a couple day of headache i finally take decision to JUST use javascript time system and include it in the link, so when user click the "try now" link it will also include today date value.
<% javascript_tag do -%>
var today = new Date();
$("trynow").href = "<%= new_invitation_path %>?today=" + today.toLocaleString();
<% end -%>
Add the following to config/environment.rb to handle time correctly and consistently all the time within the context of Rails. It's important to know that it will store your times to the database in UTC -- but this is what you want -- all the conversion is done automatically.
config.time_zone = 'Pacific Time (US & Canada)'
You can run rake time:zones:local from your Rails root directory to get a list of valid time zone strings in your area.
A quick addition to the DATE_FORMAT solution posted above. Your format can be a string, in which case it works as noted above by calling strftime, but you can also define the format as a lambda:
CoreExtensions::Time::Conversions::DATE_FORMATS.merge! :my_complex_format => lambda {|time|
# your code goes here
}

Resources