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 |