2009-11-05 29 views
20

Ruby có thể làm điều gì đó như thế này không?Ruby có thể in ra sự khác biệt về thời gian (thời gian) không?

irb(main):001:0> start = Time.now 
=> Thu Nov 05 01:02:54 -0800 2009 

irb(main):002:0> Time.now - start 
=> 25.239 

irb(main):003:0> (Time.now - start).duration 
=> "25 seconds" 

(phương pháp thời gian không tồn tại bây giờ) ... và tương tự, báo cáo

23 minutes and 35 seconds 
1 hour and 33 minutes 
2 days and 3 hours 

(hoặc báo cáo toàn bộ thời gian, lên đến bao nhiêu giây, hoặc báo cáo lên đến 2 con số và các đơn vị (nếu ngày và giờ được báo cáo, sau đó không cần phải biết bao nhiêu phút))

+1

bây giờ tôi biết Ruby hơn. .. có thể một 'Time.now.how_long_since start' hoặc' Time.now.duration_since start' có thể thanh lịch hơn? –

Trả lời

35

Đây là cách nhanh chóng và đơn giản để thực hiện việc này. Đặt số đo được xác định trước cho giây, phút, giờ và ngày. Sau đó, tùy thuộc vào kích thước của số, xuất chuỗi thích hợp với các đơn vị đó. Chúng tôi sẽ mở rộng Numeric để bạn có thể gọi phương thức trên bất kỳ lớp số nào (Fixnum, Bignum hoặc trong trường hợp của bạn Float).

class Numeric 
    def duration 
    secs = self.to_int 
    mins = secs/60 
    hours = mins/60 
    days = hours/24 

    if days > 0 
     "#{days} days and #{hours % 24} hours" 
    elsif hours > 0 
     "#{hours} hours and #{mins % 60} minutes" 
    elsif mins > 0 
     "#{mins} minutes and #{secs % 60} seconds" 
    elsif secs >= 0 
     "#{secs} seconds" 
    end 
    end 
end 
+2

Có lẽ một số xử lý bổ sung nên được thêm vào trong trường hợp một hoặc nhiều mặt hàng bằng một (1 giờ và 1 phút => 1 giờ và 1 phút). –

+0

Rất gọn gàng. Tôi thích nó: D – FelipeC

+1

Tôi đã nói quá sớm; nó không hoạt động chính xác. Phải là một cái gì đó như: "# {days} ngày và # {giờ% 24} giờ" không phải "# {days} ngày và # {giờ% ngày} giờ" – FelipeC

3

Hãy xem phương thức Rails DateHelper.distance_of_time_in_words. Nó sẽ cho bạn một nơi khởi đầu tuyệt vời. Mặc dù được nạp với số ma thuật, cách tiếp cận này sẽ làm việc cho bạn.

-1
time_difference = current_time - old_time 



def seconds_fraction_to_time(time_difference) 
    days = hours = mins = 0 
     mins = (seconds/60).to_i 
     seconds = (seconds % 60).to_i 
     hours = (mins/60).to_i 
     mins = (mins % 60).to_i 
     days = (hours/24).to_i 
     hours = (hours % 24).to_i 
    return [days,hours,mins,seconds] 
    end 

sau đó bạn có thể in nó ra gì bao giờ cách bạn muốn,

tức

if(days > 0) 
return "#{days} Days #{hours} Hours" 
else 
return "#{hours} Hours #{mins} Minutes" 
end 
2

Thời gian khác biệt một chuỗi được in đẹp:

class Numeric 
    def duration 
    rest, secs = self.divmod(60) # self is the time difference t2 - t1 
    rest, mins = rest.divmod(60) 
    days, hours = rest.divmod(24) 

    # the above can be factored out as: 
    # days, hours, mins, secs = self.duration_as_arr 
    # 
    # this is not so great, because it could include zero values: 
    # self.duration_as_arr.zip ['Days','Hours','Minutes','Seconds']).flatten.join ' ' 

    result = [] 
    result << "#{days} Days" if days > 0 
    result << "#{hours} Hours" if hours > 0 
    result << "#{mins} Minutes" if mins > 0 
    result << "#{secs} Seconds" if secs > 0 
    return result.join(' ') 
    end 
    end 

chênh giờ như một mảng:

class Numeric 
    def duration_as_arr 
     rest, secs = self.divmod(60) 
     rest, mins = rest.divmod(60) 
     days, hours = rest.divmod(24) 
     [days, hours, mins, secs] 
    end 
    end 

Ví dụ:

x = 1209801.079257 
    x.duration 
    => "14 Days 3 Minutes 21.079257000004873 Seconds" 
    x.duration_as_arr 
    => [14, 0, 3, 21.079257000004873] 
+0

Trong nhận xét zip của bạn, để không bao gồm các giá trị bằng 0, bạn có thể làm: 'self.duration_as_arr.zip ['Days', 'Hours', 'Minutes', 'Seconds']). Drop_while {| n, u | n == 0} .flatten.join '' ' – rosenfeld

1

xây dựng trên Michael Richard's answer, đây là một sự thay thế cho khối if mà được pluralization tiếng Anh đúng, và sẽ không nói những câu như "14 ngày và 0 giờ":

if days > 0 
    hour_remainder = hours % 24 
    if hour_remainder > 0 
    hour_str = hour_remainder == 1 ? 'hour' : 'hours' 
    "#{days} days and #{hour_remainder} #{hour_str}" 
    elsif days == 1 
    "#{days} day" 
    else 
    "#{days} days" 
    end 
elsif hours > 0 
    min_remainder = mins % 60 
    if min_remainder > 0 
    min_str = min_remainder == 1 ? 'minute' : 'minutes' 
    "#{hours} hours and #{min_remainder} #{min_str}" 
    elsif hours == 1 
    "#{hours} hour" 
    else 
    "#{hours} hours" 
    end 
