Project

General

Profile

Feature #20310 » 20310-v4.patch

Go MAEDA, 2025-08-31 11:19

View differences:

app/helpers/application_helper.rb
501 501
            h(page.pretty_title),
502 502
            href,
503 503
            :title => (if options[:timestamp] && page.updated_on
504
                         l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on))
504
                         l(label_by_timestamp_format(:label_updated_time), format_timestamp(page.updated_on))
505 505
                       else
506 506
                         nil
507 507
                       end)
......
745 745
  end
746 746

  
747 747
  def authoring(created, author, options={})
748
    l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
748
    l(label_by_timestamp_format(options[:label] || :label_added_time_by), :author => link_to_user(author), :age => time_tag(created)).html_safe
749 749
  end
750 750

  
751 751
  def time_tag(time)
752 752
    return if time.nil?
753 753

  
754
    text = distance_of_time_in_words(Time.now, time)
754
    text = format_timestamp(time)
755 755
    if @project
756 756
      link_to(text,
757 757
              project_activity_path(@project, :from => User.current.time_to_date(time)),
app/helpers/settings_helper.rb
238 238
    end
239 239
  end
240 240

  
241
  # Returns the options for the timestamp_format setting
242
  #   Convert the date and time three days ago into each format and use it as an example
243
  def timestamp_format_setting_options
244
    %w[relative_time relative_time_with_absolute_time absolute_time].map do |f|
245
      ["#{format_timestamp(Time.now.ago(3.days), f)} (#{l('label_' + f)})", f]
246
    end
247
  end
248

  
241 249
  def gravatar_default_setting_options
242 250
    [['Identicons', 'identicon'],
243 251
     ['Monster ids', 'monsterid'],
app/helpers/wiki_helper.rb
73 73
  end
74 74

  
75 75
  def wiki_content_update_info(content)
76
    l(:label_updated_time_by, :author => link_to_user(content.author), :age => time_tag(content.updated_on)).html_safe
76
    authoring(content.updated_on, content.author, label: :label_updated_time_by)
77 77
  end
78 78
end
app/views/issues/show.html.erb
49 49
        <p class="author">
50 50
        <%= authoring @issue.created_on, @issue.author %>.
51 51
        <% if @issue.created_on != @issue.updated_on %>
52
        <%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>.
52
        <%= l(label_by_timestamp_format(:label_updated_time), time_tag(@issue.updated_on)).html_safe %>.
53 53
        <% end %>
54 54
        </p>
55 55

  
app/views/settings/_display.html.erb
17 17

  
18 18
<p><%= setting_select :timespan_format, [["%.2f" % 0.75, 'decimal'], ['0:45 h', 'minutes']], :blank => false %></p>
19 19

  
20
<p><%= setting_select :timestamp_format, timestamp_format_setting_options %></p>
21

  
20 22
<p><%= setting_select :user_format, @options[:user_format] %></p>
21 23

  
22 24
<p><%= setting_check_box :gravatar_enabled, :data => {:enables => '#settings_gravatar_default'} %>
config/locales/en.yml
453 453
  setting_date_format: Date format
454 454
  setting_time_format: Time format
455 455
  setting_timespan_format: Time span format
456
  setting_timestamp_format: Timestamp format
456 457
  setting_cross_project_issue_relations: Allow cross-project issue relations
457 458
  setting_cross_project_subtasks: Allow cross-project subtasks
458 459
  setting_issue_list_default_columns: Issues list defaults
......
893 894
  label_f_hour: "%{value} hour"
894 895
  label_f_hour_plural: "%{value} hours"
895 896
  label_f_hour_short: "%{value} h"
897
  label_relative_time: Relative time
898
  label_relative_time_with_absolute_time: Relative time with absolute time
899
  label_absolute_time: Absolute time
896 900
  label_time_tracking: Time tracking
897 901
  label_change_plural: Changes
898 902
  label_statistics: Statistics
......
954 958
  label_added_time_by: "Added by %{author} %{age} ago"
955 959
  label_updated_time_by: "Updated by %{author} %{age} ago"
956 960
  label_updated_time: "Updated %{value} ago"
961
  label_added_absolute_time_by: "Added by %{author} on %{age}"
962
  label_updated_absolute_time_by: "Updated by %{author} on %{age}"
963
  label_updated_absolute_time: "Updated on %{value}"
957 964
  label_jump_to_a_project: Jump to a project...
958 965
  label_file_plural: Files
959 966
  label_changeset_plural: Changesets
config/locales/ja.yml
745 745
  label_added_time_by: "%{author} さんが%{age}前に追加"
746 746
  label_updated_time_by: "%{author} さんが%{age}前に更新"
747 747
  label_updated_time: "%{value}前に更新"
748
  label_added_absolute_time_by: "%{author} さんが%{age}に追加"
749
  label_updated_absolute_time_by: "%{author} さんが%{age}に更新"
750
  label_updated_absolute_time: "%{value}に更新"
748 751
  label_jump_to_a_project: プロジェクトへ移動...
749 752
  label_file_plural: ファイル
750 753
  label_changeset_plural: 更新履歴
config/settings.yml
174 174
  default: ''
175 175
timespan_format:
176 176
  default: 'minutes'
177
timestamp_format:
178
  default: 'relative_time'
177 179
user_format:
178 180
  default: :firstname_lastname
179 181
  format: symbol
lib/redmine/i18n.rb
89 89
      (include_date ? "#{format_date(local)} " : "") + ::I18n.l(local, **options)
90 90
    end
91 91

  
92
    def format_timestamp(time, timestamp_format=nil)
93
      case (timestamp_format || Setting.timestamp_format)
94
      when 'relative_time'
95
        distance_of_time_in_words(Time.now, time)
96
      when 'relative_time_with_absolute_time'
97
        "#{distance_of_time_in_words(Time.now, time)} (#{format_time(time)})"
98
      when 'absolute_time'
99
        format_time(time)
100
      end
101
    end
102

  
103
    def label_by_timestamp_format(label_name)
104
      return label_name unless Setting.timestamp_format == 'absolute_time'
105

  
106
      label_name.to_s.gsub('_time', '_absolute_time').to_sym
107
    end
108

  
92 109
    def format_hours(hours)
93 110
      return "" if hours.blank?
94 111

  
test/helpers/application_helper_test.rb
1838 1838
    result = render_page_hierarchy(pages_by_parent_id, nil, :timestamp => true)
1839 1839
    assert_select_in(
1840 1840
      result, 'ul.pages-hierarchy li a[title=?]',
1841
      l(:label_updated_time,
1842
        distance_of_time_in_words(Time.now, parent_page.updated_on)))
1841
      l(:label_updated_time, format_timestamp(parent_page.updated_on)))
1843 1842
    assert_select_in(
1844 1843
      result, 'ul.pages-hierarchy li ul.pages-hierarchy a[title=?]',
1845
      l(:label_updated_time,
1846
        distance_of_time_in_words(Time.now, child_page.updated_on)))
1844
      l(:label_updated_time, format_timestamp(child_page.updated_on)))
