Forum: Ruby Get hours, seconds and time from Date.day_fraction_to_time

648b40ee271355e605029dd38cf60cef?d=identicon&s=25 Emanuele Ianni (dierre)
on 2010-09-17 11:25
      start = DateTime.now
      sleep 15
      stop = DateTime.now
      #minutes
      puts ((stop-start) * 24 * 60).to_i

      hours,minutes,seconds,frac = Date.day_fraction_to_time(stop-start)

I have the following error:

    `<main>': private method `day_fraction_to_time' called for
Date:Class (NoMethodError)

I've checked */usr/lib/ruby/1.9.1/date.rb* and I've found it:

    def day_fraction_to_time(fr) # :nodoc:
      ss,  fr = fr.divmod(SECONDS_IN_DAY) # 4p
      h,   ss = ss.divmod(3600)
      min, s  = ss.divmod(60)
      return h, min, s, fr * 86400
    end

But I have no problem if I run it with ruby1.8.
*/usr/lib/ruby/1.8/date.rb* gives me:

      def self.day_fraction_to_time(fr)
        ss,  fr = fr.divmod(SECONDS_IN_DAY) # 4p
        h,   ss = ss.divmod(3600)
        min, s  = ss.divmod(60)
        return h, min, s, fr
      end

So I went to see the documentation(1.9)[1] and there's no trace of this
method. I know it's a dumb question, but why did they remove it? There
is even this example on how to use the method in
*/usr/lib/ruby/1.9.1/date.rb*:

     def secs_to_new_year(now = DateTime::now())
         new_year = DateTime.new(now.year + 1, 1, 1)
         dif = new_year - now
         hours, mins, secs, ignore_fractions =
Date::day_fraction_to_time(dif)
         return hours * 60 * 60 + mins * 60 + secs
     end

