1
0
mirror of https://github.com/meineerde/redmine.git synced 2025-12-19 15:01:14 +00:00

Replace Date.today with User.current.today (#22320).

Depending on the offset between a user's configured timezone and the server
timezone, Date.today may be more or less often wrong from the user's
perspective, leading to things like issues marked as overdue too early or too
late, or yesterday / tomorrow being displayed / selected where 'today' is
intended.

A test case illustrating the problem with Issue#overdue? is included

Patch by Jens Kraemer.

git-svn-id: http://svn.redmine.org/redmine/trunk@15379 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2016-05-07 10:42:22 +00:00
parent 40c9c3e922
commit ed50d42210
23 changed files with 70 additions and 42 deletions

View File

@ -27,7 +27,7 @@ class ActivitiesController < ApplicationController
begin; @date_to = params[:from].to_date + 1; rescue; end begin; @date_to = params[:from].to_date + 1; rescue; end
end end
@date_to ||= Date.today + 1 @date_to ||= User.current.today + 1
@date_from = @date_to - @days @date_from = @date_to - @days
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
if params[:user_id].present? if params[:user_id].present?

View File

@ -35,8 +35,8 @@ class CalendarsController < ApplicationController
@month = params[:month].to_i @month = params[:month].to_i
end end
end end
@year ||= Date.today.year @year ||= User.current.today.year
@month ||= Date.today.month @month ||= User.current.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month) @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query retrieve_query

View File

@ -441,7 +441,7 @@ class IssuesController < ApplicationController
@issue.project ||= @issue.allowed_target_projects.first @issue.project ||= @issue.allowed_target_projects.first
end end
@issue.author ||= User.current @issue.author ||= User.current
@issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date? @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
attrs = (params[:issue] || {}).deep_dup attrs = (params[:issue] || {}).deep_dup
if action_name == 'new' && params[:was_default_status] == attrs[:status_id] if action_name == 'new' && params[:was_default_status] == attrs[:status_id]

View File

@ -352,7 +352,7 @@ class RepositoriesController < ApplicationController
end end
def graph_commits_per_month(repository) def graph_commits_per_month(repository)
@date_to = Date.today @date_to = User.current.today
@date_from = @date_to << 11 @date_from = @date_to << 11
@date_from = Date.civil(@date_from.year, @date_from.month, 1) @date_from = Date.civil(@date_from.year, @date_from.month, 1)
commits_by_day = Changeset. commits_by_day = Changeset.
@ -371,7 +371,8 @@ class RepositoriesController < ApplicationController
changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last } changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
fields = [] fields = []
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)} today = User.current.today
12.times {|m| fields << month_name(((today.month - 1 - m) % 12) + 1)}
graph = SVG::Graph::Bar.new( graph = SVG::Graph::Bar.new(
:height => 300, :height => 300,

View File

@ -252,7 +252,7 @@ module ApplicationHelper
def due_date_distance_in_words(date) def due_date_distance_in_words(date)
if date if date
l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date)) l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date))
end end
end end

View File

@ -65,7 +65,7 @@ module MyHelper
def timelog_items def timelog_items
TimeEntry. TimeEntry.
where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, Date.today - 6, Date.today). where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, User.current.today - 6, User.current.today).
joins(:activity, :project). joins(:activity, :project).
references(:issue => [:tracker, :status]). references(:issue => [:tracker, :status]).
includes(:issue => [:tracker, :status]). includes(:issue => [:tracker, :status]).

View File

@ -182,7 +182,7 @@ module SettingsHelper
# Returns the options for the date_format setting # Returns the options for the date_format setting
def date_format_setting_options(locale) def date_format_setting_options(locale)
Setting::DATE_FORMATS.map do |f| Setting::DATE_FORMATS.map do |f|
today = ::I18n.l(Date.today, :locale => locale, :format => f) today = ::I18n.l(User.current.today, :locale => locale, :format => f)
format = f.gsub('%', '').gsub(/[dmY]/) do format = f.gsub('%', '').gsub(/[dmY]/) do
{'d' => 'dd', 'm' => 'mm', 'Y' => 'yyyy'}[$&] {'d' => 'dd', 'm' => 'mm', 'Y' => 'yyyy'}[$&]
end end

View File

@ -807,14 +807,14 @@ class Issue < ActiveRecord::Base
# Returns true if the issue is overdue # Returns true if the issue is overdue
def overdue? def overdue?
due_date.present? && (due_date < Date.today) && !closed? due_date.present? && (due_date < User.current.today) && !closed?
end end
# Is the amount of work done less than it should for the due date # Is the amount of work done less than it should for the due date
def behind_schedule? def behind_schedule?
return false if start_date.nil? || due_date.nil? return false if start_date.nil? || due_date.nil?
done_date = start_date + ((due_date - start_date + 1) * done_ratio / 100).floor done_date = start_date + ((due_date - start_date + 1) * done_ratio / 100).floor
return done_date <= Date.today return done_date <= User.current.today
end end
# Does this issue have children? # Does this issue have children?