1847 1845
  end
1848 1846

  
1849 1847
  def test_render_page_hierarchy_when_action_is_export
test/helpers/settings_helper_test.rb
29 29
    options = date_format_setting_options('en')
30 30
    assert_include ["2015-07-14 (yyyy-mm-dd)", "%Y-%m-%d"], options
31 31
  end
32

  
33
  def test_timestamp_format_setting_options_should_include_human_readable_format
34
    sample_time = Time.now.ago(3.days)
35

  
36
    options = timestamp_format_setting_options
37
    assert_include ["3 days (Relative time)", 'relative_time'], options
38
    assert_include ["3 days (#{format_time(sample_time)}) (Relative time with absolute time)", 'relative_time_with_absolute_time'], options
39
    assert_include ["#{format_time(sample_time)} (Absolute time)", 'absolute_time'], options
40
  end
32 41
end
test/unit/lib/redmine/i18n_test.rb
22 22
class Redmine::I18nTest < ActiveSupport::TestCase
23 23
  include Redmine::I18n
24 24
  include ActionView::Helpers::NumberHelper
25
  include ActionView::Helpers::DateHelper
25 26

  
26 27
  def setup
27 28
    User.current = nil
......
125 126
    end
126 127
  end
127 128

  
129
  def test_format_timestamp
130
    User.current = User.find(1)
131
    User.current.pref.time_zone = 'UTC'
132
    User.current.pref.save!
133

  
134
    travel_to Time.utc(2023, 5, 31, 23, 59, 59) # Time.now => 2023/5/31 23:59:59
135
    sample_time = Time.utc(2022, 1, 1, 0, 0, 0)
136

  
137
    with_settings :timestamp_format => 'relative_time' do
138
      assert_equal 'over 1 year', format_timestamp(sample_time)
139
    end
140
    with_settings :timestamp_format => 'relative_time_with_absolute_time', :date_format => '%d/%m/%Y', :time_format => '%H:%M' do
141
      assert_equal 'over 1 year (01/01/2022 00:00)', format_timestamp(sample_time)
142
    end
143
    with_settings :timestamp_format => 'absolute_time', :date_format => '%d/%m/%Y', :time_format => '%H:%M' do
144
      assert_equal '01/01/2022 00:00', format_timestamp(sample_time)
145
    end
146

  
147
    # If there is an argument, it takes precedence over the set value
148
    with_settings :timestamp_format => 'absolute_time', :date_format => '%d/%m/%Y', :time_format => '%H:%M' do
149
      assert_equal 'over 1 year', format_timestamp(sample_time, 'relative_time')
150
    end
151
  end
152

  
153
  def test_label_by_timestamp_format
154
    with_settings :timestamp_format => 'relative_time' do
155
      assert_equal :label_added_time_by, label_by_timestamp_format(:label_added_time_by)
156
      assert_equal :label_updated_time_by, label_by_timestamp_format(:label_updated_time_by)
157
      assert_equal :label_updated_time, label_by_timestamp_format(:label_updated_time)
158
    end
159
    with_settings :timestamp_format => 'relative_time_with_absolute_time' do
160
      assert_equal :label_added_time_by, label_by_timestamp_format(:label_added_time_by)
161
      assert_equal :label_updated_time_by, label_by_timestamp_format(:label_updated_time_by)
162
      assert_equal :label_updated_time, label_by_timestamp_format(:label_updated_time)
163
    end
164
    with_settings :timestamp_format => 'absolute_time' do
165
      assert_equal :label_added_absolute_time_by, label_by_timestamp_format(:label_added_time_by)
166
      assert_equal :label_updated_absolute_time_by, label_by_timestamp_format(:label_updated_time_by)
167
      assert_equal :label_updated_absolute_time, label_by_timestamp_format(:label_updated_time)
168
    end
169
  end
170

  
128 171
  def test_number_to_human_size_for_each_language
129 172
    valid_languages.each do |lang|
130 173
      set_language_if_valid lang
(9-9/11)