@@ -505,48 +505,48 @@ defmodule DateTime do
505
505
@ doc since: "1.4.0"
506
506
@ spec from_iso8601 ( String . t ( ) , Calendar . calendar ( ) ) ::
507
507
{ :ok , t , Calendar . utc_offset ( ) } | { :error , atom }
508
+
509
+ @ sep [ ?\s , ?T ]
510
+ [ match_date , guard_date , read_date ] = Calendar.ISO . __match_date__ ( )
511
+ [ match_time , guard_time , read_time ] = Calendar.ISO . __match_time__ ( )
512
+
508
513
def from_iso8601 ( string , calendar \\ Calendar.ISO ) when is_binary ( string ) do
509
- with << year :: 4 - bytes , ?- , month :: 2 - bytes , ?- , day :: 2 - bytes , sep , rest :: binary >> <- string ,
510
- true <- sep in [ ?\s , ?T ] ,
511
- << hour :: 2 - bytes , ?: , min :: 2 - bytes , ?: , sec :: 2 - bytes , rest :: binary >> <- rest ,
512
- { year , "" } <- Integer . parse ( year ) ,
513
- { month , "" } <- Integer . parse ( month ) ,
514
- { day , "" } <- Integer . parse ( day ) ,
515
- { hour , "" } <- Integer . parse ( hour ) ,
516
- { minute , "" } <- Integer . parse ( min ) ,
517
- { second , "" } <- Integer . parse ( sec ) ,
514
+ with << unquote ( match_date ) , sep , unquote ( match_time ) , rest :: binary >>
515
+ when unquote ( guard_date ) and sep in @ sep and unquote ( guard_time ) <- string ,
518
516
{ microsecond , rest } <- Calendar.ISO . parse_microsecond ( rest ) ,
519
- { :ok , date } <- Date . new ( year , month , day ) ,
520
- { :ok , time } <- Time . new ( hour , minute , second , microsecond ) ,
521
- { :ok , offset } <- parse_offset ( rest ) do
522
- % { year: year , month: month , day: day } = date
523
- % { hour: hour , minute: minute , second: second , microsecond: microsecond } = time
524
- { _ , precision } = microsecond
525
-
526
- datetime =
527
- Calendar.ISO . naive_datetime_to_iso_days (
528
- year ,
529
- month ,
530
- day ,
531
- hour ,
532
- minute ,
533
- second ,
534
- microsecond
535
- )
536
- |> apply_tz_offset ( offset )
537
- |> from_iso_days ( "Etc/UTC" , "UTC" , 0 , 0 , calendar , precision )
538
-
539
- { :ok , % { datetime | microsecond: microsecond } , offset }
517
+ { offset , "" } <- Calendar.ISO . parse_offset ( rest ) do
518
+ { year , month , day } = unquote ( read_date )
519
+ { hour , minute , second } = unquote ( read_time )
520
+
521
+ cond do
522
+ not calendar . valid_date? ( year , month , day ) ->
523
+ { :error , :invalid_date }
524
+
525
+ not calendar . valid_time? ( hour , minute , second , microsecond ) ->
526
+ { :error , :invalid_time }
527
+
528
+ is_nil ( offset ) ->
529
+ { :error , :missing_offset }
530
+
531
+ true ->
532
+ { _ , precision } = microsecond
533
+
534
+ datetime =
535
+ Calendar.ISO . naive_datetime_to_iso_days (
536
+ year ,
537
+ month ,
538
+ day ,
539
+ hour ,
540
+ minute ,
541
+ second ,
542
+ microsecond
543
+ )
544
+ |> apply_tz_offset ( offset )
545
+ |> from_iso_days ( "Etc/UTC" , "UTC" , 0 , 0 , calendar , precision )
546
+
547
+ { :ok , % { datetime | microsecond: microsecond } , offset }
548
+ end
540
549
else
541
- { :error , reason } -> { :error , reason }
542
- _ -> { :error , :invalid_format }
543
- end
544
- end
545
-
546
- defp parse_offset ( rest ) do
547
- case Calendar.ISO . parse_offset ( rest ) do
548
- { offset , "" } when is_integer ( offset ) -> { :ok , offset }
549
- { nil , "" } -> { :error , :missing_offset }
550
550
_ -> { :error , :invalid_format }
551
551
end
552
552
end
0 commit comments