Get hours, seconds and time from Date.day_fraction_to_time

E

Emanuele Ianni

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
 
C

Colin Bartlett

[Note: parts of this message were removed to make it a legal post.]

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-visibility-in-ruby
Here (for example) under Access Control:
http://ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html
...
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
 
E

Emanuele Ianni

Colin said:
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.
 
C

Colin Bartlett

[Note: parts of this message were removed to make it a legal post.]

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?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top