View File

@ -206,7 +206,7 @@ class MailHandler < ActionMailer::Base
issue.subject = '(no subject)' issue.subject = '(no subject)'
end end
issue.description = cleaned_up_text_body issue.description = cleaned_up_text_body
issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date? issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
issue.is_private = (handler_options[:issue][:is_private] == '1') issue.is_private = (handler_options[:issue][:is_private] == '1')
# add To and Cc as watchers before saving so the watchers can reply to Redmine # add To and Cc as watchers before saving so the watchers can reply to Redmine

View File

@ -594,7 +594,7 @@ class Project < ActiveRecord::Base
end end
def overdue? def overdue?
active? && !due_date.nil? && (due_date < Date.today) active? && !due_date.nil? && (due_date < User.current.today)
end end
# Returns the percent completed for this project, based on the # Returns the percent completed for this project, based on the

View File

@ -874,32 +874,32 @@ class Query < ActiveRecord::Base
when "w" when "w"
# = this week # = this week
first_day_of_week = l(:general_first_day_of_week).to_i first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6, is_custom_filter) sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6, is_custom_filter)
when "lw" when "lw"
# = last week # = last week
first_day_of_week = l(:general_first_day_of_week).to_i first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1, is_custom_filter) sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1, is_custom_filter)
when "l2w" when "l2w"
# = last 2 weeks # = last 2 weeks
first_day_of_week = l(:general_first_day_of_week).to_i first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1, is_custom_filter) sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1, is_custom_filter)
when "m" when "m"
# = this month # = this month
date = Date.today date = User.current.today
sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter) sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
when "lm" when "lm"
# = last month # = last month
date = Date.today.prev_month date = User.current.today.prev_month
sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter) sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
when "y" when "y"
# = this year # = this year
date = Date.today date = User.current.today
sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year, is_custom_filter) sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year, is_custom_filter)
when "~" when "~"
sql = sql_contains("#{db_table}.#{db_field}", value.first) sql = sql_contains("#{db_table}.#{db_field}", value.first)
@ -999,7 +999,7 @@ class Query < ActiveRecord::Base
# Returns a SQL clause for a date or datetime field using relative dates. # Returns a SQL clause for a date or datetime field using relative dates.
def relative_date_clause(table, field, days_from, days_to, is_custom_filter) def relative_date_clause(table, field, days_from, days_to, is_custom_filter)
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil), is_custom_filter) date_clause(table, field, (days_from ? User.current.today + days_from : nil), (days_to ? User.current.today + days_to : nil), is_custom_filter)
end end
# Returns a Date or Time from the given filter value # Returns a Date or Time from the given filter value

View File

@ -512,6 +512,7 @@ class User < Principal
if time_zone.nil? if time_zone.nil?
Date.today Date.today
else else
# TODO replace with time_zone.today
Time.now.in_time_zone(time_zone).to_date Time.now.in_time_zone(time_zone).to_date
end end
end end

View File

@ -104,7 +104,7 @@ class Version < ActiveRecord::Base
# Returns true if the version is completed: closed or due date reached and no open issues # Returns true if the version is completed: closed or due date reached and no open issues
def completed? def completed?
closed? || (effective_date && (effective_date < Date.today) && (open_issues_count == 0)) closed? || (effective_date && (effective_date < User.current.today) && (open_issues_count == 0))
end end
def behind_schedule? def behind_schedule?
@ -112,7 +112,7 @@ class Version < ActiveRecord::Base
return false return false
elsif due_date && start_date elsif due_date && start_date
done_date = start_date + ((due_date - start_date+1)* completed_percent/100).floor done_date = start_date + ((due_date - start_date+1)* completed_percent/100).floor
return done_date <= Date.today return done_date <= User.current.today
else else
false # No issues so it's not late false # No issues so it's not late
end end
@ -141,7 +141,7 @@ class Version < ActiveRecord::Base
# Returns true if the version is overdue: due date reached and some open issues # Returns true if the version is overdue: due date reached and some open issues
def overdue? def overdue?
effective_date && (effective_date < Date.today) && (open_issues_count > 0) effective_date && (effective_date < User.current.today) && (open_issues_count > 0)
end end
# Returns assigned issues count # Returns assigned issues count

View File

