use super ::{Timespec, Tm, at_utc, ParseError, NSEC_PER_SEC};
/// Parses the time from the string according to the format string.
pub fn strptime(mut s: &str, format: &str) -> Result<Tm, ParseError> {
let mut tm = Tm {
tm_sec: 0 ,
tm_min: 0 ,
tm_hour: 0 ,
tm_mday: 0 ,
tm_mon: 0 ,
tm_year: 0 ,
tm_wday: 0 ,
tm_yday: 0 ,
tm_isdst: 0 ,
tm_utcoff: 0 ,
tm_nsec: 0 ,
};
let mut chars = format.chars();
while let Some(ch) = chars.next() {
if ch == '%' {
if let Some(ch) = chars.next() {
parse_type(&mut s, ch, &mut tm)?;
}
} else {
parse_char(&mut s, ch)?;
}
}
Ok(tm)
}
fn parse_type(s: &mut &str, ch: char, tm: &mut Tm) -> Result<(), ParseError> {
match ch {
'A' => match match_strs(s, &[("Sunday" , 0 ),
("Monday" , 1 ),
("Tuesday" , 2 ),
("Wednesday" , 3 ),
("Thursday" , 4 ),
("Friday" , 5 ),
("Saturday" , 6 )]) {
Some(v) => { tm.tm_wday = v; Ok(()) }
None => Err(ParseError::InvalidDay)
},
'a' => match match_strs(s, &[("Sun" , 0 ),
("Mon" , 1 ),
("Tue" , 2 ),
("Wed" , 3 ),
("Thu" , 4 ),
("Fri" , 5 ),
("Sat" , 6 )]) {
Some(v) => { tm.tm_wday = v; Ok(()) }
None => Err(ParseError::InvalidDay)
},
'B' => match match_strs(s, &[("January" , 0 ),
("February" , 1 ),
("March" , 2 ),
("April" , 3 ),
("May" , 4 ),
("June" , 5 ),
("July" , 6 ),
("August" , 7 ),
("September" , 8 ),
("October" , 9 ),
("November" , 10 ),
("December" , 11 )]) {
Some(v) => { tm.tm_mon = v; Ok(()) }
None => Err(ParseError::InvalidMonth)
},
'b' | 'h' => match match_strs(s, &[("Jan" , 0 ),
("Feb" , 1 ),
("Mar" , 2 ),
("Apr" , 3 ),
("May" , 4 ),
("Jun" , 5 ),
("Jul" , 6 ),
("Aug" , 7 ),
("Sep" , 8 ),
("Oct" , 9 ),
("Nov" , 10 ),
("Dec" , 11 )]) {
Some(v) => { tm.tm_mon = v; Ok(()) }
None => Err(ParseError::InvalidMonth)
},
'C' => match match_digits_in_range(s, 1 , 2 , false , 0 , 99 ) {
Some(v) => { tm.tm_year += (v * 100 ) - 1900 ; Ok(()) }
None => Err(ParseError::InvalidYear)
},
'c' => {
parse_type(s, 'a' , tm)
.and_then(|()| parse_char(s, ' ' ))
.and_then(|()| parse_type(s, 'b' , tm))
.and_then(|()| parse_char(s, ' ' ))
.and_then(|()| parse_type(s, 'e' , tm))
.and_then(|()| parse_char(s, ' ' ))
.and_then(|()| parse_type(s, 'T' , tm))
.and_then(|()| parse_char(s, ' ' ))
.and_then(|()| parse_type(s, 'Y' , tm))
}
'D' | 'x' => {
parse_type(s, 'm' , tm)
.and_then(|()| parse_char(s, '/' ))
.and_then(|()| parse_type(s, 'd' , tm))
.and_then(|()| parse_char(s, '/' ))
.and_then(|()| parse_type(s, 'y' , tm))
}
'd' => match match_digits_in_range(s, 1 , 2 , false , 1 , 31 ) {
Some(v) => { tm.tm_mday = v; Ok(()) }
None => Err(ParseError::InvalidDayOfMonth)
},
'e' => match match_digits_in_range(s, 1 , 2 , true , 1 , 31 ) {
Some(v) => { tm.tm_mday = v; Ok(()) }
None => Err(ParseError::InvalidDayOfMonth)
},
'f' => {
tm.tm_nsec = match_fractional_seconds(s);
Ok(())
}
'F' => {
parse_type(s, 'Y' , tm)
.and_then(|()| parse_char(s, '-' ))
.and_then(|()| parse_type(s, 'm' , tm))
.and_then(|()| parse_char(s, '-' ))
.and_then(|()| parse_type(s, 'd' , tm))
}
'H' => {
match match_digits_in_range(s, 1 , 2 , false , 0 , 23 ) {
Some(v) => { tm.tm_hour = v; Ok(()) }
None => Err(ParseError::InvalidHour)
}
}
'I' => {
match match_digits_in_range(s, 1 , 2 , false , 1 , 12 ) {
Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
None => Err(ParseError::InvalidHour)
}
}
'j' => {
match match_digits_in_range(s, 1 , 3 , false , 1 , 366 ) {
Some(v) => { tm.tm_yday = v - 1 ; Ok(()) }
None => Err(ParseError::InvalidDayOfYear)
}
}
'k' => {
match match_digits_in_range(s, 1 , 2 , true , 0 , 23 ) {
Some(v) => { tm.tm_hour = v; Ok(()) }
None => Err(ParseError::InvalidHour)
}
}
'l' => {
match match_digits_in_range(s, 1 , 2 , true , 1 , 12 ) {
Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
None => Err(ParseError::InvalidHour)
}
}
'M' => {
match match_digits_in_range(s, 1 , 2 , false , 0 , 59 ) {
Some(v) => { tm.tm_min = v; Ok(()) }
None => Err(ParseError::InvalidMinute)
}
}
'm' => {
match match_digits_in_range(s, 1 , 2 , false , 1 , 12 ) {
Some(v) => { tm.tm_mon = v - 1 ; Ok(()) }
None => Err(ParseError::InvalidMonth)
}
}
'n' => parse_char(s, '\n' ),
'P' => match match_strs(s, &[("am" , 0 ), ("pm" , 12 )]) {
Some(v) => { tm.tm_hour += v; Ok(()) }
None => Err(ParseError::InvalidHour)
},
'p' => match match_strs(s, &[("AM" , 0 ), ("PM" , 12 )]) {
Some(v) => { tm.tm_hour += v; Ok(()) }
None => Err(ParseError::InvalidHour)
},
'R' => {
parse_type(s, 'H' , tm)
.and_then(|()| parse_char(s, ':' ))
.and_then(|()| parse_type(s, 'M' , tm))
}
'r' => {
parse_type(s, 'I' , tm)
.and_then(|()| parse_char(s, ':' ))
.and_then(|()| parse_type(s, 'M' , tm))
.and_then(|()| parse_char(s, ':' ))
.and_then(|()| parse_type(s, 'S' , tm))
.and_then(|()| parse_char(s, ' ' ))
.and_then(|()| parse_type(s, 'p' , tm))
}
's' => {
match match_digits_i64(s, 1 , 18 , false ) {
Some(v) => {
*tm = at_utc(Timespec::new(v, 0 ));
Ok(())
},
None => Err(ParseError::InvalidSecondsSinceEpoch)
}
}
'S' => {
match match_digits_in_range(s, 1 , 2 , false , 0 , 60 ) {
Some(v) => { tm.tm_sec = v; Ok(()) }
None => Err(ParseError::InvalidSecond)
}
}
//'s' {}
'T' | 'X' => {
parse_type(s, 'H' , tm)
.and_then(|()| parse_char(s, ':' ))
.and_then(|()| parse_type(s, 'M' , tm))
.and_then(|()| parse_char(s, ':' ))
.and_then(|()| parse_type(s, 'S' , tm))
}
't' => parse_char(s, '\t' ),
'u' => {
match match_digits_in_range(s, 1 , 1 , false , 1 , 7 ) {
Some(v) => { tm.tm_wday = if v == 7 { 0 } else { v }; Ok(()) }
None => Err(ParseError::InvalidDayOfWeek)
}
}
'v' => {
parse_type(s, 'e' , tm)
.and_then(|()| parse_char(s, '-' ))
.and_then(|()| parse_type(s, 'b' , tm))
.and_then(|()| parse_char(s, '-' ))
.and_then(|()| parse_type(s, 'Y' , tm))
}
//'W' {}
'w' => {
match match_digits_in_range(s, 1 , 1 , false , 0 , 6 ) {
Some(v) => { tm.tm_wday = v; Ok(()) }
None => Err(ParseError::InvalidDayOfWeek)
}
}
'Y' => {
match match_digits(s, 4 , 4 , false ) {
Some(v) => { tm.tm_year = v - 1900 ; Ok(()) }
None => Err(ParseError::InvalidYear)
}
}
'y' => {
match match_digits_in_range(s, 1 , 2 , false , 0 , 99 ) {
Some(v) => { tm.tm_year = v; Ok(()) }
None => Err(ParseError::InvalidYear)
}
}
'Z' => {
if match_str(s, "UTC" ) || match_str(s, "GMT" ) {
tm.tm_utcoff = 0 ;
Ok(())
} else {
// It's odd, but to maintain compatibility with c's
// strptime we ignore the timezone.
for (i, ch) in s.char_indices() {
if ch == ' ' {
*s = &s[i..];
return Ok(())
}
}
*s = "" ;
Ok(())
}
}
'z' => {
if parse_char(s, 'Z' ).is_ok() {
tm.tm_utcoff = 0 ;
Ok(())
} else {
let sign = if parse_char(s, '+' ).is_ok() {1 }
else if parse_char(s, '-' ).is_ok() {-1 }
else { return Err(ParseError::InvalidZoneOffset) };
let hours;
let minutes;
match match_digits(s, 2 , 2 , false ) {
Some(h) => hours = h,
None => return Err(ParseError::InvalidZoneOffset)
}
// consume the colon if its present,
// just ignore it otherwise
let _ = parse_char(s, ':' );
match match_digits(s, 2 , 2 , false ) {
Some(m) => minutes = m,
None => return Err(ParseError::InvalidZoneOffset)
}
tm.tm_utcoff = sign * (hours * 60 * 60 + minutes * 60 );
Ok(())
}
}
'%' => parse_char(s, '%' ),
ch => Err(ParseError::InvalidFormatSpecifier(ch))
}
}
fn match_str(s: &mut &str, needle: &str) -> bool {
if s.starts_with(needle) {
*s = &s[needle.len()..];
true
} else {
false
}
}
fn match_strs(ss: &mut &str, strs: &[(&str, i32)]) -> Option<i32> {
for &(needle, value) in strs.iter() {
if match_str(ss, needle) {
return Some(value)
}
}
None
}
fn match_digits(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i32> {
match match_digits_i64(ss, min_digits, max_digits, ws) {
Some(v) => Some(v as i32),
None => None
}
}
fn match_digits_i64(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i64> {
let mut value : i64 = 0 ;
let mut n = 0 ;
if ws {
#[ allow(deprecated)] // use `trim_start_matches` starting in 1.30
let s2 = ss.trim_left_matches(" " );
n = ss.len() - s2.len();
if n > max_digits { return None }
}
let chars = ss[n..].char_indices();
for (_, ch) in chars.take(max_digits - n) {
match ch {
'0' ... '9' => value = value * 10 + (ch as i64 - '0' as i64),
_ => break ,
}
n += 1 ;
}
if n >= min_digits && n <= max_digits {
*ss = &ss[n..];
Some(value)
} else {
None
}
}
fn match_fractional_seconds(ss: &mut &str) -> i32 {
let mut value = 0 ;
let mut multiplier = NSEC_PER_SEC / 10 ;
let mut chars = ss.char_indices();
let orig = *ss;
for (i, ch) in &mut chars {
*ss = &orig[i..];
match ch {
'0' ... '9' => {
// This will drop digits after the nanoseconds place
let digit = ch as i32 - '0' as i32;
value += digit * multiplier;
multiplier /= 10 ;
}
_ => break
}
}
value
}
fn match_digits_in_range(ss: &mut &str,
min_digits : usize, max_digits : usize,
ws: bool, min: i32, max: i32) -> Option<i32> {
let before = *ss;
match match_digits(ss, min_digits, max_digits, ws) {
Some(val) if val >= min && val <= max => Some(val),
_ => { *ss = before; None }
}
}
fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> {
match s.char_indices().next() {
Some((i, c2)) => {
if c == c2 {
*s = &s[i + c2.len_utf8()..];
Ok(())
} else {
Err(ParseError::UnexpectedCharacter(c, c2))
}
}
None => Err(ParseError::InvalidTime),
}
}
Messung V0.5 in Prozent C=98 H=97 G=97
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-19)
¤
*© Formatika GbR, Deutschland