elsif mins > 0 
    sec_remainder = secs % 60 
    if sec_remainder > 0 
    sec_str = sec_remainder == 1 ? 'second' : 'seconds' 
    "#{mins} minutes and #{sec_remainder} #{sec_str}" 
    elsif minutes == 1 
    "#{mins} minute" 
    else 
    "#{mins} minutes" 
    end 
elsif secs == 1 
    "#{secs} second" 
elsif secs >= 0 
    "#{secs} seconds" 
end 
1

Tôi không thể chịu đựng để đưa ra một giải pháp chung chung ở đây - mặc dù: có một năm 365 ngày?

Bổ sung Tôi đặt một số abs khi tự chuyển đổi.to_int

class Numeric 
    def duration 
     steps=[60, 60, 24, 365,0] 
     names=[:seconds, :minutes, :hours, :days, :years] 
     results=[] 
     stepper = self.to_int.abs 
     steps.each { |div| 
      if stepper>0 
       if div>0 
        results<<stepper % div 
        stepper/=div 
       else 
        results << stepper 
       end 
      end 
     } 
     e= results.empty? ? 0 : results.count-1 
     mt= e>0 ? results[e-1] : 0 
     et=results[e] || 0 

     et.to_s+" "+names[e].to_s + (mt>0 ? " "+mt.to_s+" "+names[e-1].to_s : '') 
    end 
end 

và với dịch

class Numeric 
    def i18n_duration 
     steps=[60, 60, 24, 365,0] 
     names=[:seconds, :minutes, :hours, :days, :years] 
     results=[] 
     stepper = self.to_int.abs 
     steps.each { |div| 
      if stepper>0 
       if div>0 
        results<<stepper % div 
        stepper/=div 
       else 
        results << stepper 
       end 
      end 
     } 
     e= results.empty? ? 0 : results.count-1 
     mt= e>0 ? results[e-1] : 0 
     et=results[e] || 0 

     I18n.t("datetime.distance_in_words.x_#{names[e]}", count: et) + 
      (mt>0 ? " "+I18n.t("datetime.distance_in_words.x_#{names[e-1]}", count: mt):'') 
    end 
end 
0

Là một thay thế, bạn có thể làm điều này:

start = DateTime.now.strftime('%Q').to_i/1000.0 
#=> 1409163050.088 
sleep 3.5 
#=> 3 
now_ms = DateTime.now.strftime('%Q').to_i/1000.0 
#=> 1409163053.606 
'%.3f' % (now_ms - start) 
#=> "3.518" 
0

Tôi đã đưa ra một thực hiện thay thế cho điều này khi được sử dụng trong kịch bản nhật ký ở đây [sao chép từ đó]:

How to generate a human readable time range using ruby on rails

Nếu bạn muốn hiển thị khoảng thời gian đáng kể trong giây để phạm vi ngày, một sự thay thế sẽ là (vì nó không phải thực hiện tốt nhất):

def human_duration(secs, significant_only = true) 
    n = secs.round 
    parts = [60, 60, 24, 0].map{|d| next n if d.zero?; n, r = n.divmod d; r}. 
    reverse.zip(%w(d h m s)).drop_while{|n, u| n.zero? } 
    if significant_only 
    parts = parts[0..1] # no rounding, sorry 
    parts << '0' if parts.empty? 
    end 
    parts.flatten.join 
end 
start = Time.now 
# perform job 
puts "Elapsed time: #{human_duration(Time.now - start)}" 

human_duration(0.3) == '0' 
human_duration(0.5) == '1s' 
human_duration(60) == '1m0s' 
human_duration(4200) == '1h10m' 
human_duration(3600*24) == '1d0h' 
human_duration(3600*24 + 3*60*60) == '1d3h' 
human_duration(3600*24 + 3*60*60 + 59*60) == '1d3h' # simple code, doesn't round 
human_duration(3600*24 + 3*60*60 + 59*60, false) == '1d3h59m0s' 

Hoặc bạn có thể chỉ quan tâm đến việc tước phần giây khi không quan trọng (cũng chứng minh một cách tiếp cận khác):

def human_duration(duration_in_seconds) 
    n = duration_in_seconds.round 
    parts = [] 
    [60, 60, 24].each{|d| n, r = n.divmod d; parts << r; break if n.zero?} 
    parts << n unless n.zero? 
    pairs = parts.reverse.zip(%w(d h m s)[-parts.size..-1]) 
    pairs.pop if pairs.size > 2 # do not report seconds when irrelevant 
    pairs.flatten.join 
end 

Hy vọng điều đó sẽ hữu ích.

0

tôi đến với giải pháp này

def formate_duration(started, ended) 
    differences = ["secs", "mins", "hours", "days"].reduce([[nil, [(ended - started).round, nil]]]) do |acc, unit| 
    mod = unit == "hours" ? 24 : 60 
    # will return [ over, unit_value ] 
    # over is than used for calculation in next step 
    # accumulator is in format [["unit" [ over, value]]] and we are interesting in latest over in each step 
    # => This is why main diff is in this place in accumulator 
    entry = acc[acc.size-1][1][0].divmod(mod) 
    acc << [ unit, entry ] 
    end 

    # this will do string conversion and reverse in one step 
    str = differences.drop(1).reduce("") do |acc, current| 
    if (current[1][1] > 0) 
     "#{current[1][1]} #{current[0]} #{acc}" 
    else acc end 
    end 

    str.empty? ? "now" : str 
end 

Hãy nhận biết rằng điều này sẽ không làm việc cho sự khác biệt lớn hơn 60 ngày (Trong trường hợp đó bạn sẽ cần phải thêm điều kiện)

Các vấn đề liên quan