Thursday, May 16, 2013

Ruby Times & Dates: The Good, The Bad and so on


The Good

The value of a Time object is a measure of epoch seconds (including nanoseconds). Time objects with the same value but different UTC offsets are considered equivalent.

    t  = Time.now # a local Time
    tu = t.getutc

    t.to_i       == tu.to_i      # true
    t            == tu           # true

The value of a Date object is its chronological Julian day. Date objects have a Gregorian calendar start date which is analogous to the UTC offset of a time object. It determines whether the Julian or Gregorian calendar will be used to calculate the calendar year, month and day. Date objects with the same value but different Gregorian calendar start dates are considered equivalent.

    d  = Date.today # a Gregorian Date
    dj = d.julian

    d.jd    == dj.jd     # true
    d       == dj        # true

Ruby 1.9 introduced a means of converting between Time and Date: Time#to_date.

Time#to_date works by calculating a Time object's UTC offset year, month and day according to the Gregorian calendar. Offset Time values before the introduction of the Gregorian calendar use the "proleptic" Gregorian calendar, a logical extension of the Gregorian calendar into the past.

Since we use the Gregorian calendar today, we can say without equivocation that the first day in the Gregorian calendar was Friday, 1582-10-15. This corresponds to Julian day 2299161.

    Time.new(1582, 10, 15).to_date == Date.jd(2299161)       # true
    Time.new(1582, 10, 15).to_date.gregorian?                # true
    Time.new(1582, 10, 15).to_date.to_s                      # "1582-10-15"
    Time.new(1582, 10, 15).to_date == Date.new(1582, 10, 15) # true

The day before the Gregorian calendar was introduced, Julian day 2299160, is better known by its Julian calendar date, Thursday, 1582-10-04. This day is equivalent to the proleptic Gregorian date, 1582-10-14. For values prior to the introduction of the Gregorian calendar, Time#to_date returns a Julian Date object.

    Time.new(1582, 10, 14).to_date == Date.jd(2299160)      # true
    Time.new(1582, 10, 14).to_date.julian?                  # true
    Time.new(1582, 10, 14).to_date.to_s                     # "1582-10-04"
    Time.new(1582, 10, 14).to_date == Date.new(1582, 10, 4) # true

Ruby 1.9 also introduced a means of converting between Date and Time: Date#to_time. Date#to_time returns a local Time object corresponding to midnight on the calendar day of the view.

   Date.new(1582, 10, 15).to_time == Time.new(1582, 10, 15) # true

The Bad and the Ugly

Unlike Time#to_date, Date#to_time doesn't use a fixed calendar to perform the conversion. As a result, conversions involving dates before the introduction of the Gregorian calendar exhibit surprising behaviors.

Converted to a Time, this Date purports to be equivalent to a Time value which actually occurred 10 days earlier.

   Date.new(1582, 10, 4).jd                               # 2299160
   Time.new(1582, 10, 4).to_date.jd                       # 2299150
   Date.new(1582, 10, 4).to_time == Time.new(1582, 10, 4) # true

Such Dates also have the property of not being equivalent to themselves after round-trip conversion:

    d = Date.new(1582, 10, 4)
    d.to_time.to_date == d    # false


Can we do better?

The Ruby 1.9 API is new enough, and the manipulation of pre-Gregorian dates is rare enough that perhaps it's not too late to amend the behavior of Date#to_time. The obscurity of this API is underscored by the sparse documentation:
"Returns a Time object which denotes self."
The behavior of this method is not yet specified at all in the executable Rubyspec project which the maintainers of various Ruby implementations use as their Rosetta Stone.

Time#to_date depends on a Time object's UTC offset to determine the Gregorian calendar day. This is intuitive and supports the obvious use for the conversion: To manipulate the calendar date relative to an instant in time. The value of the Date object is clearly related to the value of the Time object.

The role of the calendar reform start date in Date#to_time does not offer a parallel benefit. The value of the a Time object returned by Date#to_time does not have a clear relationship with the value of the Date object that produced it; instead the objects are related by secondary calculations which are not even guaranteed to be denominated in the same unit.

