Defect #40914 » 40914-v2.patch
app/models/time_entry.rb | ||
---|---|---|
191 | 191 |
def hours |
192 | 192 |
h = read_attribute(:hours) |
193 | 193 |
if h.is_a?(Float) |
194 |
h.round(2) |
|
194 |
# Convert the float value to a rational with a denominator of 60 to |
|
195 |
# avoid floating point errors. |
|
196 |
# |
|
197 |
# Examples: |
|
198 |
# 0.38333333333333336 => (23/60) # 23m |
|
199 |
# 0.9913888888888889 => (59/60) # 59m 29s is rounded to 59m |
|
200 |
# 0.9919444444444444 => (1/1) # 59m 30s is rounded to 60m |
|
201 |
(h * 60).round / 60r |
|
195 | 202 |
else |
196 | 203 |
h |
197 | 204 |
end |
lib/redmine/i18n.rb | ||
---|---|---|
50 | 50 |
end |
51 | 51 | |
52 | 52 |
def l_hours(hours) |
53 |
hours = hours.to_f
|
|
53 |
hours = 0 unless hours.is_a?(Numeric)
|
|
54 | 54 |
l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => format_hours(hours)) |
55 | 55 |
end |
56 | 56 | |
57 | 57 |
def l_hours_short(hours) |
58 |
l(:label_f_hour_short, :value => format_hours(hours.to_f))
|
|
58 |
l(:label_f_hour_short, :value => format_hours(hours.is_a?(Numeric) ? hours : 0))
|
|
59 | 59 |
end |
60 | 60 | |
61 | 61 |
def ll(lang, str, arg=nil) |
test/unit/time_entry_test.rb | ||
---|---|---|
87 | 87 |
"3 hours" => 3.0, |
88 | 88 |
"12min" => 0.2, |
89 | 89 |
"12 Min" => 0.2, |
90 |
"0:23" => Rational(23, 60), # 0.38333333333333336 |
|
91 |
"0.9913888888888889" => Rational(59, 60), # 59m 29s is rounded to 59m |
|
92 |
"0.9919444444444444" => 1 # 59m 30s is rounded to 60m |
|
90 | 93 |
} |
91 | 94 |
assertions.each do |k, v| |
92 | 95 |
t = TimeEntry.new(:hours => k) |
93 |
assert_equal v, t.hours, "Converting #{k} failed:"
|
|
96 |
assert v == t.hours && t.hours.is_a?(Rational), "Converting #{k} failed:"
|
|
94 | 97 |
end |
95 | 98 |
end |
96 | 99 | |
100 |
def test_hours_sum_precision |
|
101 |
# The sum of 10, 10, and 40 minutes should be 1 hour, but in older |
|
102 |
# versions of Redmine, the result was 1.01 hours. This was because |
|
103 |
# TimeEntry#hours was a float value rounded to 2 decimal places. |
|
104 |
# [0.17, 0.17, 0.67].sum => 1.01 |
|
105 | ||
106 |
hours = %w[10m 10m 40m].map {|m| TimeEntry.new(hours: m).hours} |
|
107 |
assert_equal 1, hours.sum |
|
108 |
hours.map {|h| assert h.is_a?(Rational)} |
|
109 |
end |
|
110 | ||
97 | 111 |
def test_hours_should_default_to_nil |
98 | 112 |
assert_nil TimeEntry.new.hours |
99 | 113 |
end |