Time.new subtracting 6 hours AND tacking on CST -6 on Heroku? - ruby-on-rails

I'm sure there's some conversion thing I'm looking over here.
On Heroku's console,
irb(main):052:0> Time.new(2014, 1, 21)
=> 2014-01-21 00:00:00 +0000
However, setting a column to that:
irb(main):042:0> PressRelease.first.update_attribute :published_on, Time.new(2014, 1, 21)
PressRelease Load (1.9ms) SELECT "press_releases".* FROM "press_releases" ORDER BY created_at DESC LIMIT 1
(1.0ms) BEGIN
FriendlyId::Slug Load (0.8ms) SELECT "friendly_id_slugs".* FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."sluggable_id" = 1 AND "friendly_id_slugs"."sluggable_type" = 'PressRelease' ORDER BY "friendly_id_slugs".id DESC LIMIT 1
(0.6ms) COMMIT
=> true
Gives this date:
=> Mon, 20 Jan 2014 18:00:00 CST -06:00
To clarify, application.rb does indeed have the time zone set:
config.time_zone = 'Central Time (US & Canada)'
And when I check on their console:
irb(main):054:0> Time.zone
=> (GMT-06:00) Central Time (US & Canada)
However, doing this locally works fine:
1.9.3-p448 :011 > Time.new(2014, 1, 21)
=> 2014-01-21 00:00:00 -0600
So, it looks like Heroku is subtracting -6 (since our Time Zone is set to CST -6), then tacking on the timezone of CST -6 as well. Why? This is, as you can see, messing up date-specific items.

If you want Rails to use the time_zone setting, you need to use the wrappers provided by ActiveSupport::TimeZone. See docs: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
Here's an example:
Time.zone.parse("2014-1-21")
# Outputs Tue, 21 Jan 2014 00:00:00 MST -07:00

Related

Issue Formatting DateTime in Rails