A more useful and predictable Date#to_time conversion would acknowledge that unlike the Date class, the Time class is bound to the Gregorian calendar. A Time object which is posing as a Julian calendar date is doing so in contravention of its actual value: Its meaning has been corrupted and its use is limited.

I would like to propose that Date#to_time be amended to return a local Time object corresponding to the chronological julian day of the Date value being converted. In Ruby 1.9-2.0, here's what it would look like:

diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index f898c46..8cdff41 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -8600,12 +8600,21 @@ time_to_datetime(VALUE self)
 static VALUE
 date_to_time(VALUE self)
 {
-    get_d1(self);
-
-    return f_local3(rb_cTime,
-                   m_real_year(dat),
-                   INT2FIX(m_mon(dat)),
-                   INT2FIX(m_mday(dat)));
+    get_d1a(self);
+    if (m_julian_p(adat)) {
+      VALUE tmp = d_lite_gregorian(self);
+      get_d1b(tmp);
+      return f_local3(rb_cTime,
+        m_real_year(bdat),
+        INT2FIX(m_mon(bdat)),
+        INT2FIX(m_mday(bdat)));
+    }
+    else {
+      return f_local3(rb_cTime,
+        m_real_year(adat),
+        INT2FIX(m_mon(adat)),
+        INT2FIX(m_mday(adat)));
+    }
 }

Anyone with me on this?

Sunday, August 5, 2012

Just Another Perl Punk

Perl was my gateway programming language. As a kid, I experimented with various flavors of BASIC, Logo, and even a bit of 6502 assembly. But my engagement with Perl was longer-lasting and more consequential. Perl is no longer my tool of choice, and hasn't been for years. But on reflection, I think there are reasons that it that it was a good fit at that time that it was.

In its disdain for academic categories and focus on getting the job done quickly if not necessarily elegantly, Perl embraces an ethos not entirely unlike the DIY ethic associated with punk rock. Punk musicians, unencumbered by music theory or the bounds of conventional good taste, found expressive power in the brief and determined application of a few predictable chords; Perl "hackers", often armed with little more than regular expressions and an acquaintance with the Perl Hash, have accomplished a great deal with improvised CGI scripts and one-line data transforms.

Practitioners of more disciplined languages have frequently condemned the non-conformist excesses of Perl culture. Python countered the anarchic exuberance of TMTOWTDI ("There's More Than One Way To Do It") with TOOWTDI ("There's Only One Way To Do It"). Although I later came to appreciate a more rigorous approach to the craft of making software, I am grateful to the artistic license which characterizes the Perl Way for making it possible to get started on what became my career.

Sunday, April 15, 2012

Semicolons and the limits of Aesthetics-Oriented Development

In case you missed it, there has been a dust-up on Github where the author of "JavaScript: The Good Parts" made it pointedly clear that he was not going to extend JSMin to support an energetic application of one of what he calls "The Awful Parts": Automatic Semicolon Insertion (ASI).

Given his unsparing condemnation of "the tragedy of semicolon insertion," Crockford's position on modifying JSMin to be more tolerant of omitted semicolons is predictable.

I find it more surprising that a major project like Twitter Bootstrap is flouting a canonical prescription for effective JavaScript. After all, it's not just Crockford who thinks that relying on semicolon insertion is a bad idea. Consider Marijn Haverbeke in the more recent, also influential "Eloquent JavaScript":
In some cases, JavaScript allows you to omit the semicolon at the end of a statement. In other cases, it has to be there or strange things will happen. The rules for when it can be safely omitted are complex and weird. In this book, I won't leave out any semicolons, and I strongly urge you to do the same in your own programs.
Or Stoyan Stefanov in "JavaScript Patterns":
Just like with curly braces, you should always use semicolons, even when they are implied by the JavaScript parsers.
Nevertheless, there seems to be a robust "NO;" movement. Just a few days go, the author of the Zepto project announced that henceforth the project will be "semicolon-free". One of the maintainers had spelled out his thoughts in a blog entitled "Semicolons are optional," making the same argument as the Twitter Bootstrappers:
Suppose I have code that works in every JavaScript implementation that I target ... If I run it through your minification tool and it breaks my code, then I'm sad to report that your tool is broken.
Node.js core committer Isaac Schuleter even wrote "An Open Letter to JavaScript Leaders Regarding Semicolons." "The leaders in this language community have given you lies and fear," he proclaims. Without semicolons, "you can write code that you find beautiful."