@ -31,7 +31,7 @@
<%= link_to_content_update(l(:label_next) + " \xc2\xbb", <%= link_to_content_update(l(:label_next) + " \xc2\xbb",
params.merge(:from => @date_to + @days - 1), params.merge(:from => @date_to + @days - 1),
:title => l(:label_date_from_to, :start => format_date(@date_to), :end => format_date(@date_to + @days - 1)), :title => l(:label_date_from_to, :start => format_date(@date_to), :end => format_date(@date_to + @days - 1)),
:accesskey => accesskey(:next)) unless @date_to >= Date.today %> :accesskey => accesskey(:next)) unless @date_to >= User.current.today %>
</div> </div>
&nbsp; &nbsp;
<% other_formats_links do |f| %> <% other_formats_links do |f| %>

View File

@ -7,7 +7,7 @@
<% day = calendar.startdt <% day = calendar.startdt
while day <= calendar.enddt %> while day <= calendar.enddt %>
<%= ("<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>".html_safe) if day.cwday == calendar.first_wday %> <%= ("<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>".html_safe) if day.cwday == calendar.first_wday %>
<td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>"> <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if User.current.today == day %>">
<p class="day-num"><%= day.day %></p> <p class="day-num"><%= day.day %></p>
<% calendar.events_on(day).each do |i| %> <% calendar.events_on(day).each do |i| %>
<% if i.is_a? Issue %> <% if i.is_a? Issue %>

View File

@ -302,9 +302,9 @@
<%= @gantt.lines.html_safe %> <%= @gantt.lines.html_safe %>
<% ###### Today red line (excluded from cache) ###### %> <% ###### Today red line (excluded from cache) ###### %>
<% if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %> <% if User.current.today >= @gantt.date_from and User.current.today <= @gantt.date_to %>
<% <%
today_left = (((Date.today - @gantt.date_from + 1) * zoom).floor() - 1).to_i today_left = (((User.current.today - @gantt.date_from + 1) * zoom).floor() - 1).to_i
style = "" style = ""
style += "position: absolute;" style += "position: absolute;"
style += "height: #{g_height}px;" style += "height: #{g_height}px;"

View File

@ -1,6 +1,6 @@
<h3><%= l(:label_calendar) %></h3> <h3><%= l(:label_calendar) %></h3>
<% calendar = Redmine::Helpers::Calendar.new(Date.today, current_language, :week) <% calendar = Redmine::Helpers::Calendar.new(User.current.today, current_language, :week)
calendar.events = calendar_items(calendar.startdt, calendar.enddt) %> calendar.events = calendar_items(calendar.startdt, calendar.enddt) %>
<%= render :partial => 'common/calendar', :locals => {:calendar => calendar } %> <%= render :partial => 'common/calendar', :locals => {:calendar => calendar } %>

View File

@ -29,7 +29,7 @@ entries_by_day = entries.group_by(&:spent_on)
<tbody> <tbody>
<% entries_by_day.keys.sort.reverse.each do |day| %> <% entries_by_day.keys.sort.reverse.each do |day| %>
<tr class="odd"> <tr class="odd">
<td><strong><%= day == Date.today ? l(:label_today).titleize : format_date(day) %></strong></td> <td><strong><%= day == User.current.today ? l(:label_today).titleize : format_date(day) %></strong></td>
<td colspan="2"></td> <td colspan="2"></td>
<td class="hours"><em><%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %></em></td> <td class="hours"><em><%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %></em></td>
<td></td> <td></td>

View File

@ -26,7 +26,7 @@ module Redmine
pdf = ITCPDF.new(current_language) pdf = ITCPDF.new(current_language)
pdf.set_title("#{issue.project} - #{issue.tracker} ##{issue.id}") pdf.set_title("#{issue.project} - #{issue.tracker} ##{issue.id}")
pdf.alias_nb_pages pdf.alias_nb_pages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(User.current.today)
pdf.add_page pdf.add_page
pdf.SetFontStyle('B',11) pdf.SetFontStyle('B',11)
buf = "#{issue.project} - #{issue.tracker} ##{issue.id}" buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
@ -246,7 +246,7 @@ module Redmine
title = "#{project} - #{title}" if project title = "#{project} - #{title}" if project
pdf.set_title(title) pdf.set_title(title)
pdf.alias_nb_pages pdf.alias_nb_pages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(User.current.today)
pdf.set_auto_page_break(false) pdf.set_auto_page_break(false)
pdf.add_page("L") pdf.add_page("L")

View File