So my rails application supports different time zones and sets the time zone at each request like so:
def set_current_time_zone
Time.zone = current_user.time_zone
end
All of my formatting and reports site wide work except for a jQuery date time selector.
The form submits a start time in the format of 04/20/2018 5:23 AM. When I parse the time with the code below I get the following output.
# this is the value of params[:job][:start] 04/20/2018 5:23 AM
job.start = DateTime.strptime(params[:job][:start],"%m/%d/%Y %H:%M %p")
#printing job.start outputs this: 2018-04-20 00:23:00 -0500
As you'll notice the time zone has been applied to the time and is now the wrong time. How do I correct this?
Thanks in advance.
Keep in mind DateTime.strptime('04/20/2018 5:23 AM',"%m/%d/%Y %H:%M %p") will return the value in UTC =>Fri, 20 Apr 2018 05:23:00 +0000
However if you store this value say to a database field, in an active record object, it will output the timezone value to whatever value you've set to Time.zone
This may not be a complete answer but could lead you in the right direction.
Add some more details to my last comment above.
I did this in rails console, my model is Shift and has start_time: datetime.
2.1.3 :022 > time = "04/02/2018 5:23 AM"
=> "04/02/2018 5:23 AM"
2.1.3 :023 > Time.zone = "Singapore"
=> "Singapore"
2.1.3 :024 > shift.start_at = time
=> "04/02/2018 5:23 AM"
2.1.3 :025 > shift.save
(0.1ms) begin transaction
SQL (0.5ms) UPDATE "shifts" SET "start_at" = ?, "updated_at" = ? WHERE "shifts"."id" = 2 [["st
art_at", "2018-02-03 21:23:00.000000"], ["updated_at", "2018-05-02 05:43:38.527338"]]
(1.4ms) commit transaction
=> true
2.1.3 :026 > Time.zone = "Dublin"
=> "Dublin"
2.1.3 :027 > shift.start_at = time
=> "04/02/2018 5:23 AM"
2.1.3 :028 > shift.save
(0.2ms) begin transaction
SQL (0.4ms) UPDATE "shifts" SET "start_at" = ?, "updated_at" = ? WHERE "shifts"."id" = 2 [["st
art_at", "2018-02-04 05:23:00.000000"], ["updated_at", "2018-05-02 05:45:44.638291"]]
(1.4ms) commit transaction
=> true
See the data inside the SQL.

weird time_zone EDT vs EST issue with Rails

I have a rails 5 server that is configured to be "Eastern Time (US & Canada)"
The server works great mostly, it stores times in the database in UTC.
For example, if I am in a console and do
Time.zone.now
=> Fri, 27 Oct 2017 15:07:04 EDT -04:00
Which is correct (we have not fall back on time yet)
However if I pull down an active record object:
> s = Schedule.first
Schedule Load (6.2ms) SELECT "schedules".* FROM "schedules" ORDER BY "schedules"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Schedule id: 1, product_id: 1, day: "Monday", start_time: "2000-01-01 22:00:00", end_time: "2000-01-01 23:00:00", size: 25, description: "Grades 3-5", created_at: "2017-08-23 14:16:09", updated_at: "2017-08-23 14:16:09", is_full: false>
2.4.0 :004 > s.start_time
=> Sat, 01 Jan 2000 17:00:00 EST -05:00
As you can see it convert is to Easter Standard time, which is not correct. Is it possible rails is confused or am I doing something wrong?
(using ruby 2.4)
This was user error, the objects date was in EST so Active Record was doing its job, fix was to set it to today and it would be in the right zone.

Rails datetime format "dd/mm/yyyy"

I receive dates like "14/04/17 07:00"
I want to get only the date like this: 14/04/2017
And only the hour 07:00
I tried this way, BTW I know it's for US time, it render me 17/04/10
DateTime.parse(datetime).strftime("%d/%m/%Y")
Humm I can't find anywhere how to do this?
Thanks for your help
EDIT
with the suggestion:
my_date = DateTime.parse(datetime).strftime("%d/%m/%Y")
If I do in my terminal:
[1] pry(main)> O = Onduleur.last
Onduleur Load (0.2ms) SELECT "onduleurs".* FROM "onduleurs" ORDER BY "onduleurs"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<Onduleur:0x007fa6b70186e0
id: 144,
identifier: 2,
datetime: "11/04/17 23:00",
energy: 0,
created_at: Wed, 12 Apr 2017 15:17:04 UTC +00:00,
updated_at: Wed, 12 Apr 2017 15:17:04 UTC +00:00>
[2] pry(main)> my_date = DateTime.parse(O.datetime).strftime("%d/%m/%Y")
=> "17/04/2011"
[3] pry(main)>
For reading you can use strptime and specify the format:
datetime = "14/04/17 07:00"
DateTime.strptime(datetime, "%d/%m/%y %R")
=> Wed, 14 Apr 0017 07:00:00 +0000
Explanation
%d - Day of the month, zero-padded (01..31)
%m - Month of the year, zero-padded (01..12)
%y - year % 100 (00..99)
%R - 24-hour time (%H:%M)
For getting the date you can transform DateTime objects to Date objects using to_date or use .strftime("%d/%m/%Y") directly on DateTime to get String.
[47] pry(main)> a
=> Fri, 14 Apr 2017 07:00:00 +0000
[48] pry(main)> a.to_date
=> Fri, 14 Apr 2017
[49] pry(main)> a.strftime("%d/%m/%Y")
=> "14/04/2017"
[50] pry(main)> a.strftime("%R")
=> "07:00"
Full docs here. Also a full list of format directives is available on strftime docs
You can set to variables with the desire data
datetime = "2017-04-12 10:30:14"
my_date = DateTime.parse(datetime).strftime("%d/%m/%Y")
my_hour = DateTime.parse(datetime).strftime("%H:%M")
puts my_date
puts my_hour
Output:
12/04/2017
10:30
EDIT:
If you have this format date (11/04/2017 23:00), you can try with .to_time method
require 'active_support/core_ext/string'
datetime2 = "11/04/2017 23:00".to_time
my_date2 = datetime2.strftime("%d/%m/%Y")
my_hour2 = datetime2.strftime("%H:%M")
puts my_date2
puts my_hour2

Rails: Clarification (rules-of-thumb?) for Date/Time, to_time and time zones

tl;dr: what are the rules for working with Date, Time & Datetime so I'm assured consistency across my apps?
I'm trying to wrap my head around working with Dates & Times & Zones in Rails so I don't accidentally use UTC when I really want all "user facing" dates/times adjusted for time zone. I'm noticing what appears, to me at least, to be some inconsistency and I'm hoping to understand rules or logic behind them so I'm not "surprised" again.
1.9.3p194 :028 > e = Event.find(1)
Event Load (0.3ms) SELECT "events".* FROM "events" WHERE "events"."id" = $1 LIMIT 1 [["id", 1]]
=> #<Event id: 1, start_at: "2012-08-27 19:15:00", end_at: "2012-08-27 21:00:00", created_at: "2012-08-22 07:43:31", updated_at: "2012-08-23 03:01:59">
1.9.3p194 :037 > e.start_at # <== start_at is DateTime in model
=> Mon, 27 Aug 2012 12:15:00 PDT -07:00
1.9.3p194 :036 > e.start_at.to_time
=> 2012-08-27 19:15:00 UTC ### <=== This is in UTC.. ok...
1.9.3p194 :034 > DateTime.now
=> Thu, 23 Aug 2012 10:44:16 -0700 # <=== Also a DateTime
1.9.3p194 :035 > DateTime.now.to_time
=> Thu, 23 Aug 2012 10:44:19 -0700 ### <=== But this is in Pacific Time ?!?
2 differing responses from to_time? Or did I miss something?
The fairly cryptic documentation for DateTime's to_time doesn't make any mention of timezone:
to_time()
Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class. If self has an offset other than 0, self will just be returned unaltered, since there’s no clean way to map it to a Time.
So what are "rules" for getting consistent dates & times out of Rails?
I'm assuming you have a start_at column in your database. As a result e.start_at is not an instance of DateTime but an instance of Time. (Try calling e.start_at.class to see it)
Instead of using DateTime.now I'd recommend using Rails' Time.zone.now which will return the current time in the timezone defined in Time.zone.
1.9.3p125 :005 > Time.zone
=> (GMT+00:00) UTC
1.9.3p125 :006 > Time.zone.now
=> Thu, 23 Aug 2012 20:10:34 UTC +00:00
1.9.3p125 :007 > Time.zone = "Berlin"
=> "Berlin"
1.9.3p125 :008 > Time.zone
=> (GMT+01:00) Berlin
1.9.3p125 :009 > Time.zone.now
=> Thu, 23 Aug 2012 22:10:47 CEST +02:00

Rails config.time_zone and datetime attribute update

In my application I have: config.time_zone = 'Warsaw'
A strange issue I have, is that it seems like Rails are having problems with comparision of datetime fields.
If I change the datetime 1 hour back (and Warsaw is currently in timezone +0100), Rails won't update the database, even if the field has changed. However, if I change the field once again, then the update will go to the database.
Example:
(Rails 3.1.0, ruby-1.9.2-p290, fresh rails app):
$ rails g model User starts_at:datetime
$ rake db:migrate
$ rails c
Loading development environment (Rails 3.1.0)
ruby-1.9.2-p290 :001 > u = User.create({:starts_at => "2011-01-01 10:00"})
SQL (21.3ms) INSERT INTO "users" ("created_at", "starts_at", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 13 Dec 2011 11:32:50 CET +01:00], ["starts_at", Sat, 01 Jan 2011 10:00:00 CET +01:00], ["updated_at", Tue, 13 Dec 2011 11:32:50 CET +01:00]]
=> #<User id: 1, starts_at: "2011-01-01 09:00:00", created_at: "2011-12-13 10:32:50", updated_at: "2011-12-13 10:32:50">
ruby-1.9.2-p290 :002 > u.starts_at
=> Sat, 01 Jan 2011 10:00:00 CET +01:00 # datetime created
ruby-1.9.2-p290 :003 > u.starts_at = "2011-01-01 09:00:00" # new datetime with one hour back
=> "2011-01-01 09:00:00"
ruby-1.9.2-p290 :004 > u.starts_at
=> Sat, 01 Jan 2011 09:00:00 CET +01:00 # changed datetime
ruby-1.9.2-p290 :005 > u.save
=> true
ruby-1.9.2-p290 :006 > u.starts_at = "2011-01-01 09:00:00"
=> "2011-01-01 09:00:00"
ruby-1.9.2-p290 :007 > u.save
(0.3ms) UPDATE "users" SET "starts_at" = '2011-01-01 08:00:00.000000', "updated_at" = '2011-12-13 10:33:17.919092' WHERE "users"."id" = 1
=> true
I've tested it in this fresh app, because I have a problem with this in larger application. What is going on? I've tried to browse the Rails code, tried to re-copy the relevant code 'by-hand' in console (like update, assign_attributes, even checked time_zone_conversion) and it worked, but not in 'real world'..
looks like you stumbled on a similar issue.
The problem appears to be here:
https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/attribute_methods/dirty.rb#L62
When rails it testing if the value was changed it compares old & new:
old = From cache (which is Time in your current timezone)
new = Time in UTC (+00:00) as saved in the database
If the difference in time is the UTC offset, the above erroneously succeeds (luckly the new cached value holds the intended change).
The next save/update compares with the new (and correct) cached value and marks the field as changed.
EDIT:
Done some tests, this works well for me:
https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb#L50
Change
write_attribute(:#{attr_name}, original_time)
to
write_attribute(:#{attr_name}, time.in_time_zone('UTC').to_s)
Boris

Resources