Perhaps he didn't have Backbone/Underscore/CoffeeScript creator Jeremy Ashkenas in mind when he called down plagues on the leaders of the JavaScript community for irresponsibility, dishonesty and a lack of expertise. Nevertheless, Ashkenas was kind enough to respond:
We put semicolons at the end of each statement, even though it's shorter to leave them out, because it's how JavaScript is written, and how others are comfortable reading it.
He makes a subtle point that is worth magnifying: The semicolon is, with a few exceptions, the way that lines are terminated in JavaScript. JavaScript does not use newlines as a terminator, like Ruby or Python or CoffeeScript do. When semicolons are omitted from the end of a statement, it is an error. An error with well-defined consequences is still an error.

JavaScript's creator Brendan Eich, in his response to today's controversy, is more explicit:
ASI is an error correction procedure. If you start to code as if it were a universal significant-newline rule, you will get in trouble.
It seems to me that this dispute highlights a cultural problem in contemporary software development. Code has an aesthetic component with objective and subjective aspects. The movement for software craftsmanship has called attention to the importance of aesthetic values in the production of code which is less prone to error and easier to maintain. But it is possible to emphasize the aesthetic at the expense of more significant concerns, or to emphasize a subjective aesthetic at the expense of universal values.

Sunday, March 4, 2012

Announcement: Slickback v0.3.0

Version 0.3.0 of Slickback is now available, adding support for SlickGrid v2.0, the current stable version of SlickGrid. Slickback is a javascript library that makes it easier to use Backbone models and collections with the SlickGrid jQuery datagrid. Slickback v0.3.0 is released under the MIT license and available on github.

Monday, September 5, 2011

Announcement: Slickback v0.2.1

Version 0.2.1 of Slickback is now available. Slickback is a javascript library that makes it easier to use Backbone models and collections with the SlickGrid jQuery datagrid. New features in this version include support for displaying and editing Backbone model fields as text, integers, fixed precision numbers, and for selecting field values from dropdowns. Constructors for non-paginated Slickback collections are also new in this version. Slickback v0.2.1 is released under the MIT license and available on github or via npm.

Tuesday, August 23, 2011

Announcement: A Plugin Named Slickback

Slickback version 0.1.0 is now available on Github and via npm. Slickback is a javascript library that adapts Backbone collections to work with SlickGrid, a jQuery-backed datagrid. Extensions to Backbone include support for paginating collections and composing filtered queries using an interface modeled after Ruby on Rails ActiveRecord's "named scopes". Slickback, like Backbone and SlickGrid, is released under the MIT license.

Thursday, April 28, 2011

At home he's a tourist: FFI for Ruby internals

FFI stands for "foreign function interface". That describes the most common use case: Creating Ruby bindings to external native libraries. But it's also possible to use FFI to spelunk Ruby's internals. Consider:


Foreign? Far from it. You've now got access to all of C-ruby's innards. For example, if you're using Ruby 1.9:


With this you've got a handle on the otherwise inaccessible RubyVM core object. As a pointer, it's not all that useful. But with a little more mischief...


... now you can treat the RubyVM core like any other object. One interesting (if unwieldly-named) method that it offers is "core#define_singleton_method". It accepts an InstructionSequence -- compiled YARV bytecode -- and uses it to set up a singleton method in the object where it is invoked. Let's make it a little easier to use:


Now it is easy to create methods using bytecode sequences: