@@ -505,48 +505,48 @@ defmodule DateTime do
505505 @ doc since: "1.4.0"
506506 @ spec from_iso8601 ( String . t ( ) , Calendar . calendar ( ) ) ::
507507 { :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+
508513 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 ,
518516 { 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
540549 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 }
550550 _ -> { :error , :invalid_format }
551551 end
552552 end
0 commit comments