Today, I was working on my kegtracker application and came upon a learning opportunity. Part of this application involves the creation of ‘batches’ based on available flavors (and some other information). Every batch has a lot number, which we’ve developed a nomenclature for. It’s a pretty simple concept FlavorAbbreviation-DateOfBatch-A comes first, and the second one is FlavorAbbreviation-DateOfBatch-B. Right now, two batches a day is the limit, but the pattern continues based on the DateofBatch through the alphabet.
That means, the application must check every time a new batch has been created, to determine how many other batches of the flavor have been created on the same date. I wrote my test, my test failed, I played around with StackOverflow and the Rails API to learn about TimeWithZone and how to take the created_at timestamp (which contains the date and time of a record creation for those that don’t know) and convert it into just the date portion so I could use it for comparison in the future.
Then of course, I log into my rails console (sandbox version which removes all changes when you’re done) and create a test record, and then compare the
Date.today to to the
created_at attribute converted to just the date. Ideally this returned true, but unfortunately it did not. Even though it was around 7 PM on Monday July 25th at my desk, the
created_at date portion returned tomorrow, Tuesday July 26th, and thus it’s not the same. Obviously I had run into some time zone issues based on Rails defaults I hadn’t worked with before..
created_at attribute is an automatic creation of Rails whenever you create a new Model (database as it were for non Rails people). It also creates an
updated_at attribute as well. These attributes are pretty self explanatory and they are filled in automatically by Rails when the field is created or updated respectively. However, Rails, in a move that makes sense when I think about it, does not default to the ‘local’ (i.e. mine, or yours) timezone when an application is created, but instead it has a default time zone, and that default time zone is, shockingly, not in California, but over there in Europe where for some reason ‘GMT’ is located (growing up I though Greenwich Mean Time was in Connecticut, but that was silly now wasn’t it). So, for instance, if it’s after 4 PM when I get home from work (and it is) and I create a new record (and I often do) it turns out that my application will believe that the record was actually created ‘tomorrow’ as that’s what time it is where GMT is located. This is a problem for me with this application because the actual date is important at time. (This blog application for instance, at this time, I was not worried about the time zone, so when I first publish an article it indicates it was published 11 hours ago which is no big deal for now. It still sorts newest to oldest since they all have the same off set, and after finishing up this research I realize I should be able to tweak it so that it shows the relevant time to whomever is visiting the site, but that’s not vital right now).
Of course, Rails, as always, may have a series of defaults that make it easy to get started, but it is also quite easy to change these defaults if you want/need to, and at this point I needed to change the default time zone of this app. First I had to figure out how to identify my (Pacific) time zone. That was made rather easy by
bin/rake time:zones:us which, unsurprisingly does give you the US timezones, as ‘offsets’ to the UTC used for
created_at. (UTC and GMT seem to be the same thing, I don’t know why they changed it, maybe people got it confused with Connecticut like I did?). The Pacific timezone is UTC -8. When you run that sweet rake command above you also get a name for your time zone. This is key, because, honestly, setting the time zone is really as simple as the listed name.
Thanks to work done before me by the codedecoder, I was easily able to alter the
config/application.rb as the article guides you and set it equal to the Pacific Time Zone, and voila that should work, right? Well, it didn’t seem to, at first. (Did you know that Date.today and Date.current don’t always give the same date, I learned that today). After the change of the config file as suggested by the codedecoder, the Date.today and Date.current did return the same value, but the newly created records still had their
created_at set to GMT/UTC and thus my database thought they had been created tomorrow. A quick web search, led me to, as it almost always does, a StackOverflow question that points out that though there is a way you can change the way
created_at is stored, you shouldn’t. (This is for a variety of reasons and built in functionalities of Rails that you will most likely throw off be not using UTC in the
created_at storage). When you look at the record as a whole object,
created_at is still recorded in GMT/UMT, but thanks to the change in the
config.time_zone setting, when I access
created_at on its own (as
created_at on its own is a member of the
ActiveSupport::TimeWithZone class), it does reflect the proper local time the record was created, and that was the goal, and a quick console test indicates that comparing the date portion of
created_at NOW with
Date.today does return a true response, which was my goal.
So, by default, Rails will not store or present to you the local DateTime of any records you create (or alter). However, you can easily tweak your application so that while you don’t mess with the default storage as a whole, when you call the specific creation, or update date, on its own, it will reflect the time zone you wish it to reflect, while still keeping the default storage for other Rails specific stuff. If nothing in your application has any location based time or date requirements, you can just leave it alone. However, if you want to, you can change one simple line of code and thus establish a ‘time zone’ location for your application and Rails easily adapts to that going forward.
Another example of why Rails is the answer for me. Sure we’ll set up the foundation for you so you can built the important stuff, but you’ll still be able to tweak (most) of the foundation pretty easily if need be.