Skip to content

Commit

Permalink
Fix datetime delocalization issues by using ISO8601 time format
Browse files Browse the repository at this point in the history
Localization is now performed only in the browser, without affecting browser-server communication.
Closes #3344
  • Loading branch information
mshibuya committed Sep 4, 2021
1 parent 4379451 commit 01e8d5f
Show file tree
Hide file tree
Showing 28 changed files with 599 additions and 291 deletions.
1 change: 0 additions & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ Style/RescueStandardError:
Exclude:
- 'lib/rails_admin/adapters/mongoid.rb'
- 'lib/rails_admin/adapters/mongoid/bson.rb'
- 'lib/rails_admin/support/i18n.rb'

# Offense count: 2
# Cop supports --auto-correct.
Expand Down
83 changes: 40 additions & 43 deletions app/assets/javascripts/rails_admin/ra.filter-box.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,57 +34,46 @@
}
break;
case 'date':
additional_control =
$('<input size="20" class="date additional-fieldset default input-sm form-control" type="text" />')
.css('display', (!field_operator || field_operator == "default") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[0] || '')
.add(
$('<input size="20" placeholder="-∞" class="date additional-fieldset between input-sm form-control" type="text" />')
.css('display', (field_operator == "between") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[1] || '')
)
.add(
$('<input size="20" placeholder="∞" class="date additional-fieldset between input-sm form-control" type="text" />')
.css('display', (field_operator == "between") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[2] || '')
);
case 'datetime':
case 'timestamp':
case 'time':
control = control || $('<select class="switch-additional-fieldsets input-sm form-control"></select>')
.prop('name', operator_name)
.append($('<option data-additional-fieldset="default" value="default"></option>').prop('selected', field_operator == "default").text(RailsAdmin.I18n.t("date")))
.append($('<option data-additional-fieldset="default" value="default"></option>').prop('selected', field_operator == "default").text(RailsAdmin.I18n.t(field_type == "time" ? "time" : "date")))
.append($('<option data-additional-fieldset="between" value="between"></option>').prop('selected', field_operator == "between").text(RailsAdmin.I18n.t("between_and_")))
.append($('<option value="today"></option>').prop('selected', field_operator == "today").text(RailsAdmin.I18n.t("today")))
.append($('<option value="yesterday"></option>').prop('selected', field_operator == "yesterday").text(RailsAdmin.I18n.t("yesterday")))
.append($('<option value="this_week"></option>').prop('selected', field_operator == "this_week").text(RailsAdmin.I18n.t("this_week")))
.append($('<option value="last_week"></option>').prop('selected', field_operator == "last_week").text(RailsAdmin.I18n.t("last_week")))
if (field_type != 'time') {
control.append([
$('<option value="today"></option>').prop('selected', field_operator == "today").text(RailsAdmin.I18n.t("today")),
$('<option value="yesterday"></option>').prop('selected', field_operator == "yesterday").text(RailsAdmin.I18n.t("yesterday")),
$('<option value="this_week"></option>').prop('selected', field_operator == "this_week").text(RailsAdmin.I18n.t("this_week")),
$('<option value="last_week"></option>').prop('selected', field_operator == "last_week").text(RailsAdmin.I18n.t("last_week")),
])
}
if (!required) {
control.append([
'<option disabled="disabled">---------</option>',
$('<option value="_not_null"></option>').prop('selected', field_operator == "_not_null").text(RailsAdmin.I18n.t("is_present")),
$('<option value="_null"></option>').prop('selected', field_operator == "_null").text(RailsAdmin.I18n.t("is_blank"))
])
}
additional_control = additional_control ||
$('<input size="25" class="datetime additional-fieldset default input-sm form-control" type="text" />')
.css('display', (!field_operator || field_operator == "default") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[0] || '')
.add(
$('<input size="25" placeholder="-∞" class="datetime additional-fieldset between input-sm form-control" type="text" />')
.css('display', (field_operator == "between") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[1] || '')
)
.add(
$('<input size="25" placeholder="∞" class="datetime additional-fieldset between input-sm form-control" type="text" />')
.css('display', (field_operator == "between") ? 'inline-block' : 'none')
.prop('name', value_name + '[]')
.prop('value', field_value[2] || '')
);
additional_control =
$.map([undefined, '-∞', '∞'], function(placeholder, index){
var visible = index == 0 ? (!field_operator || field_operator == "default") : (field_operator == "between");
return $('<span class="additional-fieldset"></span>')
.addClass(index == 0 ? 'default' : 'between')
.css('display', visible ? 'inline-block' : 'none')
.html(
$('<input type="hidden" />')
.prop('name', value_name + '[]')
.prop('value', field_value[index] || '')
.add(
$('<input class="input-sm form-control" type="text" />')
.addClass(field_type == 'date' ? 'date' : 'datetime')
.prop('size', field_type == 'date' || field_type == 'time' ? 20 : 25)
.prop('placeholder', placeholder)
)
);
});
break;
case 'enum':
var multiple_values = ((field_value instanceof Array) ? true : false)
Expand Down Expand Up @@ -193,10 +182,18 @@

