Ruby 1.8 strftime works differently on Linux and Windows

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

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.