I am doing something that not many do, I am writing a database driver for our database, and an active record adapter. I am having difficulty with Date and Time. Namely, the Ruby doc clearly states that it represents times internally in UTC seconds since the common epoch. However, if you look at the Ruby C code clearly this is not the case, or at least, all operations are performed relative to the default timezone. For instance: Time.new(2001, 12, 3).to_i According to international standards this OUGHT to return 1007337600, but no, it returns 1007355600, despite the Ruby documentation stating that the to_i method is supposed to return seconds since the Epoch. What was left purposefully vague in the doc was that it returns the seconds since the Epoch RELATIVE TO the timezone. Very strange!!! So I have to resort to secondary calculations to account for this behavior in Ruby: true_seconds = time.to_i + time.utc_offset Comments, questions? Now here is the sticking point... On the return path FROM the database when given the good and proper time of 1007337600, how do we convert back to a Time properly? Note that Time.at adjusts for timezone, so all values will be off by the timezone offset. I could daisy chain the construction of multiple Time objects to coerce the values properly, but it should be simpler than that. Thoughts?
on 2012-12-09 15:07
on 2012-12-09 16:10
I basically subtracted the timezone offset for Time.new from my C code and that fixed it all regardless of what default timezone is set.
on 2012-12-10 07:01
Time internally stores the value in UTC, as does #to_i properly return the seconds since the epoch; the issue here is in your construction of it: the default is to use your local timezone. I'm in +11, hence: > > Time.new(2001, 12, 3) => 2001-12-03 00:00:00 +1100 > > Time.new(2001, 12, 3).to_i > => 1007298000 > > Time.new(2001, 12, 3, 0, 0, 0, 0) > => 2001-12-03 00:00:00 +0000 > > Time.new(2001, 12, 3, 0, 0, 0, 0).to_i > => 1007337600
on 2012-12-10 07:02
(In other words: the value is correct, and Time.at will provide you with the correct time (note that Time.at(x).to_i == x, whatever your local timezone), so I suggest you just ensure you're putting the right data in, and pay close attention to the pretty-printed Time values' timezones.
on 2012-12-10 13:50
Am 09.12.2012 15:07, schrieb Robert Buck: > I am doing something that not many do, I am writing a database driver > for our database, and an active record adapter. > > I am having difficulty with Date and Time. Namely, the Ruby doc clearly > states that it represents times internally in UTC seconds since the > common epoch. However, if you look at the Ruby C code clearly this is > not the case, or at least, all operations are performed relative to the > default timezone. For instance: > > Time.new(2001, 12, 3).to_i Time.new uses the system time zone by default: [10] pry(main)> time = Time.new(2001, 12, 3) => 2001-12-03 00:00:00 +0600 [11] pry(main)> time.to_i => 1007316000 [12] pry(main)> time = Time.new(2001, 12, 3, 0, 0, 0, '+00:00') => 2001-12-03 00:00:00 +0000 [13] pry(main)> time.to_i => 1007337600 Or you can use Time.utc: [26] pry(main)> time = Time.utc(2001, 12, 3) => 2001-12-03 00:00:00 UTC [27] pry(main)> time.to_i => 1007337600
on 2012-12-11 02:13
Yes, the big problem here is that, since what I am writing is a database driver, users can throw anything at me. So I have no control over how the Time/Date objects are created. But at the database interface I need to normalize, for both inserts and queries. Since the internal database API assumes those date/times that are passed as millis since the epoch are UTC, I do that conversion explicitly using offsets. For string representations during inserts I provide my own quote_date function in the AR Adapter. On the return route I have resorted to always providing Date/Time in local timezone, applying the opposite adjustment described above.
on 2012-12-11 02:52
On 11 December 2012 11:13, Robert Buck <lists@ruby-forum.com> wrote: > Yes, the big problem here is that, since what I am writing is a database > driver, users can throw anything at me. So I have no control over how > the Time/Date objects are created. If you're writing the driver, you can define the API. If you specify clearly what is allowed and how it will be interpreted, there's not (much of) a problem. > But at the database interface I need > to normalize, for both inserts and queries. Since the internal database > API assumes those date/times that are passed as millis since the epoch > are UTC, I do that conversion explicitly using offsets. I'm still having trouble understanding "since the internal database API assumes those date/times that are passed as millis since the epoch are UTC ..." It's my understand that [milli]seconds-since-the-epoch is a timezone-agnostic value, because there was only one epoch, everywhere in the world, at the same instant, irrespective of what was showing on your wall-clock at that time. The time that was 1_098_293_801 seconds after the epoch may be written as "2004-10-20 17:36:41 UTC" or "2004-10-21 03:36:41 +1000" or whatever, but it's still the same time. Thus, if the user provides you with an integer, no interpretation is required on your part. Store it as an integer. And in Ruby you can use Time.at(seconds_since_epoch) , or if you really want to see it as a UTC string, Time.at(seconds_since_epoch).utc.strftime('%FT%TZ') or whatever. Conversely, if you allow the user to provide you with a string, like 'YYYY-MM-DD' you have to have some explicit rules about how that will be converted to either a timezone-aware Time object, or the equivalent timezone-agnostic seconds-since-the-epoch value. > For string > representations during inserts I provide my own quote_date function in > the AR Adapter. Exactly. > On the return route I have resorted to always providing > Date/Time in local timezone, applying the opposite adjustment described > above. When returning values from the database, assuming they're stored either as timezone-agnostic seconds-since-the-epoch (INTEGER) values, or as timezone-aware DATETIME fields, you can reconstruct a Time object precisely (using either Time::at , or Time::mktime in the long form). There's no "resorting" to anything. -- Matthew Kerwin, B.Sc (CompSci) (Hons) http://matthew.kerwin.net.au/ ABN: 59-013-727-651 "You'll never find a programming language that frees you from the burden of clarifying your ideas." - xkcd
on 2012-12-11 17:26
Robert Buck wrote in post #1088400: > Time.new(2001, 12, 3).to_i > > According to international standards this OUGHT to return 1007337600, > but no, it returns 1007355600, despite the Ruby documentation stating > that the to_i method is supposed to return seconds since the Epoch. It does construct the epoch time, but in this case interprets the Y/M/D you are passing in as for midnight in the local timezone. Just use the method which does what you want, which in this case is Time.gm >> ENV['TZ']="Etc/GMT-5" => "Etc/GMT-5" >> a = Time.mktime(2001,12,3) => Mon Dec 03 00:00:00 +0500 2001 >> a.to_i => 1007319600 >> b = Time.gm(2001,12,3) => Mon Dec 03 00:00:00 UTC 2001 >> b.to_i => 1007337600 [tested with ruby 1.8]
on 2012-12-11 17:27
Brian Candler wrote in post #1088693: > Just use the method which does what you want, which in this case is > Time.gm ... or the better-named Time.utc
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.