recurrence_ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +208 -0
- data/Rakefile +8 -0
- data/app/assets/config/recurrence_ui_manifest.js +1 -0
- data/app/assets/stylesheets/recurrence_ui/application.css +15 -0
- data/app/assets/stylesheets/recurrence_ui/components.css +295 -0
- data/app/assets/stylesheets/recurrence_ui/tokens.css +102 -0
- data/app/components/recurrence_ui/recurrence_picker_component.html.erb +234 -0
- data/app/components/recurrence_ui/recurrence_picker_component.rb +204 -0
- data/app/controllers/recurrence_ui/application_controller.rb +32 -0
- data/app/helpers/recurrence_ui/application_helper.rb +23 -0
- data/app/javascript/recurrence_ui/index.js +22 -0
- data/app/javascript/recurrence_ui/recurrence_ui_controller.js +954 -0
- data/app/jobs/recurrence_ui/application_job.rb +4 -0
- data/app/mailers/recurrence_ui/application_mailer.rb +6 -0
- data/app/models/concerns/recurrence_ui/has_recurrence.rb +351 -0
- data/app/models/recurrence_ui/application_record.rb +5 -0
- data/app/services/recurrence_ui/recurrence_formatter_service.rb +536 -0
- data/app/views/layouts/recurrence_ui/application.html.erb +15 -0
- data/app/views/recurrence_ui/_daily.html.erb +22 -0
- data/app/views/recurrence_ui/_exceptions.html.erb +50 -0
- data/app/views/recurrence_ui/_footer_actions.html.erb +17 -0
- data/app/views/recurrence_ui/_monthly_by_day_of_month.html.erb +47 -0
- data/app/views/recurrence_ui/_monthly_by_weekday.html.erb +55 -0
- data/app/views/recurrence_ui/_rdates.html.erb +50 -0
- data/app/views/recurrence_ui/_rule_card.html.erb +61 -0
- data/app/views/recurrence_ui/_summary_link.html.erb +25 -0
- data/app/views/recurrence_ui/_termination.html.erb +44 -0
- data/app/views/recurrence_ui/_weekly.html.erb +44 -0
- data/app/views/recurrence_ui/_yearly_by_day_of_month.html.erb +63 -0
- data/app/views/recurrence_ui/_yearly_by_weekday.html.erb +72 -0
- data/config/i18n-tasks.yml +212 -0
- data/config/importmap.rb +3 -0
- data/config/locales/recurrence_ui.be.yml +245 -0
- data/config/locales/recurrence_ui.en.yml +210 -0
- data/config/locales/recurrence_ui.ja.yml +201 -0
- data/config/locales/recurrence_ui.ru.yml +245 -0
- data/config/locales/recurrence_ui.uk.yml +245 -0
- data/config/routes.rb +5 -0
- data/lib/generators/recurrence_ui/views_generator.rb +26 -0
- data/lib/recurrence_ui/configuration.rb +116 -0
- data/lib/recurrence_ui/engine.rb +57 -0
- data/lib/recurrence_ui/version.rb +3 -0
- data/lib/recurrence_ui.rb +25 -0
- data/lib/tasks/recurrence_ui_tasks.rake +4 -0
- metadata +195 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
module RecurrenceUi
|
|
2
|
+
module HasRecurrence
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
# Class attributes for storing model field name configuration
|
|
7
|
+
class_attribute :recurrence_rules_field, default: RecurrenceUi.config.default_recurring_rules_field
|
|
8
|
+
class_attribute :recurrence_start_time_field, default: RecurrenceUi.config.default_start_time_field
|
|
9
|
+
class_attribute :recurrence_end_time_field, default: RecurrenceUi.config.default_end_time_field
|
|
10
|
+
class_attribute :recurrence_time_zone_field, default: RecurrenceUi.config.default_time_zone_field
|
|
11
|
+
class_attribute :recurrence_duration_field, default: RecurrenceUi.config.default_duration_field
|
|
12
|
+
attr_accessor :form_start_date, :form_start_time,
|
|
13
|
+
:form_duration_mode, :form_duration_days, :form_duration_hours, :form_duration_minutes,
|
|
14
|
+
:form_end_date, :form_end_time
|
|
15
|
+
|
|
16
|
+
before_save :process_recurrence_time_attributes
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class_methods do
|
|
20
|
+
# DSL method to configure model recurrence and time fields.
|
|
21
|
+
#
|
|
22
|
+
# @param rules [Symbol, String, nil] Field name for storing rules (JSON/Hash).
|
|
23
|
+
# @param start_time [Symbol, String, nil] Field name for start date/time.
|
|
24
|
+
# @param end_time [Symbol, String, nil] Field name for end date/time.
|
|
25
|
+
# @param time_zone [Symbol, String, nil] Field name for time zone.
|
|
26
|
+
# @param duration [Symbol, String, nil] Field name for duration in seconds.
|
|
27
|
+
# @return [void]
|
|
28
|
+
def has_recurrence(rules: nil, start_time: nil, end_time: nil, time_zone: nil, duration: nil)
|
|
29
|
+
self.recurrence_rules_field = rules if rules.present?
|
|
30
|
+
self.recurrence_start_time_field = start_time if start_time.present?
|
|
31
|
+
self.recurrence_end_time_field = end_time if end_time.present?
|
|
32
|
+
self.recurrence_time_zone_field = time_zone if time_zone.present?
|
|
33
|
+
self.recurrence_duration_field = duration if duration.present?
|
|
34
|
+
|
|
35
|
+
field_name = recurrence_rules_field.to_s
|
|
36
|
+
|
|
37
|
+
# READ: Returns a Hash containing rules and UI metadata
|
|
38
|
+
define_method(field_name) do
|
|
39
|
+
raw_val = read_attribute(field_name)
|
|
40
|
+
|
|
41
|
+
# What radio button mode was selected in the form? 'duration' or 'end_at'
|
|
42
|
+
return { 'form_duration_mode' => 'duration', 'rrules' => [] }.with_indifferent_access if raw_val.blank?
|
|
43
|
+
|
|
44
|
+
parsed = if raw_val.is_a?(Hash)
|
|
45
|
+
raw_val.with_indifferent_access
|
|
46
|
+
elsif raw_val.is_a?(String)
|
|
47
|
+
JSON.parse(raw_val).with_indifferent_access rescue {}
|
|
48
|
+
else
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Ensure that the form_duration_mode key is always present, defaulting to 'duration' if missing
|
|
53
|
+
parsed[:form_duration_mode] ||= 'duration'
|
|
54
|
+
parsed
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# WRITE: Accepts a Hash or JSON string and stores it in the database (it could contain both rules and UI metadata)
|
|
58
|
+
define_method("#{field_name}=") do |value|
|
|
59
|
+
parsed_value = case value
|
|
60
|
+
when String
|
|
61
|
+
JSON.parse(value) rescue {}
|
|
62
|
+
when Hash, ActionController::Parameters
|
|
63
|
+
value.to_h
|
|
64
|
+
else
|
|
65
|
+
{}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
write_attribute(field_name, parsed_value.deep_stringify_keys)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Method to return the list of permitted parameters for recurrence fields, useful for strong parameters in controllers.
|
|
73
|
+
def recurrence_permitted_params
|
|
74
|
+
[
|
|
75
|
+
recurrence_rules_field.to_sym,
|
|
76
|
+
recurrence_start_time_field.to_sym,
|
|
77
|
+
recurrence_end_time_field.to_sym,
|
|
78
|
+
recurrence_time_zone_field.to_sym,
|
|
79
|
+
recurrence_duration_field.to_sym,
|
|
80
|
+
:form_start_date,
|
|
81
|
+
:form_start_time,
|
|
82
|
+
:form_duration_mode,
|
|
83
|
+
:form_duration_days,
|
|
84
|
+
:form_duration_hours,
|
|
85
|
+
:form_duration_minutes,
|
|
86
|
+
:form_end_date,
|
|
87
|
+
:form_end_time
|
|
88
|
+
]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
# Returns a RecurrenceFormatterService instance for formatting recurrence data.
|
|
93
|
+
# @param options [Hash] Options to customize formatting (e.g., :zone_type, :show_zone)
|
|
94
|
+
# @return [RecurrenceFormatterService]
|
|
95
|
+
def summary(options = {})
|
|
96
|
+
RecurrenceFormatterService.new(self, options)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# --- Instance Methods ---
|
|
102
|
+
# Returns the raw recurrence rules stored in the model, which may include both recurrence rules and UI metadata.
|
|
103
|
+
# @return [Hash] The raw recurrence rules hash.
|
|
104
|
+
def recurrence_raw_rules
|
|
105
|
+
public_send(self.class.recurrence_rules_field)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns the event's time zone, either from the model's time zone field or falling back to the application's default time zone.
|
|
109
|
+
# @return [ActiveSupport::TimeZone] The time zone for the event.
|
|
110
|
+
def event_time_zone
|
|
111
|
+
tz_name = if self.class.recurrence_time_zone_field &&
|
|
112
|
+
respond_to?(self.class.recurrence_time_zone_field)
|
|
113
|
+
public_send(self.class.recurrence_time_zone_field)
|
|
114
|
+
end
|
|
115
|
+
# If the time zone name is not present (nil or empty string), safely return the application's Time.zone
|
|
116
|
+
return Time.zone unless tz_name.present?
|
|
117
|
+
|
|
118
|
+
ActiveSupport::TimeZone[tz_name] || Time.zone || ActiveSupport::TimeZone[Rails.application.config.time_zone]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns the recurrence start time in the event's time zone, defaulting to Time.current if not set.
|
|
122
|
+
# @return [ActiveSupport::TimeWithZone] The start time of the recurrence.
|
|
123
|
+
def recurrence_start_time
|
|
124
|
+
raw_start = nil
|
|
125
|
+
if self.class.recurrence_start_time_field && respond_to?(self.class.recurrence_start_time_field)
|
|
126
|
+
raw_start = public_send(self.class.recurrence_start_time_field)
|
|
127
|
+
end
|
|
128
|
+
(raw_start || Time.current).in_time_zone(event_time_zone)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Returns the recurrence end time in the event's time zone, or nil if not set.
|
|
132
|
+
# @return [ActiveSupport::TimeWithZone, nil] The end time of the recurrence, or nil if not set.
|
|
133
|
+
def recurrence_end_time
|
|
134
|
+
end_field = self.class.recurrence_end_time_field
|
|
135
|
+
return nil unless end_field && respond_to?(end_field)
|
|
136
|
+
|
|
137
|
+
raw_end = public_send(end_field)
|
|
138
|
+
return nil unless raw_end.present?
|
|
139
|
+
|
|
140
|
+
raw_end.in_time_zone(event_time_zone)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the recurrence start time in the specified time zone type (:event, :application, or a specific zone name).
|
|
144
|
+
# @param zone_type [Symbol, String] The type of time zone to convert to (:event, :application, or a specific zone name).
|
|
145
|
+
# @return [ActiveSupport::TimeWithZone] The start time of the recurrence in the specified time zone.
|
|
146
|
+
def start_time_in_zone(zone_type = :event)
|
|
147
|
+
time_in_zone(recurrence_start_time, zone_type)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Returns the recurrence end time in the specified time zone type (:event, :application, or a specific zone name).
|
|
151
|
+
# @param zone_type [Symbol, String] The type of time zone to convert to (:event, :application, or a specific zone name).
|
|
152
|
+
# @return [ActiveSupport::TimeWithZone, nil] The end time of the recurrence in the specified time zone, or nil
|
|
153
|
+
def end_time_in_zone(zone_type = :event)
|
|
154
|
+
time_in_zone(recurrence_end_time, zone_type)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns the current duration of the recurrence in seconds, based on the model's duration field.
|
|
158
|
+
# @return [Integer] The duration in seconds, or 0 if not set or invalid.
|
|
159
|
+
def current_duration_seconds
|
|
160
|
+
dur_field = self.class.recurrence_duration_field
|
|
161
|
+
if dur_field && respond_to?(dur_field)
|
|
162
|
+
val = public_send(dur_field)
|
|
163
|
+
return val.to_i if val.present? && val.to_i > 0
|
|
164
|
+
end
|
|
165
|
+
0
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# --- Building IceCube::Schedule ---
|
|
170
|
+
# Returns an IceCube::Schedule object based on the stored recurrence rules and start time.
|
|
171
|
+
# @param start_time_override [Time, nil] Optional override for the start time of the schedule.
|
|
172
|
+
# @return [IceCube::Schedule] The IceCube schedule object representing the recurrence rules.
|
|
173
|
+
def schedule(start_time_override = nil)
|
|
174
|
+
base_time = start_time_override || recurrence_start_time
|
|
175
|
+
s = IceCube::Schedule.new(base_time)
|
|
176
|
+
|
|
177
|
+
dur = current_duration_seconds
|
|
178
|
+
s.duration = dur if dur > 0
|
|
179
|
+
|
|
180
|
+
raw_data = recurrence_raw_rules
|
|
181
|
+
return s if raw_data.blank?
|
|
182
|
+
|
|
183
|
+
# Превращаем в хэш с строковыми ключами на всякий случай
|
|
184
|
+
raw_data = raw_data.deep_stringify_keys if raw_data.is_a?(Hash)
|
|
185
|
+
|
|
186
|
+
# Достаем массив правил безопасно, игнорируя UI-ключи типа form_duration_mode
|
|
187
|
+
rules_array = raw_data['rrules'] || (raw_data['rule_type'] ? [raw_data] : [])
|
|
188
|
+
|
|
189
|
+
rules_array.each do |rule_hash|
|
|
190
|
+
next if rule_hash.blank? || !rule_hash.is_a?(Hash)
|
|
191
|
+
|
|
192
|
+
if rule_hash['rule_type'] == 'IceCube::MonthlyRule' &&
|
|
193
|
+
rule_hash.dig('validations', 'day_of_week').is_a?(Hash)
|
|
194
|
+
|
|
195
|
+
rule_hash['validations']['day_of_week'].transform_values! do |days|
|
|
196
|
+
Array(days).map { |day| day.to_i == 0 ? 7 : day.to_i }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
begin
|
|
201
|
+
r = IceCube::Rule.from_hash(rule_hash)
|
|
202
|
+
if respond_to?(:repeat_until) && repeat_until.present? && !rule_hash['until']
|
|
203
|
+
r = r.until(repeat_until.in_time_zone(event_time_zone).end_of_day)
|
|
204
|
+
end
|
|
205
|
+
s.add_recurrence_rule(r)
|
|
206
|
+
rescue StandardError => e
|
|
207
|
+
Rails.logger.error "[RecurrenceUi] Error building IceCube::Rule: #{e.message}"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
Array(raw_data['exdates']).each do |exdate|
|
|
212
|
+
next if exdate.blank?
|
|
213
|
+
parsed_date = Date.parse(exdate.to_s) rescue nil
|
|
214
|
+
next unless parsed_date
|
|
215
|
+
|
|
216
|
+
exact_datetime = base_time.change(
|
|
217
|
+
year: parsed_date.year,
|
|
218
|
+
month: parsed_date.month,
|
|
219
|
+
day: parsed_date.day
|
|
220
|
+
)
|
|
221
|
+
s.add_exception_time(exact_datetime)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
Array(raw_data['rdates']).each do |rdate|
|
|
225
|
+
next if rdate.blank?
|
|
226
|
+
parsed_date = Date.parse(rdate.to_s) rescue nil
|
|
227
|
+
next unless parsed_date
|
|
228
|
+
|
|
229
|
+
exact_datetime = base_time.change(
|
|
230
|
+
year: parsed_date.year,
|
|
231
|
+
month: parsed_date.month,
|
|
232
|
+
day: parsed_date.day
|
|
233
|
+
)
|
|
234
|
+
s.add_recurrence_time(exact_datetime)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
s
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Returns the next occurrences of the event based on the schedule, limited to a specified number and starting from a given time.
|
|
241
|
+
# @param limit [Integer] The maximum number of occurrences to return.
|
|
242
|
+
# @param from_time [Time] The time from which to start looking for occurrences.
|
|
243
|
+
# @return [Array<Time>] An array of the next occurrence times in the event's time zone.
|
|
244
|
+
def upcoming_occurrences(limit = 5, from_time = Time.current)
|
|
245
|
+
schedule.next_occurrences(limit, from_time.in_time_zone(event_time_zone))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Returns the next occurrences of the event in a specified time zone, limited to a specified number and starting from a given time.
|
|
249
|
+
# @param limit [Integer] The maximum number of occurrences to return.
|
|
250
|
+
# @param from_time [Time] The time from which to start looking for occurrences.
|
|
251
|
+
# @param zone_type [Symbol, String] The type of time zone to convert to (:event, :application, or a specific zone name).
|
|
252
|
+
# @return [Array<Time>] An array of the next occurrence times in the specified time zone.
|
|
253
|
+
def upcoming_occurrences_in_zone(limit = 5, from_time = Time.current, zone_type = :event)
|
|
254
|
+
target_zone = resolve_zone_type(zone_type)
|
|
255
|
+
schedule.next_occurrences(limit, from_time.in_time_zone(target_zone))
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Returns the first occurrence of the event based on the schedule.
|
|
259
|
+
# @return [Time, nil] The time of the first occurrence in the event's time zone, or nil if there are no occurrences.
|
|
260
|
+
def first_occurrence
|
|
261
|
+
schedule.first
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Returns the rule object for the first recurrence rule in the schedule, if any.
|
|
265
|
+
# @return [IceCube::Rule, nil] The first recurrence rule, or nil if there are no rules.
|
|
266
|
+
def rule
|
|
267
|
+
schedule.recurrence_rules.first
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Delegate formatting methods to the summary service
|
|
271
|
+
delegate :formatted_start_time, :formatted_end_time, :formatted_duration, :formatted_upcoming_occurrences, :to_s, to: :summary
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
# Returns the given time in the specified time zone type (:event, :application, or a specific zone name).
|
|
276
|
+
# @param time [Time, nil] The time to convert.
|
|
277
|
+
# @param zone_type [Symbol, String] The type of time zone to convert to (:event, :application, or a specific zone name).
|
|
278
|
+
# @return [ActiveSupport::TimeWithZone, nil] The time in the specified time zone, or nil if the input time is nil.
|
|
279
|
+
def time_in_zone(time, zone_type = :event)
|
|
280
|
+
return nil unless time
|
|
281
|
+
|
|
282
|
+
target_zone = resolve_zone_type(zone_type)
|
|
283
|
+
time.in_time_zone(target_zone)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Processes the form attributes related to recurrence time and updates the model's fields accordingly.
|
|
287
|
+
# This method is called before saving the record to ensure that the recurrence start time, end time, and duration are correctly calculated and stored based on the form inputs.
|
|
288
|
+
def process_recurrence_time_attributes
|
|
289
|
+
tz = event_time_zone
|
|
290
|
+
|
|
291
|
+
if respond_to?(:form_start_date=) && form_start_date.present?
|
|
292
|
+
time_str = form_start_time.presence || '09:00'
|
|
293
|
+
parsed_start = tz.parse("#{form_start_date} #{time_str}")
|
|
294
|
+
write_attribute(self.class.recurrence_start_time_field, parsed_start) if parsed_start
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
start_val = public_send(self.class.recurrence_start_time_field)
|
|
298
|
+
return unless start_val.present?
|
|
299
|
+
|
|
300
|
+
dur_field = self.class.recurrence_duration_field
|
|
301
|
+
end_field = self.class.recurrence_end_time_field
|
|
302
|
+
|
|
303
|
+
# Получаем текущий хэш (он уже содержит и правила, и метаданные)
|
|
304
|
+
current_rules = recurrence_raw_rules.deep_dup
|
|
305
|
+
|
|
306
|
+
# Сохраняем выбранный в форме режим в общие метаданные правил
|
|
307
|
+
if form_duration_mode.present?
|
|
308
|
+
current_rules[:form_duration_mode] = form_duration_mode
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
if form_duration_mode == 'end_at' && form_end_date.present?
|
|
312
|
+
end_time_str = form_end_time.presence || '09:00'
|
|
313
|
+
parsed_end = tz.parse("#{form_end_date} #{end_time_str}")
|
|
314
|
+
|
|
315
|
+
if parsed_end.present?
|
|
316
|
+
write_attribute(end_field, parsed_end) if read_attribute(end_field) != parsed_end
|
|
317
|
+
calculated_dur = (parsed_end - start_val).to_i
|
|
318
|
+
write_attribute(dur_field, calculated_dur) if dur_field && respond_to?("#{dur_field}=") && read_attribute(dur_field) != calculated_dur
|
|
319
|
+
end
|
|
320
|
+
else
|
|
321
|
+
days = form_duration_days.to_i
|
|
322
|
+
hours = form_duration_hours.to_i
|
|
323
|
+
minutes = form_duration_minutes.to_i
|
|
324
|
+
total_seconds = (days * 1.day + hours * 1.hour + minutes * 1.minute).to_i
|
|
325
|
+
total_seconds = 1.hour if total_seconds <= 0
|
|
326
|
+
|
|
327
|
+
write_attribute(dur_field, total_seconds) if dur_field && respond_to?("#{dur_field}=") && read_attribute(dur_field) != total_seconds
|
|
328
|
+
|
|
329
|
+
calculated_end = start_val + total_seconds.seconds
|
|
330
|
+
write_attribute(end_field, calculated_end) if end_field && respond_to?("#{end_field}=") && read_attribute(end_field) != calculated_end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Записываем обновленный хэш (вместе с form_duration_mode) обратно через наш сеттер
|
|
334
|
+
public_send("#{self.class.recurrence_rules_field}=", current_rules)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Resolves the appropriate time zone based on the provided zone type.
|
|
338
|
+
# @param zone_type [Symbol, String] The type of time zone to resolve (:event, :application, or a specific zone name).
|
|
339
|
+
# @return [ActiveSupport::TimeZone] The resolved time zone object.
|
|
340
|
+
def resolve_zone_type(zone_type)
|
|
341
|
+
case zone_type
|
|
342
|
+
when :event
|
|
343
|
+
event_time_zone
|
|
344
|
+
when :application, :app
|
|
345
|
+
Time.zone
|
|
346
|
+
else
|
|
347
|
+
Time.find_zone(zone_type) || Time.zone
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|