fun times with ruby

34
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

Upload: geoff-harcourt

Post on 28-Jul-2015

192 views

Category:

Technology


2 download

TRANSCRIPT

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 2. Hi, Im Geoff Im the co-founder at Cortex http://cortexintel.com 3. Best label maker use ever 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 5. Why is time hard? 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 8. Everywhere a different time 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!) 10. Why use UTC? 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 users perspective ActiveSupports Time.current will always give you UTC if youve 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 couldnt 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 wont need to worry about time zones. You will regret it later if you dont 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 18. Times without dates 19. ActiveRecord times Be careful when using time attributes for times without dates! ActiveRecord returns time attributes as UTC times on 1/1/2000. # users 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 22. PostgreSQL http://www.postgresql.org/docs/9.1/static/functions -datetime.html Check out PostgreSQLs documentation on date/time functions. SELECT * FROM measurements WHERE measured_at AT TIME ZONE UTC AT TIME ZONE America/New_York BETWEEN x and y 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 users 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 26. Parsing time 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) 28. Testing 29. Testing Tools Timecop https://github.com/travisjeffery/timecop Freeze time at any point, travel to any time Zonebie https://github.com/alindeman/zonebie Changes your apps timezone to help find bugs related to times and zones 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, dont 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: July 17, 2008 Use the JS timeago library (timeago.yarp.com) to re-render timestamp tags as relative times 32. Questions? 33. Further Reading Joel Oliveras 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: [email protected] Github: geoffharcourt Twitter: @geoffharcourt