Why Software Is Not There Yet

Posted by Chris Sat, 10 Dec 2005 09:06:24 GMT

So when I tried to post the previous entry, I got a 500 error. I don't really know ruby, but there was at least a nice backtrace in the typo log. The operative bit:

RuntimeError (wrong dateTime.iso8601 format):
    /lib/xmlrpc_fix.rb:9:in `dateTime'
    /usr/local/lib/ruby/1.8/xmlrpc/parser.rb:538:in `tag_end'

It was also nice enough to log the incoming XML-RPC data. The operative bit of that:

{"name"=>"dateCreated",
 "value"=>{"dateTime.iso8601"=>"20051210T08:28:40+0000"}}

Now, thanks to open source and all that jazz, I could pretty easily find this code, repeated here since it's short:

def self.dateTime(str)
  if str =~ /^(-?\d\d\d\d)(\d\d)(\d\d)T(\d\d):(\d\d):(\d\d)Z?$/ then
    a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
    XMLRPC::DateTime.new(*a)
  else
    raise "wrong dateTime.iso8601 format"
  end
end

For what it's worth, this is overriding a method from ruby's own xmlrpc library, the version of which on my system being very similar but not accepting the final "Z" (for Zulu AKA GMT AKA UTC). That's not good enough, since MarsEdit has chosen to send the time zone, even though it's Zulu, as +0000.

But also on the web, I found this, and if you click on the "Source" bit of the dateTime method, you see a much longer and fancier function that attempts to actually handle ISO8601, including the format MarsEdit sends. There we see:

when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
  a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
  if $7
    ofs = $8.to_i*3600 + $9.to_i*60
    ofs = -ofs if $7=='+'
    utc = Time.utc(a.reverse) + ofs
    a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
  end
  XMLRPC::DateTime.new(*a)

Thinking that was what I needed, I updated the overridden method in Typo to use it. But alas:

TypeError (cannot convert Array into Integer):
    /lib/xmlrpc_fix.rb:11:in `utc'
    /lib/xmlrpc_fix.rb:11:in `dateTime'

What's the deal? A little research, and it turns out that in ruby you write *a to get the list of elements in a, rather than the Array itself. Don't ask me why it's written without the * in what appears to be official ruby library code -- my guess is that it's for a newer version of ruby where you can pass an Array to Time.utc. In any case, I tried:

utc = Time.utc(*a.reverse) + ofs

Behold, a new error!

ArgumentError (argument out of range):
    /lib/xmlrpc_fix.rb:11:in `utc'
    /lib/xmlrpc_fix.rb:11:in `dateTime'

Looking more closely, that a.reverse falls under suspicion. Time.utc seems to want (year, month, day, hour, min, sec), and that is in fact how it's coming out of the regex. So why reverse it? I haven't the foggiest clue! I took out the reverse:

utc = Time.utc(*a) + ofs

and voila! The post went through. I'm still totally stumped on how that could possibly ever work as written. Maybe some reader will help me out here. Of course, I only have the one reader (hi, Josh!), so maybe not.

Posted in ,  | Tags , ,  | no comments

Comments

(leave url/email »)

   Comment Markup Help Preview comment