The Ultimate Guide to Choosing WordPress Pros and Cons
Fun times with ruby
1. Fun Times
(and DateTimes and Dates and zones)
in Ruby
OR: Accurate Times At Railsmont High
OR: UTC and You and Me
Times, time zones, times without dates,
and other Ruby and Rails techniques
4. Why is time important?
Track when something happened
• Jane signed in at 4:12pm
• Meter reading for interval starting at 6:45am
Schedule when something will happen
• Send an email at 8:00am
• Check for new users every hour on the hour
6. Why is time hard?
• Daylight savings and “summer time”
• Time zones change over time
• Displaying times differently for users based on
their locale
• Caching makes it hard to show times relative to
“now”
7. What is UTC?
“Coordinated Universal Time”
International Standard
Other time zones can be described with the
number of hours and minutes with which they
deviate from UTC as an offset
9. UTC Offsets
Offsets are defined as “+/- hh:mm”
As of 7:30pm in Washington, DC on March 5th, here are
some offsets:
• DC: -05:00
• SF: -08:00
• London: +00:00 (London is UTC during the winter)
• Bangalore: +05:30 (not always round hours!)
11. Why use UTC?
In the database:
• UTC always moves forward
• No need to consider DST when calculating
distance between two times
• Time zones change over time!*
12. Why use UTC?
Outside the database:
• Avoid confusion over what zone you are in
• Easy to convert back to local time just before
you render (after manipulation or querying is
done)
• UTC is useful for cached pages and partials
13. Time Zones
Rails uses the tzinfo gem, which tracks the IANA
list of official time zones
ActiveSupport::TimeZone.all gives you a list of all
allowed time zones. They are named on a
region/city basis in most cases:
“America/New_York”, “Asia/Tokyo”,
“Africa/Khartoum”, etc.
14. Avoid Date.today
Date.today uses the server time zone
Use Time.now.in_time_zone(time_zone).to_date
to get the real date from the user’s perspective
ActiveSupport’s Time.current will always give you
UTC if you’ve set your default time zone to UTC
(Time.now will use the server time zone)
If you need to parse times in a local reference,
wrap it in a `Time.use_zone` block (more later)
15. Time vs. DateTime
• Previously, Time used to be bounded, and couldn’t
represent dates before 1970 or far in the future
• DateTime does not consider leap seconds or track
summer time rules (daylight savings)
• In Ruby (except for 1.9.3), Time, built in C, appears
to be faster in most cases
• Now that Ruby 2.0 supports unbounded Time, use it
whenever possible to avoid confusion
16. Ruby Tips
Just use UTC.
Everyone thinks their app won’t need to worry
about time zones. You will regret it later if you
don’t follow this rule.
# config/application.rb
# Set time zone
config.active_record.default_time_zone = “UTC”
17. ActiveRecord types
• date stores a date with no time information,
“2015-03-05”
• datetime stores a timestamp, or a fixed point in
time: “2015-03-06 00:30:00”
• time stores a time of day with no tie to a specific
date “09:00:00”
19. ActiveRecord times
Be careful when using time attributes for times
without dates! ActiveRecord returns time
attributes as UTC times on 1/1/2000.
# user’s time zone is “America/New_York”
messaging_preferences.update!(morning_update_time: “07:00”)
messaging_preferences.morning_update_time
=> 2000-01-01 07:00:00 UTC
20. Fix it everywhere
class TimeWithoutDateToTime
def initialize(undated_time, date, time_zone)
@undated_time = undated_time
@date = date
@time_zone = time_zone
end
def time
date.in_time_zone(time_zone).change(
hour: hour,
min: min,
sec: sec
)
end
protected
attr_reader :date, :time_zone, :undated_time
private
delegate :hour, :min, :sec, to: :undated_time
end
21. Schema best practices
• Name database columns that store times with
the suffix “_at”, such as “signed_up_at”
• Name database columns that store dates with
the suffix “_on”, such as “joined_on”
• Name database columns that store times not
tied to a particular date with the suffix “_time”,
such as “morning_update_scheduled_time”
23. Taking User Input
Users expect to be able to specify times in their
own time zone.
Solution: process user input with an around block
and Time.use_zone. Create times in the block
with Time.zone.local.
24. User Input Example
around_filter :set_user_time_zone
protected
# be sure to use Time.zone.local rather than
# Time.new to instantiate any time objects in
# the block that are in the user’s time zone
def set_user_time_zone
Time.use_zone(current_user.time_zone) { yield }
end
25. JavaScript
Use Moment.js and Moment-Timezone
• Convenient, ActiveSupport-like methods for
manipulation
• Much better query and display methods than
JS-native Date objects
• Locale-based timezones
27. Parsing time
Chronic (mojombo/chronic) provides natural language
parsing for times in Ruby
Chronic.parse("last Saturday”)
Use it over Time.parse when you need to take in a
string from an external source and turn it into a time
(even strings you suspect are properly formatted)
30. Guidelines for testing
• Any time your test depends on the relation of some
time to “now”, always use Timecop to freeze the
time
• Use Timecop to travel between times when testing
scenarios where time elapses
• Test around times where your local time is likely to
be a different date than UTC
• Only change the local time zone in a test with an
“around” block to prevent leaks into other tests
31. Caching
• For pages or partials that will be cached, don’t
display relative time using Rails view helpers.
Instead, render the moment as a fixed time in a
default time zone
• Store the time on the HTML tag as an attribute:
<abbr class="timeago" title="2008-07-17T09:24:17Z">July 17, 2008</abbr>
• Use the JS timeago library (timeago.yarp.com)
to re-render timestamp tags as relative times
33. Further Reading
• Joel Olivera’s awesome Boston.rb talk:
http://bit.ly/1AYs6Or
• Ruby Time API:
http://ruby-doc.org//core-2.2.0/Time.html
• Rails TimeWithZone API:
http://apidock.com/rails/ActiveSupport/TimeWithZone
• SvN (37signals/Basecamp) on caching and JS
reformatting: https://signalvnoise.com/posts/1557-
javascript-makes-relative-times-compatible-with-caching
34. Thanks!
I love talking about Ruby and Rails, so feel free to
reach out to me with questions!
email: geoff@fivetool.io
Github: geoffharcourt
Twitter: @geoffharcourt
Editor's Notes
In 2011, Russia ended use of DST time
US regions have changed DST policies over time
TZ regions have merged and divided
In 2011, Russia ended use of DST time
US regions have changed DST policies over time
TZ regions have merged and divided
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time
ActiveRecord and ActiveSupport don’t help much here, as when you pull a timestamp at the console you often get it in local time