@@ -146,7 +146,8 @@ defmodule Tz.IanaFileParser do
146
146
end
147
147
148
148
for year <- Range . new ( from_year , to_year ) do
149
- { year , month , day } = parse_day_string ( year , month , rule . day )
149
+ parsed_day = parse_day_string ( rule . day )
150
+ { year , month , day } = parsed_day_to_date ( year , month , parsed_day )
150
151
151
152
naive_date_time = new_naive_date_time ( year , month , day , hour , minute , second )
152
153
@@ -159,11 +160,38 @@ defmodule Tz.IanaFileParser do
159
160
name: rule . name ,
160
161
local_offset_from_std_time: local_offset ,
161
162
letter: if ( rule . letter == "-" , do: "" , else: rule . letter ) ,
162
- raw: rule
163
+ __datetime_data: % {
164
+ date: { year , month , parsed_day } ,
165
+ time: { hour , minute , second , time_modifier }
166
+ }
163
167
}
164
168
end
165
169
end
166
170
171
+ def change_rule_year ( rule , year , ongoing_switch \\ false )
172
+
173
+ def change_rule_year ( % { to: _ } = rule , year , ongoing_switch ) do
174
+ rule
175
+ |> Map . put ( :ongoing_switch , ongoing_switch )
176
+ |> Map . delete ( :to )
177
+ |> change_rule_year ( year , ongoing_switch )
178
+ end
179
+
180
+ def change_rule_year ( % { } = rule , year , ongoing_switch ) do
181
+ % {
182
+ date: { _ , month , parsed_day } ,
183
+ time: { hour , minute , second , time_modifier }
184
+ } = rule . __datetime_data
185
+
186
+ { year , month , day } = parsed_day_to_date ( year , month , parsed_day )
187
+ naive_date_time = new_naive_date_time ( year , month , day , hour , minute , second )
188
+
189
+ % { rule |
190
+ from: { naive_date_time , time_modifier } ,
191
+ ongoing_switch: ongoing_switch
192
+ }
193
+ end
194
+
167
195
defp new_naive_date_time ( year , month , day , 24 , minute , second ) do
168
196
{ :ok , naive_date_time } = NaiveDateTime . new ( year , month , day , 0 , minute , second )
169
197
NaiveDateTime . add ( naive_date_time , 86400 )
@@ -179,30 +207,43 @@ defmodule Tz.IanaFileParser do
179
207
naive_date_time
180
208
end
181
209
182
- defp parse_day_string ( year , month , day_string ) do
210
+ defp parse_day_string ( day_string ) do
183
211
cond do
184
212
String . contains? ( day_string , "last" ) ->
185
213
"last" <> day_of_week_string = day_string
186
214
day_of_week = day_of_week_string_to_integer ( day_of_week_string )
187
- day = day_at_last_given_day_of_week_of_month ( year , month , day_of_week )
188
- { year , month , day }
215
+ { :last_dow , day_of_week }
189
216
String . contains? ( day_string , "<=" ) ->
190
217
[ day_of_week_string , on_or_before_day ] = String . split ( day_string , "<=" , trim: true )
191
218
day_of_week = day_of_week_string_to_integer ( day_of_week_string )
192
219
on_or_before_day = String . to_integer ( on_or_before_day )
193
- day_at_given_day_of_week_of_month ( year , month , day_of_week , : on_or_before_day, on_or_before_day )
220
+ { :dow_equal_or_before_day , day_of_week , on_or_before_day }
194
221
String . contains? ( day_string , ">=" ) ->
195
222
[ day_of_week_string , on_or_after_day ] = String . split ( day_string , ">=" , trim: true )
196
223
day_of_week = day_of_week_string_to_integer ( day_of_week_string )
197
224
on_or_after_day = String . to_integer ( on_or_after_day )
198
- day_at_given_day_of_week_of_month ( year , month , day_of_week , : on_or_after_day, on_or_after_day )
225
+ { :dow_equal_or_after_day , day_of_week , on_or_after_day }
199
226
String . match? ( day_string , ~r/ [0-9]+/ ) ->
200
- { year , month , String . to_integer ( day_string ) }
227
+ { :day , String . to_integer ( day_string ) }
201
228
true ->
202
229
raise "could not parse day from rule (day to parse is \" #{ day_string } \" )"
203
230
end
204
231
end
205
232
233
+ defp parsed_day_to_date ( year , month , parsed_day ) do
234
+ case parsed_day do
235
+ { :last_dow , day_of_week } ->
236
+ day = day_at_last_given_day_of_week_of_month ( year , month , day_of_week )
237
+ { year , month , day }
238
+ { :dow_equal_or_before_day , day_of_week , on_or_before_day } ->
239
+ day_at_given_day_of_week_of_month ( year , month , day_of_week , :on_or_before_day , on_or_before_day )
240
+ { :dow_equal_or_after_day , day_of_week , on_or_after_day } ->
241
+ day_at_given_day_of_week_of_month ( year , month , day_of_week , :on_or_after_day , on_or_after_day )
242
+ { :day , day } ->
243
+ { year , month , day }
244
+ end
245
+ end
246
+
206
247
defp parse_to_field_string ( :min ) , do: :min
207
248
defp parse_to_field_string ( :max ) , do: :max
208
249
defp parse_to_field_string ( to_field_string ) do
@@ -211,14 +252,16 @@ defmodule Tz.IanaFileParser do
211
252
[ year , month , day , time ] ->
212
253
year = String . to_integer ( year )
213
254
month = month_string_to_integer ( month )
214
- { year , month , day } = parse_day_string ( year , month , day )
255
+ parsed_day = parse_day_string ( day )
256
+ { year , month , day } = parsed_day_to_date ( year , month , parsed_day )
215
257
216
258
{ hour , minute , second , time_modifier } = parse_time_string ( time )
217
259
{ year , month , day , hour , minute , second , time_modifier }
218
260
[ year , month , day ] ->
219
261
year = String . to_integer ( year )
220
262
month = month_string_to_integer ( month )
221
- { year , month , day } = parse_day_string ( year , month , day )
263
+ parsed_day = parse_day_string ( day )
264
+ { year , month , day } = parsed_day_to_date ( year , month , parsed_day )
222
265
223
266
{ year , month , day , 0 , 0 , 0 , :wall }
224
267
[ year , month ] ->
@@ -385,32 +428,25 @@ defmodule Tz.IanaFileParser do
385
428
ongoing_switch_rules = Enum . filter ( rules , & & 1 . ongoing_switch )
386
429
387
430
rules =
388
- case length ( ongoing_switch_rules ) do
389
- 0 ->
431
+ case ongoing_switch_rules do
432
+ [ ] ->
390
433
rules
391
- 2 ->
392
- [ rule1 , rule2 ] = ongoing_switch_rules
434
+ [ rule1 , rule2 ] ->
393
435
last_year = Enum . max ( [
394
436
build_periods_with_ongoing_dst_changes_until_year ,
395
437
elem ( rule1 . from , 0 ) . year ,
396
438
elem ( rule2 . from , 0 ) . year
397
439
] )
398
440
399
441
Enum . filter ( rules , & ! & 1 . ongoing_switch )
400
- ++ ( rule1 . raw
401
- |> Map . put ( :to_year , "#{ last_year } " )
402
- |> transform_rule ( ) )
403
- ++ ( rule1 . raw
404
- |> Map . put ( :from_year , "#{ last_year + 1 } " )
405
- |> Map . put ( :to_year , "max" )
406
- |> transform_rule ( ) )
407
- ++ ( rule2 . raw
408
- |> Map . put ( :to_year , "#{ last_year } " )
409
- |> transform_rule ( ) )
410
- ++ ( rule2 . raw
411
- |> Map . put ( :from_year , "#{ last_year + 1 } " )
412
- |> Map . put ( :to_year , "max" )
413
- |> transform_rule ( ) )
442
+ ++ for year <- Range . new ( elem ( rule1 . from , 0 ) . year , last_year ) do
443
+ change_rule_year ( rule1 , year )
444
+ end
445
+ ++ [ change_rule_year ( rule1 , last_year + 1 , true ) ]
446
+ ++ for year <- Range . new ( elem ( rule2 . from , 0 ) . year , last_year ) do
447
+ change_rule_year ( rule2 , year )
448
+ end
449
+ ++ [ change_rule_year ( rule2 , last_year + 1 , true ) ]
414
450
_ ->
415
451
raise "unexpected number of rules to \" max\" , rules: \" #{ inspect rules } \" "
416
452
end
0 commit comments