@@ -71,52 +71,111 @@ impl DateTime {
7171 Self :: from_epoch_msec_tz ( epoch_msec, 0 )
7272 }
7373 pub fn from_iso_str ( iso_str : & str ) -> Result < DateTime , String > {
74- const PATTERN : & str = "2020-02-03T11:59:43" ;
75- if iso_str. len ( ) >= PATTERN . len ( ) {
76- let s = iso_str;
77- let naive_str = & s[ ..PATTERN . len ( ) ] ;
78- if let Ok ( ndt) = chrono:: NaiveDateTime :: parse_from_str ( naive_str, "%Y-%m-%dT%H:%M:%S" ) {
79- let mut msec = 0 ;
80- let mut offset = 0 ;
81- let mut rest = & s[ PATTERN . len ( ) ..] ;
82- if matches ! ( rest. as_bytes( ) . first( ) , Some ( b'.' ) ) {
83- rest = & rest[ 1 ..] ;
84- if rest. len ( ) >= 3 {
85- match rest[ ..3 ] . parse :: < i32 > ( ) {
86- Ok ( ms) => {
87- msec = ms;
88- rest = & rest[ 3 ..] ;
89- }
90- Err ( err) => {
91- return Err ( format ! ( "Parsing DateTime msec part error: {err}, in '{iso_str}" ) )
92- }
93- }
94- }
95- }
96- if !rest. is_empty ( ) {
97- if rest. len ( ) == 1 && * rest. as_bytes ( ) . first ( ) . expect ( "len() is 1" ) == b'Z' {
98- } else if rest. len ( ) == 3 {
99- if let Ok ( hrs) = rest. parse :: < i32 > ( ) {
100- offset = 60 * 60 * hrs;
101- } else {
102- return Err ( format ! ( "Invalid DateTime TZ(3) part: '{rest}, date time: {iso_str}" ) )
103- }
104- } else if rest. len ( ) == 5 {
105- if let Ok ( hrs) = rest. parse :: < i32 > ( ) {
106- offset = 60 * ( 60 * ( hrs / 100 ) + ( hrs % 100 ) ) ;
107- } else {
108- return Err ( format ! ( "Invalid DateTime TZ(5) part: '{rest}, date time: {iso_str}" ) )
109- }
110- } else {
111- return Err ( format ! ( "Invalid DateTime TZ part: '{rest}, date time: {iso_str}" ) )
112- }
74+ const BASE_LEN : usize = 19 ;
75+ let b = iso_str. as_bytes ( ) ;
76+ if b. len ( ) < BASE_LEN {
77+ return Err ( format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ;
78+ }
79+ if b[ 4 ] != b'-' || b[ 7 ] != b'-' || b[ 10 ] != b'T' || b[ 13 ] != b':' || b[ 16 ] != b':' {
80+ return Err ( format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ;
81+ }
82+ fn parse_digits ( slice : & [ u8 ] ) -> Option < i32 > {
83+ let mut v = 0i32 ;
84+ for & c in slice {
85+ if c < b'0' || c > b'9' {
86+ return None ;
87+ }
88+ v = v * 10 + ( ( c - b'0' ) as i32 ) ;
89+ }
90+ Some ( v)
91+ }
92+ let year = parse_digits ( & b[ 0 ..4 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
93+ let month = parse_digits ( & b[ 5 ..7 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
94+ let day = parse_digits ( & b[ 8 ..10 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
95+ let hour = parse_digits ( & b[ 11 ..13 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
96+ let minute = parse_digits ( & b[ 14 ..16 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
97+ let second = parse_digits ( & b[ 17 ..19 ] ) . ok_or_else ( || format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ?;
98+
99+ let mut idx = BASE_LEN ;
100+ let mut msec: i32 = 0 ;
101+
102+ if idx < b. len ( ) && b[ idx] == b'.' {
103+ idx += 1 ;
104+ let mut digits = 0usize ;
105+ let mut val = 0i32 ;
106+ while idx < b. len ( ) && digits < 3 {
107+ let c = b[ idx] ;
108+ if c < b'0' || c > b'9' {
109+ break ;
110+ }
111+ val = val * 10 + ( ( c - b'0' ) as i32 ) ;
112+ idx += 1 ;
113+ digits += 1 ;
114+ }
115+ if digits == 0 {
116+ return Err ( format ! ( "Parsing DateTime msec part error, in '{iso_str}" ) ) ;
117+ }
118+ if digits == 1 {
119+ msec = val * 100 ;
120+ } else if digits == 2 {
121+ msec = val * 10 ;
122+ } else {
123+ msec = val;
124+ }
125+ while idx < b. len ( ) {
126+ let c = b[ idx] ;
127+ if c < b'0' || c > b'9' {
128+ break ;
129+ }
130+ idx += 1 ;
131+ }
132+ }
133+
134+ let mut offset_seconds: i32 = 0 ;
135+ if idx < b. len ( ) {
136+ let c = b[ idx] ;
137+ if c == b'Z' {
138+ if idx + 1 != b. len ( ) {
139+ return Err ( format ! ( "Invalid DateTime TZ part: '{}', date time: {iso_str}" , & iso_str[ idx..] ) ) ;
140+ }
141+ } else if c == b'+' || c == b'-' {
142+ let sign = if c == b'-' { -1 } else { 1 } ;
143+ idx += 1 ;
144+ if idx >= b. len ( ) {
145+ return Err ( format ! ( "Invalid DateTime TZ part: '{}', date time: {iso_str}" , & iso_str[ idx. saturating_sub( 1 ) ..] ) ) ;
146+ }
147+ let rem = & b[ idx..] ;
148+ if rem. len ( ) >= 5 && rem[ 2 ] == b':' {
149+ let hh = parse_digits ( & rem[ 0 ..2 ] ) . ok_or_else ( || format ! ( "Invalid DateTime TZ(3) part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ?;
150+ let mm = parse_digits ( & rem[ 3 ..5 ] ) . ok_or_else ( || format ! ( "Invalid DateTime TZ(3) part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ?;
151+ if rem. len ( ) != 5 {
152+ return Err ( format ! ( "Invalid DateTime TZ part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ;
113153 }
114- let epoch_msec = ( ndt. and_utc ( ) . timestamp ( ) - i64:: from ( offset) ) * 1000 + i64:: from ( msec) ;
115- let dt = DateTime :: from_epoch_msec_tz ( epoch_msec, offset) ;
116- return Ok ( dt)
154+ offset_seconds = sign * ( hh * 3600 + mm * 60 ) ;
155+ } else if rem. len ( ) == 2 {
156+ let hh = parse_digits ( & rem[ 0 ..2 ] ) . ok_or_else ( || format ! ( "Invalid DateTime TZ(3) part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ?;
157+ offset_seconds = sign * ( hh * 3600 ) ;
158+ } else if rem. len ( ) == 4 {
159+ let hh = parse_digits ( & rem[ 0 ..2 ] ) . ok_or_else ( || format ! ( "Invalid DateTime TZ(5) part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ?;
160+ let mm = parse_digits ( & rem[ 2 ..4 ] ) . ok_or_else ( || format ! ( "Invalid DateTime TZ(5) part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ?;
161+ offset_seconds = sign * ( hh * 3600 + mm * 60 ) ;
162+ } else {
163+ return Err ( format ! ( "Invalid DateTime TZ part: '{}', date time: {iso_str}" , & iso_str[ idx - 1 ..] ) ) ;
117164 }
165+ } else {
166+ return Err ( format ! ( "Invalid DateTime TZ part: '{}', date time: {iso_str}" , & iso_str[ idx..] ) ) ;
118167 }
119- Err ( format ! ( "Invalid DateTime: '{iso_str:?}" ) )
168+ }
169+
170+ let naive = match chrono:: NaiveDate :: from_ymd_opt ( year, month as u32 , day as u32 )
171+ . and_then ( |d| chrono:: NaiveTime :: from_hms_milli_opt ( hour as u32 , minute as u32 , second as u32 , msec as u32 ) . map ( |t| d. and_time ( t) ) ) {
172+ Some ( ndt) => ndt,
173+ None => return Err ( format ! ( "Invalid DateTime: '{iso_str:?}" ) ) ,
174+ } ;
175+
176+ let epoch_msec = ( naive. and_utc ( ) . timestamp ( ) - i64:: from ( offset_seconds) ) * 1000 + i64:: from ( msec) ;
177+ let dt = DateTime :: from_epoch_msec_tz ( epoch_msec, offset_seconds) ;
178+ Ok ( dt)
120179 }
121180 pub fn epoc_msec_utc_offset ( self ) -> ( i64 , i32 ) {
122181 let msec= self . 0 / ( TZ_MASK + 1 ) ;
0 commit comments