but I'm still getting the error:

    test.rb:24:in `secs_to_new_year': private method
`day_fraction_to_time' called for Date:Class (NoMethodError)
      from test.rb:28:in `<main>'


  [1]: http://ruby-doc.org/core-1.9/classes/Date.html
C49d5fa17af413a0334a967c5251bf07?d=identicon&s=25 Colin Bartlett (Guest)
on 2010-09-18 04:04
(Received via mailing list)
See below at *** for two "work-arounds" which seem to allow you to do
what
you want in Ruby 1.9 as well as in 1.8.6 and 1.8.7.

Some links for explanations of private and public (and protected)
methods:
http://weblog.jamisbuck.org/2007/2/23/method-visib...
Here (for example) under Access Control:
http://ruby-doc.org/docs/ProgrammingRuby/html/tut_...
...
Private methods cannot be called with an explicit receiver. Because you
cannot specify an object when using them, private methods can be called
only
in the defining class and by direct descendents within that same object.

Compare the "full" code for the 1.8 and 1.9 versions of Date. (See just
below.) The 1.8 version can only be used as a class method
Date.day_fraction_to_time, but the 1.9 version can also be used as a
*private* instance method.

That said, I don't know why the class (singleton) method
day_fraction_to_time was changed from public in Date before 1.9 to
private
in 1.9, and I would welcome any enlightened suggestions. (One reason for
making a method private is, I assume, to warn people that an internal
implementation detail might change, so it would be unwise to use such a
private method in case its implementation changes in the future. That
gives
more freedom to the code originator for future changes.)

# In 1.8:
class Date
  #...
    def self.day_fraction_to_time(fr)
  #...
    end
  #...
end

# But in 1.9:
class Date
  #...
  t = Module.new do
    private
  #...
    def day_fraction_to_time(fr) # :nodoc:
  #...
    end
  #...
  end
  extend t
  include t
  #...
end


*** work arounds

require "date"

start = DateTime.now
sleep 3
stop = DateTime.now
# minutes
puts( ((stop - start) * 24 * 60).to_i )

puts "Date.day_fraction_to_time"
begin
    hours, minutes, seconds, frac =
      Date.day_fraction_to_time( stop - start ) # works in 1.8.6 & 1.8.7
    p hours, minutes, seconds, frac
rescue # raises exception in 1.9.1
    p $!
end

puts "Date.day_fraction_to_time using __send__"
hours, minutes, seconds, frac =
Date.__send__( :day_fraction_to_time, stop - start )
p hours, minutes, seconds, frac

puts "Date.day_fraction_to_time using wrapper"
class Date
    class << self
      def wrap_day_fraction_to_time( day_frac )
        day_fraction_to_time( day_frac )
      end
   end
end
hours, minutes, seconds, frac =
    Date.wrap_day_fraction_to_time( stop - start )
p hours, minutes, seconds, frac
648b40ee271355e605029dd38cf60cef?d=identicon&s=25 Emanuele Ianni (dierre)
on 2010-09-18 13:08
Colin Bartlett wrote:
> puts "Date.day_fraction_to_time using wrapper"
> class Date
>     class << self
>       def wrap_day_fraction_to_time( day_frac )
>         day_fraction_to_time( day_frac )
>       end
>    end
> end
> hours, minutes, seconds, frac =
>     Date.wrap_day_fraction_to_time( stop - start )
> p hours, minutes, seconds, frac

Thanks for the answer. Could you examplain me the wrapper in detail,
please? I'm assuming you're extending the Date class with an anonymous
class but it's really not clear to me the class << self declaration.
C49d5fa17af413a0334a967c5251bf07?d=identicon&s=25 Colin Bartlett (Guest)
on 2010-09-19 20:25
(Received via mailing list)
On Sat, Sep 18, 2010 at 12:09 PM, Emanuele Ianni <dierre@gmail.com>
wrote:

> >     Date.wrap_day_fraction_to_time( stop - start )
> > p hours, minutes, seconds, frac
>
> Thanks for the answer. Could you explain me the wrapper in detail,
> please? I'm assuming you're extending the Date class with an anonymous
>  class but it's really not clear to me the class << self declaration.

I'm not surprised that "class << self" isn't clear to you. It wasn't
clear
to me when I first saw it, and I'm not sure I'm 100% there yet! I hope
the
following is right and helps. (Corrections are welcome.)

Here's a link to the online first edition of Programming Ruby
http://ruby-doc.org/docs/ProgrammingRuby/html/classes.html
... Object-Specific Classes ...

Basically you can extend any object with methods which won't be
available to
any other object unless you also extend that other object.

In the version of Date with Ruby 1.9 one way used is to set up an
(anonymous?) module and then use "extend", as shown in the extract from
(1.9) Date in my first post in this thread.

Another way is to use the "class << object" notation, which (I think)
says
evaluate the code in "class<<...end" in the context of the specified
object,
extending that object (as singleton methods of that object) with any
methods
defined in "class<<...end". For example:
obj = Array.new
(r = obj.new_singleton_method) rescue p $! #=> undefined method error
class << obj
    def new_singleton_method(); "Karel Capek"; end
end
r = obj.new_singleton_method #=> "Karel Capek"

So in:
class Date
    class << self
      def wrap_day_fraction_to_time( day_frac )
        day_fraction_to_time( day_frac )
      end
    end
end
the object being extended in "class<<self...end" is whatever "self" is,
and
in the context of the "class Date" self is the Date class. And the
private
method "day_fraction_to_time" is accessible from "within" the object, so
we
can write a public method (callable as Date.wrap_day_fraction_to_time)
which
calls the private method "day_fraction_to_time".

Also, instead of writing a wrapper method, the following (simpler) way
also
seems to work:
hours = mins = secs = nil
hours, mins, secs = Date.day_fraction_to_time( 0.03125 ) rescue p $!
p hours, mins, secs #=> nil, nil, nil
class Date
    class << self
      public :day_fraction_to_time
    end
end
hours, mins, secs = Date.day_fraction_to_time( 0.03125 ) rescue p $!
p hours, mins, secs #=> 0, 45, 0

In the context of a private method, I'm not at all sure that putting a
public wrapper method which calls it is better than simply making the
private method public: I think that any downsides/problems with making
the
private method public will also apply to wrapping it. Does anyone know
(or
think) differently?
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.