$('#filters_box').append($content);

$content.find('.date, .datetime').datetimepicker({
locale: RailsAdmin.I18n.locale,
showTodayButton: true,
format: options['datetimepicker_format']
$content.find('.date, .datetime').each(function() {
$(this).datetimepicker({
date: moment($(this).siblings('[type=hidden]').val()),
locale: RailsAdmin.I18n.locale,
showTodayButton: true,
format: options['datetimepicker_format']
});
$(this).on('dp.change', function(e) {
if (e.date) {
$(this).siblings('[type=hidden]').val(e.date.format('YYYY-MM-DD[T]HH:mm:ss'));
}
});
});

$("hr.filters_box:hidden").show('slow');
Expand Down
6 changes: 6 additions & 0 deletions app/assets/javascripts/rails_admin/ra.widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,15 @@
var options;
options = $(this).data('options');
$.extend(options, {
date: moment($(this).siblings('[type=hidden]').val()),
locale: RailsAdmin.I18n.locale
});
$(this).datetimepicker(options);
$(this).on('dp.change', function(e) {
if (e.date) {
$(this).siblings('[type=hidden]').val(e.date.format('YYYY-MM-DD[T]HH:mm:ss'));
}
});
});
content.find('[data-enumeration]').each(function() {
if ($(this).is('[multiple]')) {
Expand Down
4 changes: 0 additions & 4 deletions app/helpers/rails_admin/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
require 'rails_admin/support/i18n'

module RailsAdmin
module ApplicationHelper
include RailsAdmin::Support::I18n

def capitalize_first_letter(wording)
return nil unless wording.present? && wording.is_a?(String)

Expand Down
2 changes: 1 addition & 1 deletion app/helpers/rails_admin/main_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def ordered_filter_options
when :enum
options[:select_options] = options_for_select(field.with(object: @abstract_model.model.new).enum, filter_hash['v'])
when :date, :datetime, :time
options[:datetimepicker_format] = field.parser.to_momentjs
options[:datetimepicker_format] = field.momentjs_format
end
options[:label] = field.label
options[:name] = field.name
Expand Down
3 changes: 2 additions & 1 deletion app/views/rails_admin/main/_form_datetime.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.form-inline
.input-group
= form.send field.view_helper, field.method_name, field.html_attributes.reverse_merge({value: field.form_value, class: 'form-control', data: {datetimepicker: true, options: field.datepicker_options.to_json}})
= form.hidden_field(field.method_name, id: nil, value: field.form_value)
= form.text_field field.method_name, field.html_attributes.reverse_merge({class: 'form-control', data: {datetimepicker: true, options: field.datepicker_options.to_json}, name: nil, value: nil})
= form.label(field.method_name, class: 'input-group-addon') do
%i.fa.fa-fw.fa-calendar
2 changes: 1 addition & 1 deletion app/views/rails_admin/main/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
- else
- ''
%li
%a{href: '#', :"data-field-label" => field.label, :"data-field-name" => field.name, :"data-field-operator" => field.default_filter_operator, :"data-field-options" => field_options.html_safe, :"data-field-required" => field.required.to_s, :"data-field-type" => field.type, :"data-field-value" => "", :"data-field-datetimepicker-format" => (field.try(:parser) && field.parser.to_momentjs)}= capitalize_first_letter(field.label)
%a{href: '#', :"data-field-label" => field.label, :"data-field-name" => field.name, :"data-field-operator" => field.default_filter_operator, :"data-field-options" => field_options.html_safe, :"data-field-required" => field.required.to_s, :"data-field-type" => field.type, :"data-field-value" => "", :"data-field-datetimepicker-format" => field.try(:momentjs_format)}= capitalize_first_letter(field.label)
%style
- properties.select{ |p| p.column_width.present? }.each do |property|
Expand Down
1 change: 1 addition & 0 deletions config/locales/rails_admin.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ en:
yesterday: Yesterday
this_week: This week
last_week: Last week
time: Time ...
number: Number ...
contains: Contains
is_exactly: Is exactly
Expand Down
6 changes: 3 additions & 3 deletions lib/rails_admin/abstract_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def build_statement_for_type_generic
case @type
when :date
build_statement_for_date
when :datetime, :timestamp
when :datetime, :timestamp, :time
build_statement_for_datetime_or_timestamp
end
end
Expand Down Expand Up @@ -178,8 +178,8 @@ def build_statement_for_date

def build_statement_for_datetime_or_timestamp
start_date, end_date = get_filtering_duration
start_date = start_date.try(:beginning_of_day) if start_date
end_date = end_date.try(:end_of_day) if end_date
start_date = start_date.beginning_of_day if start_date.is_a?(Date)
end_date = end_date.end_of_day if end_date.is_a?(Date)
range_filter(start_date, end_date)
end

Expand Down
4 changes: 3 additions & 1 deletion lib/rails_admin/adapters/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ def boolean_unary_operators
alias_method :numeric_unary_operators, :boolean_unary_operators

def range_filter(min, max)
if min && max
if min && max && min == max
["(#{@column} = ?)", min]
elsif min && max
["(#{@column} BETWEEN ? AND ?)", min, max]
elsif min
["(#{@column} >= ?)", min]
Expand Down
4 changes: 3 additions & 1 deletion lib/rails_admin/adapters/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,9 @@ def build_statement_for_belongs_to_association_or_bson_object_id
end

def range_filter(min, max)
if min && max
if min && max && min == max
{@column => min}
elsif min && max
{@column => {'$gte' => min, '$lte' => max}}
elsif min
{@column => {'$gte' => min}}
Expand Down
11 changes: 4 additions & 7 deletions lib/rails_admin/config/fields/types/date.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module Types
class Date < RailsAdmin::Config::Fields::Types::Datetime
RailsAdmin::Config::Fields::Types.register(self)

def parse_value(value)
::Date.parse(value) if value.present?
end

register_instance_option :date_format do
:long
end
Expand All @@ -15,13 +19,6 @@ class Date < RailsAdmin::Config::Fields::Types::Datetime
[:date, :formats]
end

register_instance_option :datepicker_options do
{
showTodayButton: true,
format: parser.to_momentjs,
}
end

register_instance_option :html_attributes do
{
required: required?,
Expand Down
29 changes: 14 additions & 15 deletions lib/rails_admin/config/fields/types/datetime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,14 @@ module Types
class Datetime < RailsAdmin::Config::Fields::Base
RailsAdmin::Config::Fields::Types.register(self)

def parser
RailsAdmin::Support::Datetime.new(strftime_format)
end

def parse_value(value)
parser.parse_string(value)
::Time.zone.parse(value)
end

def parse_input(params)
params[name] = parse_value(params[name]) if params[name]
end

def value
parent_value = super
if %w(DateTime Date Time).include?(parent_value.class.name)
parent_value.in_time_zone
else
parent_value
end
end

register_instance_option :date_format do
:long
end
Expand All @@ -43,10 +30,14 @@ def value
"%B %d, %Y %H:%M"
end

def momentjs_format
RailsAdmin::Support::Datetime.to_momentjs(strftime_format)
end

register_instance_option :datepicker_options do
{
showTodayButton: true,
format: parser.to_momentjs,
format: momentjs_format,
}
end

Expand All @@ -61,6 +52,10 @@ def value
true
end

register_instance_option :queryable? do
false
end

register_instance_option :formatted_value do
if time = (value || default_value)
::I18n.l(time, format: strftime_format)
Expand All @@ -72,6 +67,10 @@ def value
register_instance_option :partial do
:form_datetime
end

def form_value
value&.in_time_zone&.strftime('%FT%T') || form_default_value
end
end
end
end
Expand Down
5 changes: 1 addition & 4 deletions lib/rails_admin/config/fields/types/time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ class Time < RailsAdmin::Config::Fields::Types::Datetime
RailsAdmin::Config::Fields::Types.register(self)

def parse_value(value)
parent_value = super(value)
return unless parent_value
value_with_tz = parent_value.in_time_zone
::DateTime.parse(value_with_tz.strftime('%Y-%m-%d %H:%M:%S'))
abstract_model.model.type_for_attribute(name.to_s).serialize(super)&.change(year: 2000, month: 1, day: 1)
end

register_instance_option :strftime_format do
Expand Down
4 changes: 0 additions & 4 deletions lib/rails_admin/config/fields/types/timestamp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ module Types
class Timestamp < RailsAdmin::Config::Fields::Types::Datetime
# Register field type for the type loader
RailsAdmin::Config::Fields::Types.register(self)

@format = :long
@i18n_scope = [:time, :formats]
@js_plugin_options = {}
end
end
end
Expand Down
Loading

0 comments on commit 01e8d5f

Please sign in to comment.