Rails Date MySQL operational differences

Hey Ladies and Gentlemen,

I have been working with a couple of date operations in rails and my answers have been pretty consistently inconsistent. I am a bit puzzled about the difference between: 1) days_stat = user.daily_stats.where({:day => dte.midnight.. (dte.midnight + 1.day)}).first 2) days_stat = user.daily_stats.where('date(day) = ?', dte).first

where dte = "2010-11-04".to_date

#1 consistently gets me a the next day's record. #2 seems to work right, but will it be able to do the UTC conversion into EST?

Let me know if that makes sense.

Thanks and Regards.

bertly_the_coder wrote in post #960002:

I have been working with a couple of date operations in rails and my answers have been pretty consistently inconsistent. I am a bit puzzled about the difference between: 1) days_stat = user.daily_stats.where({:day => dte.midnight.. (dte.midnight + 1.day)}).first 2) days_stat = user.daily_stats.where('date(day) = ?', dte).first

I find it helpful to simplify how I think about what a Date or Time object really is. Let's think about it as it relates to UNIX time. UNIX time is represented as the number of seconds since January 1, 1970 UTC. So that means UNIX time is nothing more than a simple integer value (stored internally as a long).

Now when we ask for a range, as you show in #1 above, we are basically creating a range of integer values. Imagine if exactly midnight today UTC was assigned the value 0:

today_midnight = today.at_midnight => 0

tomorrow_midnight = today_midnight + 1.day => 86400

today_midnight...tomorrow_midnight => 0...86400

Notice I used the exclusive range operator (...) rather than the inclusive (..). We want to include exactly midnight_today, but exclude exactly midnight_tomorrow.

Now assuming all dates and times are stored in the database in UTC, which I believe is the default in current versions of Rails, then thinking in these terms works.

Now some questions to clarify your questions:

1. Are the dates your working with stored in the database as date or datetime columns? 2. If you are looking for a specific record to be returned by .first are you specifying an order by clause containing the relevant data/datatime column?

In case of #1 above, if your date is stored in a date column as opposed to datetime then a simple comparison will suffice:

today = Date.today days_stat = user.daily_stats.where({ :day => today }).first

Looking at this from our imaginary date reference where midnight today = 0 you're basically saying this:

days_stat = user.daily_stats.where("day = 0").first

However, if :day is stored in a datetime field then it's safest, and most efficient, assuming the :day column is indexed, to use the exclusive range method:

today = Date.today tomorrow = today + 1.day days_stat = user.daily_stats.where({ :day => today...tomorrow }).first

which is like saying days_stat = user.daily_stats.where("day >= 0 and day < 86400").first

Another possible inconsistency relates to my question #2. Assuming it is possible to have more that one daily_stats record for a given day and there is no ordering specified. Then calling first would be like saying, "Give me ANY one of the records where day = today." You cannot guarantee that records will be returned in a specific order unless you explicitly specify that ordering in the SQL. Beyond that if the only ordering specified was on your day column then rows with the exact date value would have arbitrary ordering, which will likely vary depending on what database back-end is used.

One last note about the representation of dates in other times zones (like EST). This should not affect how you query your database, only how the resulting date value is displayed to the end user. Store all dates and times in UTC. Then setup your Rails application to have a default local zone (i.e. UTC). If you want your application to be able to display dates and time in the end user's local time zone then you need ask them what that is when they sign up. Then store that timezone in their user account record.

Translation of dates and times (as well as translations of numbers and currency BTW) are part of the View layer's responsibility not the model. However, I often see this responsibility delegated to model objects, and in this case I think that's fine because it is convenient to do so.

Not all systems do this mixing of responsibilities though. Rudy/Rails happens to be one that does. Other frameworks use separate formatter classes that delegate this responsibility out to view layer objects. Examples are (SimpleDateFormat in Java and NSDateFormatter in Objective-C).

What's important to you is that the storage of date and time representations at the actual model layer is consistent. If times are stored in UTC, always store times in UTC. Don't mix representations in the database.