@ -26,7 +26,7 @@ module Redmine
pdf = Redmine::Export::PDF::ITCPDF.new(current_language) pdf = Redmine::Export::PDF::ITCPDF.new(current_language)
pdf.set_title(project.name) pdf.set_title(project.name)
pdf.alias_nb_pages pdf.alias_nb_pages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(User.current.today)
pdf.add_page pdf.add_page
pdf.SetFontStyle('B',11) pdf.SetFontStyle('B',11)
pdf.RDMMultiCell(190,5, project.name) pdf.RDMMultiCell(190,5, project.name)
@ -43,7 +43,7 @@ module Redmine
pdf = ITCPDF.new(current_language) pdf = ITCPDF.new(current_language)
pdf.set_title("#{project} - #{page.title}") pdf.set_title("#{project} - #{page.title}")
pdf.alias_nb_pages pdf.alias_nb_pages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(User.current.today)
pdf.add_page pdf.add_page
pdf.SetFontStyle('B',11) pdf.SetFontStyle('B',11)
pdf.RDMMultiCell(190,5, pdf.RDMMultiCell(190,5,

View File

@ -59,8 +59,8 @@ module Redmine
@month_from = 1 @month_from = 1
end end
else else
@month_from ||= Date.today.month @month_from ||= User.current.today.month
@year_from ||= Date.today.year @year_from ||= User.current.today.year
end end
zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
@ -428,9 +428,9 @@ module Redmine
lines(:image => gc, :top => top, :zoom => zoom, lines(:image => gc, :top => top, :zoom => zoom,
:subject_width => subject_width, :format => :image) :subject_width => subject_width, :format => :image)
# today red line # today red line
if Date.today >= @date_from and Date.today <= date_to if User.current.today >= @date_from and User.current.today <= date_to
gc.stroke('red') gc.stroke('red')
x = (Date.today - @date_from + 1) * zoom + subject_width x = (User.current.today - @date_from + 1) * zoom + subject_width
gc.line(x, headers_height, x, headers_height + g_height - 1) gc.line(x, headers_height, x, headers_height + g_height - 1)
end end
gc.draw(imgl) gc.draw(imgl)
@ -442,7 +442,7 @@ module Redmine
pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language) pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language)
pdf.SetTitle("#{l(:label_gantt)} #{project}") pdf.SetTitle("#{l(:label_gantt)} #{project}")
pdf.alias_nb_pages pdf.alias_nb_pages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(User.current.today)
pdf.AddPage("L") pdf.AddPage("L")
pdf.SetFontStyle('B', 12) pdf.SetFontStyle('B', 12)
pdf.SetX(15) pdf.SetX(15)
@ -592,8 +592,8 @@ module Redmine
coords[:bar_progress_end] = self.date_to - self.date_from + 1 coords[:bar_progress_end] = self.date_to - self.date_from + 1
end end
end end
if progress_date < Date.today if progress_date < User.current.today
late_date = [Date.today, end_date].min late_date = [User.current.today, end_date].min
if late_date > self.date_from && late_date > start_date if late_date > self.date_from && late_date > start_date
if late_date < self.date_to if late_date < self.date_to
coords[:bar_late_end] = late_date - self.date_from + 1 coords[:bar_late_end] = late_date - self.date_from + 1

View File

@ -70,10 +70,10 @@ module Redmine
end end
min = @hours.collect {|row| row['spent_on']}.min min = @hours.collect {|row| row['spent_on']}.min
@from = min ? min.to_date : Date.today @from = min ? min.to_date : User.current.today
max = @hours.collect {|row| row['spent_on']}.max max = @hours.collect {|row| row['spent_on']}.max
@to = max ? max.to_date : Date.today @to = max ? max.to_date : User.current.today
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f} @total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}

View File

@ -2756,4 +2756,30 @@ class IssueTest < ActiveSupport::TestCase
issue.reload.assigned_to = nil issue.reload.assigned_to = nil
assert_equal group, issue.assigned_to_was assert_equal group, issue.assigned_to_was
end end
def test_issue_overdue_should_respect_user_timezone
user_in_europe = users(:users_001)
user_in_europe.pref.update_attribute :time_zone, 'UTC'
user_in_asia = users(:users_002)
user_in_asia.pref.update_attribute :time_zone, 'Hongkong'
issue = Issue.generate! :due_date => Date.parse('2016-03-20')
# server time is UTC
time = Time.parse '2016-03-20 20:00 UTC'
Time.stubs(:now).returns(time)
Date.stubs(:today).returns(time.to_date)
# for a user in the same time zone as the server the issue is not overdue
# yet
User.current = user_in_europe
assert !issue.overdue?
# at the same time, a user in East Asia looks at the issue - it's already
# March 21st and the issue should be marked overdue
User.current = user_in_asia
assert issue.overdue?
end
end end