MTJ Hax

Ruby 1.8 strftime works differently on Linux and Windows

Posted in code, ruby, ruby on rails by mtjhax on December 27, 2010

Running Ruby 1.8 on Linux, cygwin, Mac, any POSIX environment:

now = Time.now
=> Mon Dec 27 18:28:28 -0500 2010
now.strftime("%l") # that's a lowercase "L"
=> " 6"

Under Windows using the exact same Ruby version and patch level:

now = Time.now
=> Mon Dec 27 18:28:28 -0500 2010
now.strftime("%l") # that's a lowercase "L"
=> ""

What? It turns out in Ruby 1.8 they cheated by simply calling the standard C strftime() function to implement Ruby’s strftime method. Because different compilers implement strftime() differently, compiling it under Windows results in a different set of supported % directives. Specifically, it looks like under Windows only the basic ANSI C version is implemented, but under POSIX compilers you get a mix of directives defined by ANSI and other standards like Gnu and Single Unix Specification. The “%l” directive was not even listed in the Ruby 1.8 docs — it worked by accident.

The good news is in Rails 1.9 they appear to have implemented their own strftime for consistency, instead of handing it off to the C compiler. If you need to fix this in the meantime, I provide the following monkey patch (which I simply place in my Rails config/initializers folder on any Windows developer machine):

# monkey patch Ruby strftime methods to implement
# %l directive that is missing in Windows Ruby
#
# as a monkey patch, just use this on your Windows
# development boxes to bridge the gap, it's not
# recommended for production
#
# notes:
# - you may want to add support for other missing directives
# - check your RUBY_PLATFORM and make sure it is in the regexp below
#
if RUBY_PLATFORM =~ /mingw32|mingw64|mswin32|mswin64/

  class Time
    alias_method :original_strftime, :strftime
    def strftime(fmt)
      hour12 = "%2d" % ((hour + 11) % 12 + 1)
      original_strftime(fmt.gsub(/%l/, hour12))
    end
  end
 
  class Date
    alias_method :original_strftime, :strftime
    def strftime(fmt)
      hour12 = "%2d" % ((hour + 11) % 12 + 1)
      original_strftime(fmt.gsub(/%l/, hour12))
    end
  end

end
Advertisements
Tagged with: , ,