Skip to content

Commit 1c09377

Browse files
TheElectronWilldjc
authored andcommitted
feat: Accept "+00:00" as an alternative way to specify the UTC timezone
1 parent 9383ae2 commit 1c09377

File tree

1 file changed

+67
-6
lines changed

1 file changed

+67
-6
lines changed

src/date.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,17 @@ fn two_digits(b1: u8, b2: u8) -> Result<u64, Error> {
7676

7777
/// Parse RFC3339 timestamp `2018-02-14T00:28:07Z`
7878
///
79-
/// Supported feature: any precision of fractional
80-
/// digits `2018-02-14T00:28:07.133Z`.
79+
/// Supported features:
80+
/// - Any precision of fractional digits `2018-02-14T00:28:07.133Z`.
81+
/// - The UTC timezone can be indicated with `Z` or `+00:00`.
8182
///
8283
/// Unsupported feature: localized timestamps. Only UTC is supported.
8384
pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> {
8485
if s.len() < "2018-02-14T00:28:07Z".len() {
8586
return Err(Error::InvalidFormat);
8687
}
8788
let b = s.as_bytes();
88-
if b[10] != b'T' || b.last() != Some(&b'Z') {
89+
if b[10] != b'T' || (b.last() != Some(&b'Z') && !s.ends_with("+00:00")) {
8990
return Err(Error::InvalidFormat);
9091
}
9192
parse_rfc3339_weak(s)
@@ -96,7 +97,7 @@ pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> {
9697
/// Supported features:
9798
///
9899
/// 1. Any precision of fractional digits `2018-02-14 00:28:07.133`.
99-
/// 2. Supports timestamp with or without either of `T` or `Z`
100+
/// 2. Supports timestamp with or without either of `T`, `Z` or `+00:00`.
100101
/// 3. Anything valid for [`parse_rfc3339`](parse_rfc3339) is valid for this function
101102
///
102103
/// Unsupported feature: localized timestamps. Only UTC is supported, even if
@@ -171,14 +172,19 @@ pub fn parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error> {
171172
if idx == b.len() - 1 {
172173
break;
173174
}
174-
175+
return Err(Error::InvalidDigit);
176+
} else if b[idx] == b'+' {
177+
// start of "+00:00", which must be at the end
178+
if idx == b.len() - 6 {
179+
break;
180+
}
175181
return Err(Error::InvalidDigit);
176182
}
177183

178184
nanos += mult * (b[idx] as char).to_digit(10).ok_or(Error::InvalidDigit)?;
179185
mult /= 10;
180186
}
181-
} else if b.len() != 19 && (b.len() > 20 || b[19] != b'Z') {
187+
} else if b.len() != 19 && (b.len() > 25 || (b[19] != b'Z' && (&b[19..] != b"+00:00"))) {
182188
return Err(Error::InvalidFormat);
183189
}
184190

@@ -679,4 +685,59 @@ mod test {
679685
);
680686
parse_rfc3339("1970-01-01 00:00:00Z").unwrap_err();
681687
}
688+
689+
#[test]
690+
fn parse_offset_00() {
691+
assert_eq!(
692+
parse_rfc3339("1970-01-01T00:00:00+00:00").unwrap(),
693+
UNIX_EPOCH + Duration::new(0, 0)
694+
);
695+
assert_eq!(
696+
parse_rfc3339("1970-01-01T00:00:01+00:00").unwrap(),
697+
UNIX_EPOCH + Duration::new(1, 0)
698+
);
699+
assert_eq!(
700+
parse_rfc3339("2018-02-13T23:08:32+00:00").unwrap(),
701+
UNIX_EPOCH + Duration::new(1_518_563_312, 0)
702+
);
703+
assert_eq!(
704+
parse_rfc3339("2012-01-01T00:00:00+00:00").unwrap(),
705+
UNIX_EPOCH + Duration::new(1_325_376_000, 0)
706+
);
707+
708+
// invalid
709+
assert_eq!(
710+
parse_rfc3339("2012-01-01T00:00:00 +00:00"),
711+
Err(super::Error::InvalidFormat)
712+
);
713+
assert_eq!(
714+
parse_rfc3339("2012-01-01T00:00:00+00"),
715+
Err(super::Error::InvalidFormat)
716+
);
717+
}
718+
719+
#[test]
720+
fn weak_parse_offset_00() {
721+
assert_eq!(
722+
parse_rfc3339_weak("1970-01-01 00:00:00+00:00").unwrap(),
723+
UNIX_EPOCH + Duration::new(0, 0)
724+
);
725+
assert_eq!(
726+
parse_rfc3339_weak("1970-01-01 00:00:00.000123+00:00").unwrap(),
727+
UNIX_EPOCH + Duration::new(0, 123_000)
728+
);
729+
assert_eq!(
730+
parse_rfc3339_weak("1970-01-01T00:00:00.000123+00:00").unwrap(),
731+
UNIX_EPOCH + Duration::new(0, 123_000)
732+
);
733+
734+
// invalid
735+
parse_rfc3339("2012-01-01T+00:00:00").unwrap_err();
736+
parse_rfc3339("1970-01-01 00:00:00.00+0123").unwrap_err();
737+
parse_rfc3339("1970-01-01 00:00:00.0000123+00:00").unwrap_err();
738+
parse_rfc3339("1970-01-01 00:00:00.0000123+00:00abcd").unwrap_err();
739+
parse_rfc3339("1970-01-01 00:00:00.0000123+02:00").unwrap_err();
740+
parse_rfc3339("1970-01-01 00:00:00.0000123+00").unwrap_err();
741+
parse_rfc3339("1970-01-01 00:00:00.0000123+").unwrap_err();
742+
}
682743
}

0 commit comments

Comments
 (0)