From 72eae556ae9253c0fa718e12e174d3ee28d556ee Mon Sep 17 00:00:00 2001
From: John DiSanti <jdisanti@amazon.com>
Date: Thu, 11 Nov 2021 16:01:30 -0800
Subject: [PATCH] Relegate `chrono` to an optional feature in a new conversion
 crate (#849)

* Refactor Instant to use `time` instead of `chrono`
* Rename methods on Instant to have a consistent naming scheme.
* Remove built-in Instant conversions.
* Remove `chrono` from `aws-sigv4`
* Re-export `Instant` from service crates
* Implement `aws-smithy-types-convert`
* Rename `Instant` to `DateTime`
* Make date-time formatting operations fallible
* Add initial changelog entries
* Update changelog
* Make DateTime to SystemTime conversion fallible
* Incorporate review feedback
* Fix merge issues
* Fix examples
* Fix doc comments
* Fix unused import warning when using `convert-chrono` feature exclusively
---
 CHANGELOG.md                                  |  53 ++
 aws/SDK_CHANGELOG.md                          |  50 ++
 .../aws-config/src/json_credentials.rs        |  26 +-
 aws/rust-runtime/aws-config/src/sts.rs        |  18 +-
 .../aws-sig-auth/src/event_stream.rs          |   2 +-
 aws/rust-runtime/aws-sig-auth/src/signer.rs   |   2 +-
 aws/rust-runtime/aws-sigv4/Cargo.toml         |   3 +-
 aws/rust-runtime/aws-sigv4/src/date_fmt.rs    |  54 --
 aws/rust-runtime/aws-sigv4/src/date_time.rs   | 144 +++++
 .../aws-sigv4/src/event_stream.rs             |  43 +-
 .../src/http_request/canonical_request.rs     |  78 +--
 .../aws-sigv4/src/http_request/mod.rs         |   4 +-
 .../aws-sigv4/src/http_request/sign.rs        |  18 +-
 aws/rust-runtime/aws-sigv4/src/lib.rs         |  30 +-
 aws/rust-runtime/aws-sigv4/src/sign.rs        |  14 +-
 aws/sdk/build.gradle.kts                      |   1 +
 aws/sdk/examples/apigateway/Cargo.toml        |   1 +
 .../apigateway/src/bin/get_rest_apis.rs       |   6 +-
 aws/sdk/examples/cognitoidentity/Cargo.toml   |   3 +-
 .../src/bin/describe-identity-pool.rs         |  28 +-
 .../src/bin/list-identity-pools.rs            |  10 +-
 .../src/bin/list-pool-identities.rs           |  15 +-
 .../cognitoidentityprovider/Cargo.toml        |   1 +
 .../src/bin/list-user-pools.rs                |  17 +-
 aws/sdk/examples/cognitosync/Cargo.toml       |   1 +
 .../src/bin/list-identity-pool-usage.rs       |  13 +-
 aws/sdk/examples/sagemaker/Cargo.toml         |   3 +-
 .../sagemaker/src/bin/list-training-jobs.rs   |  14 +-
 .../sagemaker/src/bin/sagemaker-helloworld.rs |  11 +-
 .../protocols/ServerHttpProtocolGenerator.kt  |  14 +-
 .../smithy/rust/codegen/rustlang/RustTypes.kt |   6 +-
 .../rust/codegen/smithy/RuntimeTypes.kt       |   6 +-
 .../rust/codegen/smithy/SymbolVisitor.kt      |   2 +-
 .../SmithyTypesPubUseGenerator.kt             |   2 +
 .../codegen/smithy/generators/Instantiator.kt |   4 +-
 .../http/RequestBindingGenerator.kt           |  14 +-
 .../http/ResponseBindingGenerator.kt          |   4 +-
 .../parse/XmlBindingTraitParserGenerator.kt   |   2 +-
 .../serialize/JsonSerializerGenerator.kt      |   2 +-
 .../serialize/QuerySerializerGenerator.kt     |   2 +-
 .../XmlBindingTraitSerializerGenerator.kt     |   2 +-
 .../smithy/rust/codegen/SymbolBuilderTest.kt  |   4 +-
 .../generators/StructureGeneratorTest.kt      |   2 +-
 .../http/RequestBindingGeneratorTest.kt       |  23 +-
 .../EventStreamUnmarshallerGeneratorTest.kt   |  40 +-
 .../EventStreamMarshallerGeneratorTest.kt     |   6 +-
 design/src/smithy/simple_shapes.md            |   8 +-
 rust-runtime/Cargo.toml                       |   1 +
 .../fuzz/fuzz_targets/mutated_headers.rs      |   4 +-
 .../aws-smithy-eventstream/src/error.rs       |   4 +-
 .../aws-smithy-eventstream/src/frame.rs       |  20 +-
 .../aws-smithy-eventstream/src/smithy.rs      |   4 +-
 rust-runtime/aws-smithy-http/src/header.rs    |  10 +-
 rust-runtime/aws-smithy-http/src/label.rs     |   7 +-
 rust-runtime/aws-smithy-http/src/operation.rs |  27 +-
 rust-runtime/aws-smithy-http/src/query.rs     |  14 +-
 .../aws-smithy-json/src/deserialize/token.rs  |  18 +-
 rust-runtime/aws-smithy-json/src/serialize.rs |  73 ++-
 rust-runtime/aws-smithy-query/src/lib.rs      |  44 +-
 .../aws-smithy-types-convert/Cargo.toml       |  18 +
 rust-runtime/aws-smithy-types-convert/LICENSE | 175 ++++++
 .../aws-smithy-types-convert/src/date_time.rs | 237 ++++++++
 .../aws-smithy-types-convert/src/lib.rs       |  17 +
 rust-runtime/aws-smithy-types/Cargo.toml      |   7 +-
 rust-runtime/aws-smithy-types/fuzz/Cargo.toml |  12 +
 .../fuzz/fuzz_targets/parse_date_time.rs      |   4 +-
 .../fuzz/fuzz_targets/parse_epoch_seconds.rs  |   4 +-
 .../fuzz/fuzz_targets/parse_http_date.rs      |   4 +-
 .../fuzz/fuzz_targets/read_date_time.rs       |  18 +
 .../fuzz/fuzz_targets/read_http_date.rs       |  18 +
 .../proptest-regressions/instant/format.txt   |   7 +
 .../src/{instant => date_time}/format.rs      | 425 +++++++++-----
 .../aws-smithy-types/src/date_time/mod.rs     | 546 ++++++++++++++++++
 .../aws-smithy-types/src/instant/mod.rs       | 428 --------------
 rust-runtime/aws-smithy-types/src/lib.rs      |   4 +-
 75 files changed, 1955 insertions(+), 1001 deletions(-)
 delete mode 100644 aws/rust-runtime/aws-sigv4/src/date_fmt.rs
 create mode 100644 aws/rust-runtime/aws-sigv4/src/date_time.rs
 create mode 100644 rust-runtime/aws-smithy-types-convert/Cargo.toml
 create mode 100644 rust-runtime/aws-smithy-types-convert/LICENSE
 create mode 100644 rust-runtime/aws-smithy-types-convert/src/date_time.rs
 create mode 100644 rust-runtime/aws-smithy-types-convert/src/lib.rs
 create mode 100644 rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_date_time.rs
 create mode 100644 rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_http_date.rs
 create mode 100644 rust-runtime/aws-smithy-types/proptest-regressions/instant/format.txt
 rename rust-runtime/aws-smithy-types/src/{instant => date_time}/format.rs (54%)
 create mode 100644 rust-runtime/aws-smithy-types/src/date_time/mod.rs
 delete mode 100644 rust-runtime/aws-smithy-types/src/instant/mod.rs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b70827881..ec4d3a85a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,60 @@
 vNext (Month Day, Year)
 =======================
+
+**TODO Next Release:**
 - Update README & aws-sdk-rust CI for MSRV upgrade to 1.54
 
+**Breaking Changes**
+
+Several breaking changes around `aws_smithy_types::Instant` were introduced by smithy-rs#849:
+- `aws_smithy_types::Instant` from was renamed to `DateTime` to avoid confusion with the standard library's monotonically nondecreasing `Instant` type.
+- `DateParseError` in `aws_smithy_types` has been renamed to `DateTimeParseError` to match the type that's being parsed.
+- The `chrono-conversions` feature and associated functions have been moved to the `aws-smithy-types-convert` crate.
+  - Calls to `Instant::from_chrono` should be changed to:
+    ```rust
+    use aws_smithy_types::DateTime;
+    use aws_smithy_types_convert::date_time::DateTimeExt;
+
+    // For chrono::DateTime<Utc>
+    let date_time = DateTime::from_chrono_utc(chrono_date_time);
+    // For chrono::DateTime<FixedOffset>
+    let date_time = DateTime::from_chrono_offset(chrono_date_time);
+    ```
+  - Calls to `instant.to_chrono()` should be changed to:
+    ```rust
+    use aws_smithy_types_convert::date_time::DateTimeExt;
+
+    date_time.to_chrono_utc();
+    ```
+- `Instant::from_system_time` and `Instant::to_system_time` have been changed to `From` trait implementations.
+  - Calls to `from_system_time` should be changed to:
+    ```rust
+    DateTime::from(system_time);
+    // or
+    let date_time: DateTime = system_time.into();
+    ```
+  - Calls to `to_system_time` should be changed to:
+    ```rust
+    SystemTime::from(date_time);
+    // or
+    let system_time: SystemTime = date_time.into();
+    ```
+- Several functions in `Instant`/`DateTime` were renamed:
+  - `Instant::from_f64` -> `DateTime::from_secs_f64`
+  - `Instant::from_fractional_seconds` -> `DateTime::from_fractional_secs`
+  - `Instant::from_epoch_seconds` -> `DateTime::from_secs`
+  - `Instant::from_epoch_millis` -> `DateTime::from_millis`
+  - `Instant::epoch_fractional_seconds` -> `DateTime::as_secs_f64`
+  - `Instant::has_nanos` -> `DateTime::has_subsec_nanos`
+  - `Instant::epoch_seconds` -> `DateTime::secs`
+  - `Instant::epoch_subsecond_nanos` -> `DateTime::subsec_nanos`
+  - `Instant::to_epoch_millis` -> `DateTime::to_millis`
+- The `DateTime::fmt` method is now fallible and fails when a `DateTime`'s value is outside what can be represented by the desired date format.
+- In `aws-sigv4`, the `SigningParams` builder's `date_time` setter was renamed to `time` and changed to take a `std::time::SystemTime` instead of a chrono's `DateTime<Utc>`.
+
+**New this week**
+- Conversions from `aws_smithy_types::DateTime` to `OffsetDateTime` from the `time` crate are now available from the `aws-smithy-types-convert` crate. (smithy-rs#849)
+
 v0.28.0-alpha (November 11th, 2021)
 ===================================
 
diff --git a/aws/SDK_CHANGELOG.md b/aws/SDK_CHANGELOG.md
index 29ae4b2f8..d45ba06ea 100644
--- a/aws/SDK_CHANGELOG.md
+++ b/aws/SDK_CHANGELOG.md
@@ -2,6 +2,56 @@ vNext (Month Day, Year)
 =======================
 - Update README & aws-sdk-rust CI for MSRV upgrade to 1.54
 
+**Breaking Changes**
+
+Several breaking changes around `aws_smithy_types::Instant` were introduced by smithy-rs#849:
+- `aws_smithy_types::Instant` from was renamed to `DateTime` to avoid confusion with the standard library's monotonically nondecreasing `Instant` type.
+- `DateParseError` in `aws_smithy_types` has been renamed to `DateTimeParseError` to match the type that's being parsed.
+- The `chrono-conversions` feature and associated functions have been moved to the `aws-smithy-types-convert` crate.
+  - Calls to `Instant::from_chrono` should be changed to:
+    ```rust
+    use aws_smithy_types::DateTime;
+    use aws_smithy_types_convert::date_time::DateTimeExt;
+
+    // For chrono::DateTime<Utc>
+    let date_time = DateTime::from_chrono_utc(chrono_date_time);
+    // For chrono::DateTime<FixedOffset>
+    let date_time = DateTime::from_chrono_offset(chrono_date_time);
+    ```
+  - Calls to `instant.to_chrono()` should be changed to:
+    ```rust
+    use aws_smithy_types_convert::date_time::DateTimeExt;
+
+    date_time.to_chrono_utc();
+    ```
+- `Instant::from_system_time` and `Instant::to_system_time` have been changed to `From` trait implementations.
+  - Calls to `from_system_time` should be changed to:
+    ```rust
+    DateTime::from(system_time);
+    // or
+    let date_time: DateTime = system_time.into();
+    ```
+  - Calls to `to_system_time` should be changed to:
+    ```rust
+    SystemTime::from(date_time);
+    // or
+    let system_time: SystemTime = date_time.into();
+    ```
+- Several functions in `Instant`/`DateTime` were renamed:
+  - `Instant::from_f64` -> `DateTime::from_secs_f64`
+  - `Instant::from_fractional_seconds` -> `DateTime::from_fractional_secs`
+  - `Instant::from_epoch_seconds` -> `DateTime::from_secs`
+  - `Instant::from_epoch_millis` -> `DateTime::from_millis`
+  - `Instant::epoch_fractional_seconds` -> `DateTime::as_secs_f64`
+  - `Instant::has_nanos` -> `DateTime::has_subsec_nanos`
+  - `Instant::epoch_seconds` -> `DateTime::secs`
+  - `Instant::epoch_subsecond_nanos` -> `DateTime::subsec_nanos`
+  - `Instant::to_epoch_millis` -> `DateTime::to_millis`
+- The `DateTime::fmt` method is now fallible and fails when a `DateTime`'s value is outside what can be represented by the desired date format.
+
+**New this week**
+- Conversions from `aws_smithy_types::DateTime` to `OffsetDateTime` from the `time` crate are now available from the `aws-smithy-types-convert` crate. (smithy-rs#849)
+
 v0.0.25-alpha (November 11th, 2021)
 ===================================
 
diff --git a/aws/rust-runtime/aws-config/src/json_credentials.rs b/aws/rust-runtime/aws-config/src/json_credentials.rs
index 7008f1cff..5d123d968 100644
--- a/aws/rust-runtime/aws-config/src/json_credentials.rs
+++ b/aws/rust-runtime/aws-config/src/json_credentials.rs
@@ -1,8 +1,14 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
 use aws_smithy_json::deserialize::token::skip_value;
 use aws_smithy_json::deserialize::{json_token_iter, EscapeError, Token};
-use aws_smithy_types::instant::Format;
-use aws_smithy_types::Instant;
+use aws_smithy_types::date_time::Format;
+use aws_smithy_types::DateTime;
 use std::borrow::Cow;
+use std::convert::TryFrom;
 use std::error::Error;
 use std::fmt::{Display, Formatter};
 use std::time::SystemTime;
@@ -167,14 +173,16 @@ pub(crate) fn parse_json_credentials(
                 session_token.ok_or(InvalidJsonCredentials::MissingField("Token"))?;
             let expiration =
                 expiration.ok_or(InvalidJsonCredentials::MissingField("Expiration"))?;
-            let expiration = Instant::from_str(expiration.as_ref(), Format::DateTime)
-                .map_err(|err| {
+            let expiration = SystemTime::try_from(
+                DateTime::from_str(expiration.as_ref(), Format::DateTime).map_err(|err| {
                     InvalidJsonCredentials::Other(format!("invalid date: {}", err).into())
-                })?
-                .to_system_time()
-                .ok_or_else(|| {
-                    InvalidJsonCredentials::Other("invalid expiration (prior to unix epoch)".into())
-                })?;
+                })?,
+            )
+            .map_err(|_| {
+                InvalidJsonCredentials::Other(
+                    "credential expiration time cannot be represented by a SystemTime".into(),
+                )
+            })?;
             Ok(JsonCredentials::RefreshableCredentials {
                 access_key_id,
                 secret_access_key,
diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs
index 1c527c019..9055681a6 100644
--- a/aws/rust-runtime/aws-config/src/sts.rs
+++ b/aws/rust-runtime/aws-config/src/sts.rs
@@ -12,6 +12,7 @@ pub(crate) mod util {
     use aws_sdk_sts::model::Credentials as StsCredentials;
     use aws_types::credentials::{self, CredentialsError};
     use aws_types::Credentials as AwsCredentials;
+    use std::convert::TryFrom;
     use std::time::{SystemTime, UNIX_EPOCH};
 
     /// Convert STS credentials to aws_auth::Credentials
@@ -21,14 +22,15 @@ pub(crate) mod util {
     ) -> credentials::Result {
         let sts_credentials = sts_credentials
             .ok_or_else(|| CredentialsError::unhandled("STS credentials must be defined"))?;
-        let expiration = sts_credentials
-            .expiration
-            .ok_or_else(|| CredentialsError::unhandled("missing expiration"))?;
-        let expiration = expiration.to_system_time().ok_or_else(|| {
-            CredentialsError::unhandled(format!(
-                "expiration is before unix epoch: {:?}",
-                &expiration
-            ))
+        let expiration = SystemTime::try_from(
+            sts_credentials
+                .expiration
+                .ok_or_else(|| CredentialsError::unhandled("missing expiration"))?,
+        )
+        .map_err(|_| {
+            CredentialsError::unhandled(
+                "credential expiration time cannot be represented by a SystemTime",
+            )
         })?;
         Ok(AwsCredentials::new(
             sts_credentials
diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs
index c5401853e..dd859d4cb 100644
--- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs
+++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs
@@ -43,7 +43,7 @@ impl SigV4Signer {
             .secret_key(credentials.secret_access_key())
             .region(region.as_ref())
             .service_name(signing_service.as_ref())
-            .date_time(time.into())
+            .time(time)
             .settings(());
         builder.set_security_token(credentials.session_token());
         builder.build().unwrap()
diff --git a/aws/rust-runtime/aws-sig-auth/src/signer.rs b/aws/rust-runtime/aws-sig-auth/src/signer.rs
index e88419bcd..4c91af3ca 100644
--- a/aws/rust-runtime/aws-sig-auth/src/signer.rs
+++ b/aws/rust-runtime/aws-sig-auth/src/signer.rs
@@ -167,7 +167,7 @@ impl SigV4Signer {
             .secret_key(credentials.secret_access_key())
             .region(request_config.region.as_ref())
             .service_name(request_config.service.as_ref())
-            .date_time(request_config.request_ts.into())
+            .time(request_config.request_ts)
             .settings(settings);
         builder.set_security_token(credentials.session_token());
         builder.build().expect("all required fields set")
diff --git a/aws/rust-runtime/aws-sigv4/Cargo.toml b/aws/rust-runtime/aws-sigv4/Cargo.toml
index d8bd59746..9dc2afbf1 100644
--- a/aws/rust-runtime/aws-sigv4/Cargo.toml
+++ b/aws/rust-runtime/aws-sigv4/Cargo.toml
@@ -16,7 +16,6 @@ default = ["sign-http"]
 [dependencies]
 aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true }
 bytes = { version = "1", optional = true }
-chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
 form_urlencoded = { version = "1.0", optional = true }
 hex = "0.4"
 http = { version = "0.2", optional = true }
@@ -24,6 +23,7 @@ once_cell = "1.8"
 percent-encoding = { version = "2.1", optional = true }
 regex = "1.5"
 ring = "0.16"
+time = "0.3.4"
 tracing = "0.1"
 
 [dev-dependencies]
@@ -31,3 +31,4 @@ bytes = "1"
 httparse = "1.5"
 pretty_assertions = "1.0"
 proptest = "1"
+time = { version = "0.3.4", features = ["parsing"] }
diff --git a/aws/rust-runtime/aws-sigv4/src/date_fmt.rs b/aws/rust-runtime/aws-sigv4/src/date_fmt.rs
deleted file mode 100644
index a714fca20..000000000
--- a/aws/rust-runtime/aws-sigv4/src/date_fmt.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
-
-// Some of the functions in this file are unused when disabling certain features
-#![allow(dead_code)]
-use chrono::{Date, DateTime, NaiveDate, NaiveDateTime, ParseError, Utc};
-
-const DATE_TIME_FORMAT: &str = "%Y%m%dT%H%M%SZ";
-const DATE_FORMAT: &str = "%Y%m%d";
-
-/// Formats a chrono `Date<Utc>` in `YYYYMMDD` format.
-pub(crate) fn format_date(date: &Date<Utc>) -> String {
-    date.format(DATE_FORMAT).to_string()
-}
-
-/// Parses `YYYYMMDD` formatted dates into a chrono `Date<Utc>`.
-pub(crate) fn parse_date(date_str: &str) -> Result<Date<Utc>, ParseError> {
-    Ok(Date::<Utc>::from_utc(
-        NaiveDate::parse_from_str(date_str, "%Y%m%d")?,
-        Utc,
-    ))
-}
-
-/// Formats a chrono `DateTime<Utc>` in `YYYYMMDD'T'HHMMSS'Z'` format.
-pub(crate) fn format_date_time(date_time: &DateTime<Utc>) -> String {
-    date_time.format(DATE_TIME_FORMAT).to_string()
-}
-
-/// Parses `YYYYMMDD'T'HHMMSS'Z'` formatted dates into a chrono `DateTime<Utc>`.
-pub(crate) fn parse_date_time(date_time_str: &str) -> Result<DateTime<Utc>, ParseError> {
-    Ok(DateTime::<Utc>::from_utc(
-        NaiveDateTime::parse_from_str(date_time_str, DATE_TIME_FORMAT)?,
-        Utc,
-    ))
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn date_time_roundtrip() {
-        let date = parse_date_time("20150830T123600Z").unwrap();
-        assert_eq!("20150830T123600Z", format_date_time(&date));
-    }
-
-    #[test]
-    fn date_roundtrip() {
-        let date = parse_date("20150830").unwrap();
-        assert_eq!("20150830", format_date(&date));
-    }
-}
diff --git a/aws/rust-runtime/aws-sigv4/src/date_time.rs b/aws/rust-runtime/aws-sigv4/src/date_time.rs
new file mode 100644
index 000000000..bf05278ef
--- /dev/null
+++ b/aws/rust-runtime/aws-sigv4/src/date_time.rs
@@ -0,0 +1,144 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+// Some of the functions in this file are unused when disabling certain features
+#![allow(dead_code)]
+
+use std::time::SystemTime;
+use time::{OffsetDateTime, Time};
+
+/// Truncates the subseconds from the given `SystemTime` to zero.
+pub(crate) fn truncate_subsecs(time: SystemTime) -> SystemTime {
+    let date_time = OffsetDateTime::from(time);
+    let time = date_time.time();
+    date_time
+        .replace_time(
+            Time::from_hms(time.hour(), time.minute(), time.second()).expect("was already a time"),
+        )
+        .into()
+}
+
+/// Formats a `SystemTime` in `YYYYMMDD` format.
+pub(crate) fn format_date(time: SystemTime) -> String {
+    let time = OffsetDateTime::from(time);
+    format!(
+        "{:04}{:02}{:02}",
+        time.year(),
+        u8::from(time.month()),
+        time.day()
+    )
+}
+
+/// Formats a `SystemTime` in `YYYYMMDD'T'HHMMSS'Z'` format.
+pub(crate) fn format_date_time(time: SystemTime) -> String {
+    let time = OffsetDateTime::from(time);
+    format!(
+        "{:04}{:02}{:02}T{:02}{:02}{:02}Z",
+        time.year(),
+        u8::from(time.month()),
+        time.day(),
+        time.hour(),
+        time.minute(),
+        time.second()
+    )
+}
+
+/// Parse functions that are only needed for unit tests.
+#[cfg(test)]
+pub(crate) mod test_parsers {
+    use std::{borrow::Cow, error::Error, fmt, time::SystemTime};
+    use time::format_description;
+    use time::{Date, PrimitiveDateTime, Time};
+
+    const DATE_TIME_FORMAT: &str = "[year][month][day]T[hour][minute][second]Z";
+    const DATE_FORMAT: &str = "[year][month][day]";
+
+    /// Parses `YYYYMMDD'T'HHMMSS'Z'` formatted dates into a `SystemTime`.
+    pub(crate) fn parse_date_time(date_time_str: &str) -> Result<SystemTime, ParseError> {
+        let date_time = PrimitiveDateTime::parse(
+            date_time_str,
+            &format_description::parse(DATE_TIME_FORMAT).unwrap(),
+        )
+        .map_err(|err| ParseError(err.to_string().into()))?
+        .assume_utc();
+        Ok(date_time.into())
+    }
+
+    /// Parses `YYYYMMDD` formatted dates into a `SystemTime`.
+    pub(crate) fn parse_date(date_str: &str) -> Result<SystemTime, ParseError> {
+        let date_time = PrimitiveDateTime::new(
+            Date::parse(date_str, &format_description::parse(DATE_FORMAT).unwrap())
+                .map_err(|err| ParseError(err.to_string().into()))?,
+            Time::from_hms(0, 0, 0).unwrap(),
+        )
+        .assume_utc();
+        Ok(date_time.into())
+    }
+
+    #[derive(Debug)]
+    pub(crate) struct ParseError(Cow<'static, str>);
+
+    impl fmt::Display for ParseError {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            write!(f, "failed to parse time: {}", self.0)
+        }
+    }
+
+    impl Error for ParseError {}
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::date_time::test_parsers::{parse_date, parse_date_time};
+    use time::format_description::well_known::Rfc3339;
+
+    #[test]
+    fn date_format() {
+        let time: SystemTime = OffsetDateTime::parse("2039-02-04T23:01:09.104Z", &Rfc3339)
+            .unwrap()
+            .into();
+        assert_eq!("20390204", format_date(time));
+        let time: SystemTime = OffsetDateTime::parse("0100-01-02T00:00:00.000Z", &Rfc3339)
+            .unwrap()
+            .into();
+        assert_eq!("01000102", format_date(time));
+    }
+
+    #[test]
+    fn date_time_format() {
+        let time: SystemTime = OffsetDateTime::parse("2039-02-04T23:01:09.104Z", &Rfc3339)
+            .unwrap()
+            .into();
+        assert_eq!("20390204T230109Z", format_date_time(time));
+        let time: SystemTime = OffsetDateTime::parse("0100-01-02T00:00:00.000Z", &Rfc3339)
+            .unwrap()
+            .into();
+        assert_eq!("01000102T000000Z", format_date_time(time));
+    }
+
+    #[test]
+    fn date_time_roundtrip() {
+        let time = parse_date_time("20150830T123600Z").unwrap();
+        assert_eq!("20150830T123600Z", format_date_time(time));
+    }
+
+    #[test]
+    fn date_roundtrip() {
+        let time = parse_date("20150830").unwrap();
+        assert_eq!("20150830", format_date(time));
+    }
+
+    #[test]
+    fn test_truncate_subsecs() {
+        let time: SystemTime = OffsetDateTime::parse("2039-02-04T23:01:09.104Z", &Rfc3339)
+            .unwrap()
+            .into();
+        let expected: SystemTime = OffsetDateTime::parse("2039-02-04T23:01:09.000Z", &Rfc3339)
+            .unwrap()
+            .into();
+        assert_eq!(expected, truncate_subsecs(time));
+    }
+}
diff --git a/aws/rust-runtime/aws-sigv4/src/event_stream.rs b/aws/rust-runtime/aws-sigv4/src/event_stream.rs
index 938c1de51..48f1249d1 100644
--- a/aws/rust-runtime/aws-sigv4/src/event_stream.rs
+++ b/aws/rust-runtime/aws-sigv4/src/event_stream.rs
@@ -9,8 +9,8 @@
 //!
 //! ```rust
 //! use aws_sigv4::event_stream::{sign_message, SigningParams};
-//! use chrono::Utc;
 //! use aws_smithy_eventstream::frame::{Header, HeaderValue, Message};
+//! use std::time::SystemTime;
 //!
 //! // The `last_signature` argument is the previous message's signature, or
 //! // the signature of the initial HTTP request if a message hasn't been signed yet.
@@ -26,7 +26,7 @@
 //!     .secret_key("example secret key")
 //!     .region("us-east-1")
 //!     .service_name("exampleservice")
-//!     .date_time(Utc::now())
+//!     .time(SystemTime::now())
 //!     .settings(())
 //!     .build()
 //!     .unwrap();
@@ -36,13 +36,13 @@
 //!     sign_message(&message_to_sign, &last_signature, &params).into_parts();
 //! ```
 
-use crate::date_fmt::{format_date, format_date_time};
+use crate::date_time::{format_date, format_date_time, truncate_subsecs};
 use crate::sign::{calculate_signature, generate_signing_key, sha256_hex_string};
 use crate::SigningOutput;
 use aws_smithy_eventstream::frame::{write_headers_to, Header, HeaderValue, Message};
 use bytes::Bytes;
-use chrono::{DateTime, SubsecRound, Utc};
 use std::io::Write;
+use std::time::SystemTime;
 
 /// Event stream signing parameters
 pub type SigningParams<'a> = super::SigningParams<'a, ()>;
@@ -51,13 +51,13 @@ pub type SigningParams<'a> = super::SigningParams<'a, ()>;
 fn calculate_string_to_sign(
     message_payload: &[u8],
     last_signature: &str,
-    date_time: &DateTime<Utc>,
+    time: SystemTime,
     params: &SigningParams<'_>,
 ) -> Vec<u8> {
     // Event Stream string to sign format is documented here:
     // https://docs.aws.amazon.com/transcribe/latest/dg/how-streaming.html
-    let date_time_str = format_date_time(date_time);
-    let date_str = format_date(&date_time.date());
+    let date_time_str = format_date_time(time);
+    let date_str = format_date(time);
 
     let mut sts: Vec<u8> = Vec::new();
     writeln!(sts, "AWS4-HMAC-SHA256-PAYLOAD").unwrap();
@@ -70,7 +70,7 @@ fn calculate_string_to_sign(
     .unwrap();
     writeln!(sts, "{}", last_signature).unwrap();
 
-    let date_header = Header::new(":date", HeaderValue::Timestamp((*date_time).into()));
+    let date_header = Header::new(":date", HeaderValue::Timestamp(time.into()));
     let mut date_buffer = Vec::new();
     write_headers_to(&[date_header], &mut date_buffer).unwrap();
     writeln!(sts, "{}", sha256_hex_string(&date_buffer)).unwrap();
@@ -115,18 +115,14 @@ fn sign_payload<'a>(
 ) -> SigningOutput<Message> {
     // Truncate the sub-seconds up front since the timestamp written to the signed message header
     // needs to exactly match the string formatted timestamp, which doesn't include sub-seconds.
-    let date_time = params.date_time.trunc_subsecs(0);
+    let time = truncate_subsecs(params.time);
 
-    let signing_key = generate_signing_key(
-        params.secret_key,
-        date_time.date(),
-        params.region,
-        params.service_name,
-    );
+    let signing_key =
+        generate_signing_key(params.secret_key, time, params.region, params.service_name);
     let string_to_sign = calculate_string_to_sign(
         message_payload.as_ref().map(|v| &v[..]).unwrap_or(&[]),
         last_signature,
-        &date_time,
+        time,
         params,
     );
     let signature = calculate_signature(signing_key, &string_to_sign);
@@ -138,10 +134,7 @@ fn sign_payload<'a>(
                 ":chunk-signature",
                 HeaderValue::ByteArray(hex::decode(&signature).unwrap().into()),
             ))
-            .add_header(Header::new(
-                ":date",
-                HeaderValue::Timestamp(date_time.into()),
-            )),
+            .add_header(Header::new(":date", HeaderValue::Timestamp(time.into()))),
         signature,
     )
 }
@@ -166,7 +159,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "testservice",
-            date_time: (UNIX_EPOCH + Duration::new(123_456_789_u64, 1234u32)).into(),
+            time: (UNIX_EPOCH + Duration::new(123_456_789_u64, 1234u32)).into(),
             settings: (),
         };
 
@@ -185,7 +178,7 @@ mod tests {
             std::str::from_utf8(&calculate_string_to_sign(
                 &message_payload,
                 &last_signature,
-                &params.date_time,
+                params.time,
                 &params
             ))
             .unwrap()
@@ -204,7 +197,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "testservice",
-            date_time: (UNIX_EPOCH + Duration::new(123_456_789_u64, 1234u32)).into(),
+            time: (UNIX_EPOCH + Duration::new(123_456_789_u64, 1234u32)).into(),
             settings: (),
         };
 
@@ -219,9 +212,9 @@ mod tests {
         }
         assert_eq!(":date", signed.headers()[1].name().as_str());
         if let HeaderValue::Timestamp(value) = signed.headers()[1].value() {
-            assert_eq!(123_456_789_i64, value.epoch_seconds());
+            assert_eq!(123_456_789_i64, value.secs());
             // The subseconds should have been truncated off
-            assert_eq!(0, value.epoch_subsecond_nanos());
+            assert_eq!(0, value.subsec_nanos());
         } else {
             panic!("expected timestamp for :date header");
         }
diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs
index 4f5e33fee..22b35fa8f 100644
--- a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs
+++ b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs
@@ -5,11 +5,10 @@
 
 use super::query_writer::QueryWriter;
 use super::{Error, PayloadChecksumKind, SignableBody, SignatureLocation, SigningParams};
-use crate::date_fmt::{format_date, format_date_time, parse_date, parse_date_time};
+use crate::date_time::{format_date, format_date_time};
 use crate::http_request::sign::SignableRequest;
 use crate::http_request::PercentEncodingMode;
 use crate::sign::sha256_hex_string;
-use chrono::{Date, DateTime, Utc};
 use http::header::{HeaderName, CONTENT_LENGTH, CONTENT_TYPE, HOST, USER_AGENT};
 use http::{HeaderMap, HeaderValue, Method, Uri};
 use std::borrow::Cow;
@@ -18,6 +17,7 @@ use std::convert::TryFrom;
 use std::fmt;
 use std::fmt::Formatter;
 use std::str::FromStr;
+use std::time::SystemTime;
 
 pub(crate) mod header {
     pub(crate) const X_AMZ_CONTENT_SHA_256: &str = "x-amz-content-sha256";
@@ -133,7 +133,7 @@ impl<'a> CanonicalRequest<'a> {
         };
         let payload_hash = Self::payload_hash(req.body());
 
-        let date_time = format_date_time(&params.date_time);
+        let date_time = format_date_time(params.time);
         let (signed_headers, canonical_headers) =
             Self::headers(req, params, &payload_hash, &date_time)?;
         let signed_headers = SignedHeaders::new(signed_headers);
@@ -150,7 +150,7 @@ impl<'a> CanonicalRequest<'a> {
                 credential: format!(
                     "{}/{}/{}/{}/aws4_request",
                     params.access_key,
-                    format_date(&params.date_time.date()),
+                    format_date(params.time),
                     params.region,
                     params.service_name,
                 ),
@@ -429,7 +429,7 @@ impl Ord for CanonicalHeaderName {
 
 #[derive(PartialEq, Debug, Clone)]
 pub(super) struct SigningScope<'a> {
-    pub(super) date: Date<Utc>,
+    pub(super) time: SystemTime,
     pub(super) region: &'a str,
     pub(super) service: &'a str,
 }
@@ -439,75 +439,37 @@ impl<'a> fmt::Display for SigningScope<'a> {
         write!(
             f,
             "{}/{}/{}/aws4_request",
-            format_date(&self.date),
+            format_date(self.time),
             self.region,
             self.service
         )
     }
 }
 
-impl<'a> TryFrom<&'a str> for SigningScope<'a> {
-    type Error = Error;
-    fn try_from(s: &'a str) -> Result<SigningScope<'a>, Self::Error> {
-        let mut scopes = s.split('/');
-        let date = parse_date(scopes.next().expect("missing date"))?;
-        let region = scopes.next().expect("missing region");
-        let service = scopes.next().expect("missing service");
-
-        let scope = SigningScope {
-            date,
-            region,
-            service,
-        };
-
-        Ok(scope)
-    }
-}
-
 #[derive(PartialEq, Debug)]
 pub(super) struct StringToSign<'a> {
     pub(super) scope: SigningScope<'a>,
-    pub(super) date: DateTime<Utc>,
+    pub(super) time: SystemTime,
     pub(super) region: &'a str,
     pub(super) service: &'a str,
     pub(super) hashed_creq: &'a str,
 }
 
-impl<'a> TryFrom<&'a str> for StringToSign<'a> {
-    type Error = Error;
-    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
-        let lines = s.lines().collect::<Vec<&str>>();
-        let date = parse_date_time(lines[1])?;
-        let scope: SigningScope<'_> = TryFrom::try_from(lines[2])?;
-        let hashed_creq = &lines[3];
-
-        let sts = StringToSign {
-            date,
-            region: scope.region,
-            service: scope.service,
-            scope,
-            hashed_creq,
-        };
-
-        Ok(sts)
-    }
-}
-
 impl<'a> StringToSign<'a> {
     pub(crate) fn new(
-        date: DateTime<Utc>,
+        time: SystemTime,
         region: &'a str,
         service: &'a str,
         hashed_creq: &'a str,
     ) -> Self {
         let scope = SigningScope {
-            date: date.date(),
+            time,
             region,
             service,
         };
         Self {
             scope,
-            date,
+            time,
             region,
             service,
             hashed_creq,
@@ -521,7 +483,7 @@ impl<'a> fmt::Display for StringToSign<'a> {
             f,
             "{}\n{}\n{}\n{}",
             HMAC_256,
-            format_date_time(&self.date),
+            format_date_time(self.time),
             self.scope.to_string(),
             self.hashed_creq
         )
@@ -530,7 +492,7 @@ impl<'a> fmt::Display for StringToSign<'a> {
 
 #[cfg(test)]
 mod tests {
-    use crate::date_fmt::parse_date_time;
+    use crate::date_time::test_parsers::parse_date_time;
     use crate::http_request::canonical_request::{
         normalize_header_value, trim_all, CanonicalRequest, SigningScope, StringToSign,
     };
@@ -542,7 +504,6 @@ mod tests {
     use crate::sign::sha256_hex_string;
     use pretty_assertions::assert_eq;
     use proptest::{proptest, strategy::Strategy};
-    use std::convert::TryFrom;
     use std::time::Duration;
 
     fn signing_params(settings: SigningSettings) -> SigningParams<'static> {
@@ -552,7 +513,7 @@ mod tests {
             security_token: None,
             region: "test-region",
             service_name: "testservicename",
-            date_time: parse_date_time("20210511T154045Z").unwrap(),
+            time: parse_date_time("20210511T154045Z").unwrap(),
             settings,
         }
     }
@@ -624,9 +585,8 @@ mod tests {
     #[test]
     fn test_generate_scope() {
         let expected = "20150830/us-east-1/iam/aws4_request\n";
-        let date = parse_date_time("20150830T123600Z").unwrap();
         let scope = SigningScope {
-            date: date.date(),
+            time: parse_date_time("20150830T123600Z").unwrap(),
             region: "us-east-1",
             service: "iam",
         };
@@ -635,21 +595,15 @@ mod tests {
 
     #[test]
     fn test_string_to_sign() {
-        let date = parse_date_time("20150830T123600Z").unwrap();
+        let time = parse_date_time("20150830T123600Z").unwrap();
         let creq = test_canonical_request("get-vanilla-query-order-key-case");
         let expected_sts = test_sts("get-vanilla-query-order-key-case");
         let encoded = sha256_hex_string(creq.as_bytes());
 
-        let actual = StringToSign::new(date, "us-east-1", "service", &encoded);
+        let actual = StringToSign::new(time, "us-east-1", "service", &encoded);
         assert_eq!(expected_sts, actual.to_string());
     }
 
-    #[test]
-    fn read_sts() {
-        let sts = test_sts("get-vanilla-query-order-key-case");
-        StringToSign::try_from(sts.as_ref()).unwrap();
-    }
-
     #[test]
     fn test_digest_of_canonical_request() {
         let creq = test_canonical_request("get-vanilla-query-order-key-case");
diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs
index 25dd7fd35..0b80e7113 100644
--- a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs
+++ b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs
@@ -10,8 +10,8 @@
 //! ```rust
 //! # fn test() -> Result<(), aws_sigv4::http_request::Error> {
 //! use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest};
-//! use chrono::Utc;
 //! use http;
+//! use std::time::SystemTime;
 //!
 //! // Create the request to sign
 //! let mut request = http::Request::builder()
@@ -26,7 +26,7 @@
 //!     .secret_key("example secret key")
 //!     .region("us-east-1")
 //!     .service_name("exampleservice")
-//!     .date_time(Utc::now())
+//!     .time(SystemTime::now())
 //!     .settings(signing_settings)
 //!     .build()
 //!     .unwrap();
diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs
index 46d7b54b5..d8182a609 100644
--- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs
+++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs
@@ -184,14 +184,14 @@ fn calculate_signing_params<'a>(
 
     let encoded_creq = &sha256_hex_string(creq.to_string().as_bytes());
     let sts = StringToSign::new(
-        params.date_time,
+        params.time,
         params.region,
         params.service_name,
         encoded_creq,
     );
     let signing_key = generate_signing_key(
         params.secret_key,
-        params.date_time.date(),
+        params.time,
         params.region,
         params.service_name,
     );
@@ -235,7 +235,7 @@ fn calculate_signing_headers<'a>(
     // Step 2: https://docs.aws.amazon.com/en_pv/general/latest/gr/sigv4-create-string-to-sign.html.
     let encoded_creq = &sha256_hex_string(creq.to_string().as_bytes());
     let sts = StringToSign::new(
-        params.date_time,
+        params.time,
         params.region,
         params.service_name,
         encoded_creq,
@@ -244,7 +244,7 @@ fn calculate_signing_headers<'a>(
     // Step 3: https://docs.aws.amazon.com/en_pv/general/latest/gr/sigv4-calculate-signature.html
     let signing_key = generate_signing_key(
         params.secret_key,
-        params.date_time.date(),
+        params.time,
         params.region,
         params.service_name,
     );
@@ -299,7 +299,7 @@ fn build_authorization_header(
 #[cfg(test)]
 mod tests {
     use super::{sign, SigningInstructions};
-    use crate::date_fmt::parse_date_time;
+    use crate::date_time::test_parsers::parse_date_time;
     use crate::http_request::sign::SignableRequest;
     use crate::http_request::test::{
         make_headers_comparable, test_request, test_signed_request,
@@ -328,7 +328,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "service",
-            date_time: parse_date_time("20150830T123600Z").unwrap(),
+            time: parse_date_time("20150830T123600Z").unwrap(),
             settings,
         };
 
@@ -358,7 +358,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "service",
-            date_time: parse_date_time("20150830T123600Z").unwrap(),
+            time: parse_date_time("20150830T123600Z").unwrap(),
             settings,
         };
 
@@ -386,7 +386,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "service",
-            date_time: parse_date_time("20150830T123600Z").unwrap(),
+            time: parse_date_time("20150830T123600Z").unwrap(),
             settings,
         };
 
@@ -436,7 +436,7 @@ mod tests {
             security_token: None,
             region: "us-east-1",
             service_name: "service",
-            date_time: parse_date_time("20150830T123600Z").unwrap(),
+            time: parse_date_time("20150830T123600Z").unwrap(),
             settings,
         };
 
diff --git a/aws/rust-runtime/aws-sigv4/src/lib.rs b/aws/rust-runtime/aws-sigv4/src/lib.rs
index ddfe5560a..75fc46135 100644
--- a/aws/rust-runtime/aws-sigv4/src/lib.rs
+++ b/aws/rust-runtime/aws-sigv4/src/lib.rs
@@ -14,11 +14,11 @@
     unreachable_pub
 )]
 
-use chrono::{DateTime, Utc};
+use std::time::SystemTime;
 
 pub mod sign;
 
-mod date_fmt;
+mod date_time;
 
 #[cfg(feature = "sign-eventstream")]
 pub mod event_stream;
@@ -41,8 +41,8 @@ pub struct SigningParams<'a, S> {
     pub(crate) region: &'a str,
     /// AWS Service Name to sign for.
     pub(crate) service_name: &'a str,
-    /// Timestamp to use in the signature (should be `Utc::now()` unless testing).
-    pub(crate) date_time: DateTime<Utc>,
+    /// Timestamp to use in the signature (should be `SystemTime::now()` unless testing).
+    pub(crate) time: SystemTime,
 
     /// Additional signing settings. These differ between HTTP and Event Stream.
     pub(crate) settings: S,
@@ -58,9 +58,9 @@ impl<'a, S: Default> SigningParams<'a, S> {
 /// Builder and error for creating [`SigningParams`]
 pub mod signing_params {
     use super::SigningParams;
-    use chrono::{DateTime, Utc};
     use std::error::Error;
     use std::fmt;
+    use std::time::SystemTime;
 
     /// [`SigningParams`] builder error
     #[derive(Debug)]
@@ -89,7 +89,7 @@ pub mod signing_params {
         security_token: Option<&'a str>,
         region: Option<&'a str>,
         service_name: Option<&'a str>,
-        date_time: Option<DateTime<Utc>>,
+        time: Option<SystemTime>,
         settings: Option<S>,
     }
 
@@ -144,14 +144,14 @@ pub mod signing_params {
             self.service_name = service_name;
         }
 
-        /// Sets the date time to be used in the signature (required)
-        pub fn date_time(mut self, date_time: DateTime<Utc>) -> Self {
-            self.date_time = Some(date_time);
+        /// Sets the time to be used in the signature (required)
+        pub fn time(mut self, time: SystemTime) -> Self {
+            self.time = Some(time);
             self
         }
-        /// Sets the date time to be used in the signature (required)
-        pub fn set_date_time(&mut self, date_time: Option<DateTime<Utc>>) {
-            self.date_time = date_time;
+        /// Sets the time to be used in the signature (required)
+        pub fn set_time(&mut self, time: Option<SystemTime>) {
+            self.time = time;
         }
 
         /// Sets additional signing settings (required)
@@ -181,9 +181,9 @@ pub mod signing_params {
                 service_name: self
                     .service_name
                     .ok_or_else(|| BuildError::new("service name is required"))?,
-                date_time: self
-                    .date_time
-                    .ok_or_else(|| BuildError::new("date time is required"))?,
+                time: self
+                    .time
+                    .ok_or_else(|| BuildError::new("time is required"))?,
                 settings: self
                     .settings
                     .ok_or_else(|| BuildError::new("settings are required"))?,
diff --git a/aws/rust-runtime/aws-sigv4/src/sign.rs b/aws/rust-runtime/aws-sigv4/src/sign.rs
index 6041b8c5c..3e5dec987 100644
--- a/aws/rust-runtime/aws-sigv4/src/sign.rs
+++ b/aws/rust-runtime/aws-sigv4/src/sign.rs
@@ -5,12 +5,12 @@
 
 //! Functions to create signing keys and calculate signatures.
 
-use crate::date_fmt::format_date;
-use chrono::{Date, Utc};
+use crate::date_time::format_date;
 use ring::{
     digest::{self},
     hmac::{self, Key, Tag},
 };
+use std::time::SystemTime;
 
 /// HashedPayload = Lowercase(HexEncode(Hash(requestPayload)))
 #[allow(dead_code)] // Unused when compiling without certain features
@@ -29,7 +29,7 @@ pub fn calculate_signature(signing_key: Tag, string_to_sign: &[u8]) -> String {
 /// Generates a signing key for Sigv4
 pub fn generate_signing_key(
     secret: &str,
-    date: Date<Utc>,
+    time: SystemTime,
     region: &str,
     service: &str,
 ) -> hmac::Tag {
@@ -41,7 +41,7 @@ pub fn generate_signing_key(
 
     let secret = format!("AWS4{}", secret);
     let secret = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes());
-    let tag = hmac::sign(&secret, format_date(&date).as_bytes());
+    let tag = hmac::sign(&secret, format_date(time).as_bytes());
 
     // sign region
     let key = hmac::Key::new(hmac::HMAC_SHA256, tag.as_ref());
@@ -59,7 +59,7 @@ pub fn generate_signing_key(
 #[cfg(test)]
 mod tests {
     use super::{calculate_signature, generate_signing_key};
-    use crate::date_fmt::parse_date_time;
+    use crate::date_time::test_parsers::parse_date_time;
     use crate::http_request::test::test_canonical_request;
     use crate::sign::sha256_hex_string;
 
@@ -67,9 +67,9 @@ mod tests {
     fn test_signature_calculation() {
         let secret = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
         let creq = test_canonical_request("iam");
-        let date = parse_date_time("20150830T123600Z").unwrap();
+        let time = parse_date_time("20150830T123600Z").unwrap();
 
-        let derived_key = generate_signing_key(secret, date.date(), "us-east-1", "iam");
+        let derived_key = generate_signing_key(secret, time, "us-east-1", "iam");
         let signature = calculate_signature(derived_key, creq.as_bytes());
 
         let expected = "5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7";
diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts
index 47708f8cf..dfc8e0ac4 100644
--- a/aws/sdk/build.gradle.kts
+++ b/aws/sdk/build.gradle.kts
@@ -35,6 +35,7 @@ val runtimeModules = listOf(
     "aws-smithy-protocol-test",
     "aws-smithy-query",
     "aws-smithy-types",
+    "aws-smithy-types-convert",
     "aws-smithy-xml"
 )
 val awsModules = listOf(
diff --git a/aws/sdk/examples/apigateway/Cargo.toml b/aws/sdk/examples/apigateway/Cargo.toml
index 69ec42012..f931a5a58 100644
--- a/aws/sdk/examples/apigateway/Cargo.toml
+++ b/aws/sdk/examples/apigateway/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2018"
 
 [dependencies]
 aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
+aws-smithy-types-convert = { path = "../../build/aws-sdk/sdk/aws-smithy-types-convert", features = ["convert-chrono"] }
 aws-sdk-apigateway = { path = "../../build/aws-sdk/sdk/apigateway" }
 tokio = { version = "1", features = ["full"] }
 structopt = { version = "0.3", default-features = false }
diff --git a/aws/sdk/examples/apigateway/src/bin/get_rest_apis.rs b/aws/sdk/examples/apigateway/src/bin/get_rest_apis.rs
index 37eccb390..5e91cf536 100644
--- a/aws/sdk/examples/apigateway/src/bin/get_rest_apis.rs
+++ b/aws/sdk/examples/apigateway/src/bin/get_rest_apis.rs
@@ -5,6 +5,7 @@
 
 use aws_config::meta::region::RegionProviderChain;
 use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
+use aws_smithy_types_convert::date_time::DateTimeExt;
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -27,7 +28,10 @@ async fn show_apis(client: &Client) -> Result<(), Error> {
         println!("Name:        {}", api.name().unwrap_or_default());
         println!("Description: {}", api.description().unwrap_or_default());
         println!("Version:     {}", api.version().unwrap_or_default());
-        println!("Created:     {}", api.created_date().unwrap().to_chrono());
+        println!(
+            "Created:     {}",
+            api.created_date().unwrap().to_chrono_utc()
+        );
         println!();
     }
 
diff --git a/aws/sdk/examples/cognitoidentity/Cargo.toml b/aws/sdk/examples/cognitoidentity/Cargo.toml
index 3061d266c..b8b5b3708 100644
--- a/aws/sdk/examples/cognitoidentity/Cargo.toml
+++ b/aws/sdk/examples/cognitoidentity/Cargo.toml
@@ -8,7 +8,8 @@ edition = "2018"
 
 [dependencies]
 aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
-cognitoidentity = { package = "aws-sdk-cognitoidentity", path = "../../build/aws-sdk/sdk/cognitoidentity" }
+aws-smithy-types-convert = { path = "../../build/aws-sdk/sdk/aws-smithy-types-convert", features = ["convert-chrono"] }
+aws-sdk-cognitoidentity = { path = "../../build/aws-sdk/sdk/cognitoidentity" }
 aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
 tokio = { version = "1", features = ["full"] }
 chrono = "0.4"
diff --git a/aws/sdk/examples/cognitoidentity/src/bin/describe-identity-pool.rs b/aws/sdk/examples/cognitoidentity/src/bin/describe-identity-pool.rs
index 8681acf9d..56a1f12fd 100644
--- a/aws/sdk/examples/cognitoidentity/src/bin/describe-identity-pool.rs
+++ b/aws/sdk/examples/cognitoidentity/src/bin/describe-identity-pool.rs
@@ -4,7 +4,7 @@
  */
 
 use aws_config::meta::region::RegionProviderChain;
-use cognitoidentity::{Client, Error, Region, PKG_VERSION};
+use aws_sdk_cognitoidentity::{Client, Error, Region, PKG_VERSION};
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -64,16 +64,16 @@ async fn main() -> Result<(), Error> {
         .send()
         .await?;
 
-    let allow_classic = response.allow_classic_flow.unwrap_or_default();
-    let allow_unauth_ids = response.allow_unauthenticated_identities;
+    let allow_classic = response.allow_classic_flow().unwrap_or_default();
+    let allow_unauth_ids = response.allow_unauthenticated_identities();
     println!("  Allow classic flow                {}", allow_classic);
     println!("  Allow unauthenticated identities: {}", allow_unauth_ids);
-    if let Some(providers) = response.cognito_identity_providers {
+    if let Some(providers) = response.cognito_identity_providers() {
         println!("  Identity Providers:");
         for provider in providers {
-            let client_id = provider.client_id.unwrap_or_default();
-            let name = provider.provider_name.unwrap_or_default();
-            let server_side_check = provider.server_side_token_check.unwrap_or_default();
+            let client_id = provider.client_id().unwrap_or_default();
+            let name = provider.provider_name().unwrap_or_default();
+            let server_side_check = provider.server_side_token_check().unwrap_or_default();
 
             println!("    Client ID:                {}", client_id);
             println!("    Name:                     {}", name);
@@ -82,15 +82,15 @@ async fn main() -> Result<(), Error> {
         }
     }
 
-    let developer_provider = response.developer_provider_name.unwrap_or_default();
-    let id = response.identity_pool_id.unwrap_or_default();
-    let name = response.identity_pool_name.unwrap_or_default();
+    let developer_provider = response.developer_provider_name().unwrap_or_default();
+    let id = response.identity_pool_id().unwrap_or_default();
+    let name = response.identity_pool_name().unwrap_or_default();
 
     println!("  Developer provider:               {}", developer_provider);
     println!("  Identity pool ID:                 {}", id);
     println!("  Identity pool name:               {}", name);
 
-    if let Some(tags) = response.identity_pool_tags {
+    if let Some(tags) = response.identity_pool_tags() {
         println!("  Tags:");
         for (key, value) in tags {
             println!("    key:   {}", key);
@@ -98,14 +98,14 @@ async fn main() -> Result<(), Error> {
         }
     }
 
-    if let Some(open_id_arns) = response.open_id_connect_provider_ar_ns {
+    if let Some(open_id_arns) = response.open_id_connect_provider_ar_ns() {
         println!("  Open ID provider ARNs:");
         for arn in open_id_arns {
             println!("    {}", arn);
         }
     }
 
-    if let Some(saml_arns) = response.saml_provider_ar_ns {
+    if let Some(saml_arns) = response.saml_provider_ar_ns() {
         println!("  SAML provider ARNs:");
         for arn in saml_arns {
             println!("    {}", arn);
@@ -113,7 +113,7 @@ async fn main() -> Result<(), Error> {
     }
 
     // SupportedLoginProviders
-    if let Some(login_providers) = response.supported_login_providers {
+    if let Some(login_providers) = response.supported_login_providers() {
         println!("  Supported login providers:");
         for (key, value) in login_providers {
             println!("    key:   {}", key);
diff --git a/aws/sdk/examples/cognitoidentity/src/bin/list-identity-pools.rs b/aws/sdk/examples/cognitoidentity/src/bin/list-identity-pools.rs
index e0fc447dc..86c396f02 100644
--- a/aws/sdk/examples/cognitoidentity/src/bin/list-identity-pools.rs
+++ b/aws/sdk/examples/cognitoidentity/src/bin/list-identity-pools.rs
@@ -4,7 +4,7 @@
  */
 
 use aws_config::meta::region::RegionProviderChain;
-use cognitoidentity::{Client, Error, Region, PKG_VERSION};
+use aws_sdk_cognitoidentity::{Client, Error, Region, PKG_VERSION};
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -52,18 +52,18 @@ async fn main() -> Result<(), Error> {
     let response = client.list_identity_pools().max_results(10).send().await?;
 
     // Print IDs and names of pools.
-    if let Some(pools) = response.identity_pools {
+    if let Some(pools) = response.identity_pools() {
         println!("Identity pools:");
         for pool in pools {
-            let id = pool.identity_pool_id.unwrap_or_default();
-            let name = pool.identity_pool_name.unwrap_or_default();
+            let id = pool.identity_pool_id().unwrap_or_default();
+            let name = pool.identity_pool_name().unwrap_or_default();
             println!("  Identity pool ID:   {}", id);
             println!("  Identity pool name: {}", name);
             println!();
         }
     }
 
-    println!("Next token: {:?}", response.next_token);
+    println!("Next token: {:?}", response.next_token());
 
     Ok(())
 }
diff --git a/aws/sdk/examples/cognitoidentity/src/bin/list-pool-identities.rs b/aws/sdk/examples/cognitoidentity/src/bin/list-pool-identities.rs
index aac3ca589..35a4d9ae6 100644
--- a/aws/sdk/examples/cognitoidentity/src/bin/list-pool-identities.rs
+++ b/aws/sdk/examples/cognitoidentity/src/bin/list-pool-identities.rs
@@ -4,7 +4,8 @@
  */
 
 use aws_config::meta::region::RegionProviderChain;
-use cognitoidentity::{Client, Error, Region, PKG_VERSION};
+use aws_sdk_cognitoidentity::{Client, Error, Region, PKG_VERSION};
+use aws_smithy_types_convert::date_time::DateTimeExt;
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -66,18 +67,18 @@ async fn main() -> Result<(), Error> {
         .send()
         .await?;
 
-    if let Some(ids) = response.identities {
+    if let Some(ids) = response.identities() {
         println!("Identitities:");
         for id in ids {
-            let creation_timestamp = id.creation_date.unwrap().to_chrono();
-            let idid = id.identity_id.unwrap_or_default();
-            let mod_timestamp = id.last_modified_date.unwrap().to_chrono();
+            let creation_timestamp = id.creation_date().unwrap().to_chrono_utc();
+            let idid = id.identity_id().unwrap_or_default();
+            let mod_timestamp = id.last_modified_date().unwrap().to_chrono_utc();
             println!("  Creation date:      {}", creation_timestamp);
             println!("  ID:                 {}", idid);
             println!("  Last modified date: {}", mod_timestamp);
 
             println!("  Logins:");
-            for login in id.logins.unwrap_or_default() {
+            for login in id.logins().unwrap_or_default() {
                 println!("    {}", login);
             }
 
@@ -85,7 +86,7 @@ async fn main() -> Result<(), Error> {
         }
     }
 
-    println!("Next token: {:?}", response.next_token);
+    println!("Next token: {:?}", response.next_token());
 
     println!();
 
diff --git a/aws/sdk/examples/cognitoidentityprovider/Cargo.toml b/aws/sdk/examples/cognitoidentityprovider/Cargo.toml
index 09d7482a0..235ed1a1e 100644
--- a/aws/sdk/examples/cognitoidentityprovider/Cargo.toml
+++ b/aws/sdk/examples/cognitoidentityprovider/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 
 [dependencies]
 aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
+aws-smithy-types-convert = { path = "../../build/aws-sdk/sdk/aws-smithy-types-convert", features = ["convert-chrono"] }
 aws-sdk-cognitoidentityprovider = { package = "aws-sdk-cognitoidentityprovider", path = "../../build/aws-sdk/sdk/cognitoidentityprovider" }
 aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
 tokio = { version = "1", features = ["full"] }
diff --git a/aws/sdk/examples/cognitoidentityprovider/src/bin/list-user-pools.rs b/aws/sdk/examples/cognitoidentityprovider/src/bin/list-user-pools.rs
index 96d1169c6..4a1375398 100644
--- a/aws/sdk/examples/cognitoidentityprovider/src/bin/list-user-pools.rs
+++ b/aws/sdk/examples/cognitoidentityprovider/src/bin/list-user-pools.rs
@@ -5,6 +5,7 @@
 
 use aws_config::meta::region::RegionProviderChain;
 use aws_sdk_cognitoidentityprovider::{Client, Error, Region, PKG_VERSION};
+use aws_smithy_types_convert::date_time::DateTimeExt;
 
 use structopt::StructOpt;
 
@@ -48,25 +49,25 @@ async fn main() -> Result<(), Error> {
     }
 
     let response = client.list_user_pools().max_results(10).send().await?;
-    if let Some(pools) = response.user_pools {
+    if let Some(pools) = response.user_pools() {
         println!("User pools:");
         for pool in pools {
-            println!("  ID:              {}", pool.id.unwrap_or_default());
-            println!("  Name:            {}", pool.name.unwrap_or_default());
-            println!("  Status:          {:?}", pool.status);
-            println!("  Lambda Config:   {:?}", pool.lambda_config.unwrap());
+            println!("  ID:              {}", pool.id().unwrap_or_default());
+            println!("  Name:            {}", pool.name().unwrap_or_default());
+            println!("  Status:          {:?}", pool.status());
+            println!("  Lambda Config:   {:?}", pool.lambda_config().unwrap());
             println!(
                 "  Last modified:   {}",
-                pool.last_modified_date.unwrap().to_chrono()
+                pool.last_modified_date().unwrap().to_chrono_utc()
             );
             println!(
                 "  Creation date:   {:?}",
-                pool.creation_date.unwrap().to_chrono()
+                pool.creation_date().unwrap().to_chrono_utc()
             );
             println!();
         }
     }
-    println!("Next token: {}", response.next_token.unwrap_or_default());
+    println!("Next token: {}", response.next_token().unwrap_or_default());
 
     Ok(())
 }
diff --git a/aws/sdk/examples/cognitosync/Cargo.toml b/aws/sdk/examples/cognitosync/Cargo.toml
index 97d665537..f155fff8f 100644
--- a/aws/sdk/examples/cognitosync/Cargo.toml
+++ b/aws/sdk/examples/cognitosync/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 
 [dependencies]
 aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
+aws-smithy-types-convert = { path = "../../build/aws-sdk/sdk/aws-smithy-types-convert", features = ["convert-chrono"] }
 aws-sdk-cognitosync = { package = "aws-sdk-cognitosync", path = "../../build/aws-sdk/sdk/cognitosync" }
 aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
 tokio = { version = "1", features = ["full"] }
diff --git a/aws/sdk/examples/cognitosync/src/bin/list-identity-pool-usage.rs b/aws/sdk/examples/cognitosync/src/bin/list-identity-pool-usage.rs
index c75e5c1d0..23c9354bb 100644
--- a/aws/sdk/examples/cognitosync/src/bin/list-identity-pool-usage.rs
+++ b/aws/sdk/examples/cognitosync/src/bin/list-identity-pool-usage.rs
@@ -5,6 +5,7 @@
 
 use aws_config::meta::region::RegionProviderChain;
 use aws_sdk_cognitosync::{Client, Error, Region, PKG_VERSION};
+use aws_smithy_types_convert::date_time::DateTimeExt;
 
 use structopt::StructOpt;
 
@@ -56,31 +57,31 @@ async fn main() -> Result<(), Error> {
         .send()
         .await?;
 
-    if let Some(pools) = response.identity_pool_usages {
+    if let Some(pools) = response.identity_pool_usages() {
         println!("Identity pools:");
 
         for pool in pools {
             println!(
                 "  Identity pool ID:    {}",
-                pool.identity_pool_id.unwrap_or_default()
+                pool.identity_pool_id().unwrap_or_default()
             );
             println!(
                 "  Data storage:        {}",
-                pool.data_storage.unwrap_or_default()
+                pool.data_storage().unwrap_or_default()
             );
             println!(
                 "  Sync sessions count: {}",
-                pool.sync_sessions_count.unwrap_or_default()
+                pool.sync_sessions_count().unwrap_or_default()
             );
             println!(
                 "  Last modified:       {}",
-                pool.last_modified_date.unwrap().to_chrono()
+                pool.last_modified_date().unwrap().to_chrono_utc()
             );
             println!();
         }
     }
 
-    println!("Next token: {:?}", response.next_token);
+    println!("Next token: {:?}", response.next_token());
 
     Ok(())
 }
diff --git a/aws/sdk/examples/sagemaker/Cargo.toml b/aws/sdk/examples/sagemaker/Cargo.toml
index fa8dcf5c1..4ce33dc10 100644
--- a/aws/sdk/examples/sagemaker/Cargo.toml
+++ b/aws/sdk/examples/sagemaker/Cargo.toml
@@ -8,7 +8,8 @@ edition = "2018"
 
 [dependencies]
 aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
-sagemaker = {package = "aws-sdk-sagemaker", path = "../../build/aws-sdk/sdk/sagemaker"}
+aws-sdk-sagemaker = { path = "../../build/aws-sdk/sdk/sagemaker"}
+aws-smithy-types-convert = { path = "../../build/aws-sdk/sdk/aws-smithy-types-convert", features = ["convert-chrono"] }
 aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
 
 tokio = { version = "1", features = ["full"] }
diff --git a/aws/sdk/examples/sagemaker/src/bin/list-training-jobs.rs b/aws/sdk/examples/sagemaker/src/bin/list-training-jobs.rs
index c13faee0a..618cfbd1e 100644
--- a/aws/sdk/examples/sagemaker/src/bin/list-training-jobs.rs
+++ b/aws/sdk/examples/sagemaker/src/bin/list-training-jobs.rs
@@ -4,9 +4,9 @@
  */
 
 use aws_config::meta::region::RegionProviderChain;
-
+use aws_sdk_sagemaker as sagemaker;
+use aws_smithy_types_convert::date_time::DateTimeExt;
 use sagemaker::{Client, Region};
-
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -54,12 +54,12 @@ async fn main() -> Result<(), sagemaker::Error> {
     let job_details = client.list_training_jobs().send().await?;
 
     println!("Job Name\tCreation DateTime\tDuration\tStatus");
-    for j in job_details.training_job_summaries.unwrap_or_default() {
-        let name = j.training_job_name.as_deref().unwrap_or_default();
-        let creation_time = j.creation_time.unwrap().to_chrono();
-        let training_end_time = j.training_end_time.unwrap().to_chrono();
+    for j in job_details.training_job_summaries().unwrap_or_default() {
+        let name = j.training_job_name().unwrap_or_default();
+        let creation_time = j.creation_time().unwrap().to_chrono_utc();
+        let training_end_time = j.training_end_time().unwrap().to_chrono_utc();
 
-        let status = j.training_job_status.unwrap();
+        let status = j.training_job_status().unwrap();
         let duration = training_end_time - creation_time;
 
         println!(
diff --git a/aws/sdk/examples/sagemaker/src/bin/sagemaker-helloworld.rs b/aws/sdk/examples/sagemaker/src/bin/sagemaker-helloworld.rs
index 9d8c6b34d..b5f65d4dd 100644
--- a/aws/sdk/examples/sagemaker/src/bin/sagemaker-helloworld.rs
+++ b/aws/sdk/examples/sagemaker/src/bin/sagemaker-helloworld.rs
@@ -4,9 +4,8 @@
  */
 
 use aws_config::meta::region::RegionProviderChain;
-
+use aws_sdk_sagemaker as sagemaker;
 use sagemaker::{Client, Region};
-
 use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
@@ -52,10 +51,10 @@ async fn main() -> Result<(), sagemaker::Error> {
 
     let notebooks = client.list_notebook_instances().send().await?;
 
-    for n in notebooks.notebook_instances.unwrap_or_default() {
-        let n_instance_type = n.instance_type.unwrap();
-        let n_status = n.notebook_instance_status.unwrap();
-        let n_name = n.notebook_instance_name.as_deref().unwrap_or_default();
+    for n in notebooks.notebook_instances().unwrap_or_default() {
+        let n_instance_type = n.instance_type().unwrap();
+        let n_status = n.notebook_instance_status().unwrap();
+        let n_name = n.notebook_instance_name().unwrap_or_default();
 
         println!(
             "Notebook Name : {}, Notebook Status : {:#?}, Notebook Instance Type : {:#?}",
diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt
index d70be7f32..2ecbc6946 100644
--- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt
+++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt
@@ -187,12 +187,12 @@ private class ServerHttpProtocolImplGenerator(
         if (operationShape.errors.isNotEmpty()) {
             rustTemplate(
                 """
-            impl #{SerializeHttpError} for $operationName {
-                type Output = std::result::Result<#{http}::Response<#{Bytes}>, #{Error}>;
-                type Struct = #{E};
-                fn serialize(&self, error: &Self::Struct) -> Self::Output {
-                    #{serialize_error}(error)
-                }
+                impl #{SerializeHttpError} for $operationName {
+                    type Output = std::result::Result<#{http}::Response<#{Bytes}>, #{Error}>;
+                    type Struct = #{E};
+                    fn serialize(&self, error: &Self::Struct) -> Self::Output {
+                        #{serialize_error}(error)
+                    }
             }""",
                 *codegenScope,
                 "E" to errorSymbol,
@@ -569,7 +569,7 @@ private class ServerHttpProtocolImplGenerator(
                     let value = #{PercentEncoding}::percent_decode_str(value)
                         .decode_utf8()
                         .map_err(|err| #{Error}::DeserializeLabel(err.to_string()))?;
-                    let value = #{Instant}::Instant::from_str(&value, #{format})
+                    let value = #{DateTime}::DateTime::from_str(&value, #{format})
                         .map_err(|err| #{Error}::DeserializeLabel(err.to_string()))?;
                     Ok(Some(value))
                     """.trimIndent(),
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt
index 6fd6ee482..9de53b193 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt
@@ -151,9 +151,9 @@ fun RustType.render(fullyQualified: Boolean = true): String {
 }
 
 /**
- * Returns true if [this] contains [t] anywhere within it's tree. For example,
- * Option<Instant>.contains(Instant) would return true.
- * Option<Instant>.contains(Blob) would return false.
+ * Returns true if [this] contains [t] anywhere within its tree. For example,
+ * Option<DateTime>.contains(DateTime) would return true.
+ * Option<DateTime>.contains(Blob) would return false.
  */
 fun <T : RustType> RustType.contains(t: T): Boolean = when (this) {
     t -> true
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt
index 0d648de50..182e6b332 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt
@@ -177,8 +177,8 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
         val StdError = RuntimeType("Error", dependency = null, namespace = "std::error")
         val String = RuntimeType("String", dependency = null, namespace = "std::string")
 
-        fun Instant(runtimeConfig: RuntimeConfig) =
-            RuntimeType("Instant", CargoDependency.SmithyTypes(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_types")
+        fun DateTime(runtimeConfig: RuntimeConfig) =
+            RuntimeType("DateTime", CargoDependency.SmithyTypes(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_types")
 
         fun GenericError(runtimeConfig: RuntimeConfig) =
             RuntimeType("Error", CargoDependency.SmithyTypes(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_types")
@@ -219,7 +219,7 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
             return RuntimeType(
                 timestampFormat,
                 CargoDependency.SmithyTypes(runtimeConfig),
-                "${runtimeConfig.crateSrcPrefix}_types::instant::Format"
+                "${runtimeConfig.crateSrcPrefix}_types::date_time::Format"
             )
         }
 
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt
index b7a31e5a3..5bfaa46c0 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt
@@ -286,7 +286,7 @@ class SymbolVisitor(
     }
 
     override fun timestampShape(shape: TimestampShape?): Symbol {
-        return RuntimeType.Instant(config.runtimeConfig).toSymbol()
+        return RuntimeType.DateTime(config.runtimeConfig).toSymbol()
     }
 
     private fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder {
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SmithyTypesPubUseGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SmithyTypesPubUseGenerator.kt
index 74f165f8f..1d650165c 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SmithyTypesPubUseGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SmithyTypesPubUseGenerator.kt
@@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection
 
 fun pubUseTypes(runtimeConfig: RuntimeConfig) = listOf(
     RuntimeType.Blob(runtimeConfig),
+    RuntimeType.DateTime(runtimeConfig),
     CargoDependency.SmithyHttp(runtimeConfig).asType().member("result::SdkError"),
     CargoDependency.SmithyHttp(runtimeConfig).asType().member("byte_stream::ByteStream"),
 )
@@ -26,6 +27,7 @@ class SmithyTypesPubUseGenerator(private val runtimeConfig: RuntimeConfig) : Lib
             LibRsSection.Body -> pubUseTypes(runtimeConfig).forEach {
                 rust("pub use #T;", it)
             }
+            else -> { }
         }
     }
 }
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt
index cfbd2e03b..a83f32695 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt
@@ -90,8 +90,8 @@ class Instantiator(
 
             // Wrapped Shapes
             is TimestampShape -> writer.write(
-                "#T::from_epoch_seconds(${(arg as NumberNode).value})",
-                RuntimeType.Instant(runtimeConfig)
+                "#T::from_secs(${(arg as NumberNode).value})",
+                RuntimeType.DateTime(runtimeConfig)
             )
 
             /**
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt
index 75b1616cb..dcb8140d3 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt
@@ -120,7 +120,7 @@ class RequestBindingGenerator(
             write("let mut uri = String::new();")
             write("uri_base(input, &mut uri)?;")
             if (hasQuery) {
-                write("uri_query(input, &mut uri);")
+                write("uri_query(input, &mut uri)?;")
             }
             if (hasHeaders) {
                 write("let builder = add_headers(input, builder)?;")
@@ -257,7 +257,7 @@ class RequestBindingGenerator(
                 val timestampFormat =
                     index.determineTimestampFormat(member, HttpBinding.Location.HEADER, defaultTimestampFormat)
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
-                "$targetName.fmt(${writer.format(timestampFormatType)})"
+                "$targetName.fmt(${writer.format(timestampFormatType)})?"
             }
             target.isListShape || target.isMemberShape -> {
                 throw IllegalArgumentException("lists should be handled at a higher level")
@@ -322,7 +322,10 @@ class RequestBindingGenerator(
             return false
         }
         val preloadedParams = literalParams.keys + dynamicParams.map { it.locationName }
-        writer.rustBlockTemplate("fn uri_query(_input: &#{Input}, mut output: &mut String)", *codegenScope) {
+        writer.rustBlockTemplate(
+            "fn uri_query(_input: &#{Input}, mut output: &mut String) -> Result<(), #{BuildError}>",
+            *codegenScope
+        ) {
             write("let mut query = #T::new(&mut output);", RuntimeType.QueryFormat(runtimeConfig, "Writer"))
             literalParams.forEach { (k, v) ->
                 // When `v` is an empty string, no value should be set.
@@ -372,6 +375,7 @@ class RequestBindingGenerator(
                     }
                 }
             }
+            writer.rust("Ok(())")
         }
         return true
     }
@@ -390,7 +394,7 @@ class RequestBindingGenerator(
                     index.determineTimestampFormat(member, HttpBinding.Location.QUERY, defaultTimestampFormat)
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
                 val func = writer.format(RuntimeType.QueryFormat(runtimeConfig, "fmt_timestamp"))
-                "&$func($targetName, ${writer.format(timestampFormatType)})"
+                "&$func($targetName, ${writer.format(timestampFormatType)})?"
             }
             target.isListShape || target.isMemberShape -> {
                 throw IllegalArgumentException("lists should be handled at a higher level")
@@ -426,7 +430,7 @@ class RequestBindingGenerator(
                     index.determineTimestampFormat(member, HttpBinding.Location.LABEL, defaultTimestampFormat)
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
                 val func = format(RuntimeType.LabelFormat(runtimeConfig, "fmt_timestamp"))
-                rust("let $outputVar = $func($input, ${format(timestampFormatType)});")
+                rust("let $outputVar = $func($input, ${format(timestampFormatType)})?;")
             }
             else -> {
                 rust(
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt
index 4ab7b7fc4..6a702d112 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt
@@ -58,7 +58,7 @@ class ResponseBindingGenerator(
     private val index = HttpBindingIndex.of(model)
     private val headerUtil = CargoDependency.SmithyHttp(runtimeConfig).asType().member("header")
     private val defaultTimestampFormat = TimestampFormatTrait.Format.EPOCH_SECONDS
-    private val instant = RuntimeType.Instant(runtimeConfig).toSymbol().rustType()
+    private val dateTime = RuntimeType.DateTime(runtimeConfig).toSymbol().rustType()
     private val httpSerdeModule = RustModule.private("http_serde")
 
     /**
@@ -264,7 +264,7 @@ class ResponseBindingGenerator(
             rustType to targetType
         }
         val parsedValue = safeName()
-        if (coreType == instant) {
+        if (coreType == dateTime) {
             val timestampFormat =
                 index.determineTimestampFormat(
                     memberShape,
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt
index f7b414437..190c3a145 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt
@@ -605,7 +605,7 @@ class XmlBindingTraitParserGenerator(
                         TimestampFormatTrait.Format.DATE_TIME
                     )
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
-                withBlock("#T::from_str(", ")", RuntimeType.Instant(runtimeConfig)) {
+                withBlock("#T::from_str(", ")", RuntimeType.DateTime(runtimeConfig)) {
                     provider()
                     rust(", #T", timestampFormatType)
                 }
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt
index 6388cc253..83386c484 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt
@@ -337,7 +337,7 @@ class JsonSerializerGenerator(
                 val timestampFormat =
                     httpBindingResolver.timestampFormat(context.shape, HttpLocation.DOCUMENT, EPOCH_SECONDS)
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
-                rust("$writer.instant(${value.name}, #T);", timestampFormatType)
+                rust("$writer.date_time(${value.name}, #T)?;", timestampFormatType)
             }
             is CollectionShape -> jsonArrayWriter(context) { arrayName ->
                 serializeCollection(Context(arrayName, context.valueExpression, target))
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/QuerySerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/QuerySerializerGenerator.kt
index cd7e8613a..8a7512cbc 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/QuerySerializerGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/QuerySerializerGenerator.kt
@@ -229,7 +229,7 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct
             is TimestampShape -> {
                 val timestampFormat = determineTimestampFormat(context.shape)
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
-                rust("$writer.instant(${value.name}, #T);", timestampFormatType)
+                rust("$writer.date_time(${value.name}, #T)?;", timestampFormatType)
             }
             is CollectionShape -> serializeCollection(context, Context(writer, context.valueExpression, target))
             is MapShape -> serializeMap(context, Context(writer, context.valueExpression, target))
diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt
index aa7e607c9..198797415 100644
--- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt
+++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt
@@ -245,7 +245,7 @@ class XmlBindingTraitSerializerGenerator(
                         TimestampFormatTrait.Format.DATE_TIME
                     )
                 val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
-                rust("$input.fmt(#T).as_ref()", timestampFormatType)
+                rust("$input.fmt(#T)?.as_ref()", timestampFormatType)
             }
             else -> TODO(member.toString())
         }
diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/SymbolBuilderTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/SymbolBuilderTest.kt
index 5c5ea6a8b..145bb4e54 100644
--- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/SymbolBuilderTest.kt
+++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/SymbolBuilderTest.kt
@@ -221,8 +221,8 @@ class SymbolBuilderTest {
             .unwrap()
         val provider: SymbolProvider = testSymbolProvider(model)
         val sym = provider.toSymbol(member)
-        sym.rustType().render(false) shouldBe "Option<Instant>"
-        sym.referenceClosure().map { it.name } shouldContain "Instant"
+        sym.rustType().render(false) shouldBe "Option<DateTime>"
+        sym.referenceClosure().map { it.name } shouldContain "DateTime"
         sym.references[0].dependencies.shouldNotBeEmpty()
     }
 
diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt
index 11586a03e..9f948470d 100644
--- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt
+++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt
@@ -258,7 +258,7 @@ class StructureGeneratorTest {
                     """
                     let _: Option<&str> = one.field_string();
                     let _: Option<&aws_smithy_types::Blob> = one.field_blob();
-                    let _: Option<&aws_smithy_types::instant::Instant> = one.field_timestamp();
+                    let _: Option<&aws_smithy_types::DateTime> = one.field_timestamp();
                     let _: Option<&aws_smithy_types::Document> = one.field_document();
                     let _: Option<bool> = one.field_boolean();
                     let _: bool = one.field_primitive_boolean();
diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/http/RequestBindingGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/http/RequestBindingGeneratorTest.kt
index 36e96f36c..80b94a9aa 100644
--- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/http/RequestBindingGeneratorTest.kt
+++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/http/RequestBindingGeneratorTest.kt
@@ -127,7 +127,10 @@ class RequestBindingGeneratorTest {
             // some wrappers that can be called directly from the tests. The functions will get duplicated,
             // but that's not a problem.
 
-            rustBlock("pub fn test_uri_query(&self, mut output: &mut String)") {
+            rustBlock(
+                "pub fn test_uri_query(&self, mut output: &mut String) -> Result<(), #T>",
+                TestRuntimeConfig.operationBuildError()
+            ) {
                 bindingGen.renderUpdateHttpBuilder(this)
                 rust("uri_query(self, output)")
             }
@@ -164,7 +167,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("somebucket/ok")
                 .key(ts.clone())
@@ -188,7 +191,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("somebucket/ok")
                 .key(ts.clone())
@@ -210,7 +213,7 @@ class RequestBindingGeneratorTest {
         writer.compileAndTest(
             """
             use std::collections::HashMap;
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("buk")
                 .set_date_header_list(Some(vec![ts.clone()]))
@@ -247,7 +250,7 @@ class RequestBindingGeneratorTest {
         writer.compileAndTest(
             """
             use std::collections::HashMap;
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("buk")
                 .key(ts.clone())
@@ -266,7 +269,7 @@ class RequestBindingGeneratorTest {
         writer.compileAndTest(
             """
             use std::collections::HashMap;
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("buk")
                 .key(ts.clone())
@@ -284,7 +287,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("buk")
                 .key(ts.clone())
@@ -303,7 +306,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 // don't set bucket
                 // .bucket_name("buk")
@@ -321,7 +324,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("buk")
                 // don't set key
@@ -339,7 +342,7 @@ class RequestBindingGeneratorTest {
         renderOperation(writer)
         writer.compileAndTest(
             """
-            let ts = aws_smithy_types::Instant::from_epoch_seconds(10123125);
+            let ts = aws_smithy_types::DateTime::from_secs(10123125);
             let inp = PutObjectInput::builder()
                 .bucket_name("")
                 .key(ts.clone())
diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt
index af5599a5a..70a9bfe08 100644
--- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt
+++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt
@@ -49,7 +49,7 @@ class EventStreamUnmarshallerGeneratorTest {
             writer.rust(
                 """
                 use aws_smithy_eventstream::frame::{Header, HeaderValue, Message, UnmarshallMessage, UnmarshalledMessage};
-                use aws_smithy_types::{Blob, Instant};
+                use aws_smithy_types::{Blob, DateTime};
                 use crate::error::*;
                 use crate::model::*;
 
@@ -86,15 +86,15 @@ class EventStreamUnmarshallerGeneratorTest {
             writer.unitTest(
                 name = "message_with_blob",
                 test = """
-                let message = msg("event", "MessageWithBlob", "application/octet-stream", b"hello, world!");
-                let result = ${writer.format(generator.render())}().unmarshall(&message);
-                assert!(result.is_ok(), "expected ok, got: {:?}", result);
-                assert_eq!(
-                    TestStream::MessageWithBlob(
-                        MessageWithBlob::builder().data(Blob::new(&b"hello, world!"[..])).build()
-                    ),
-                    expect_event(result.unwrap())
-                );
+                    let message = msg("event", "MessageWithBlob", "application/octet-stream", b"hello, world!");
+                    let result = ${writer.format(generator.render())}().unmarshall(&message);
+                    assert!(result.is_ok(), "expected ok, got: {:?}", result);
+                    assert_eq!(
+                        TestStream::MessageWithBlob(
+                            MessageWithBlob::builder().data(Blob::new(&b"hello, world!"[..])).build()
+                        ),
+                        expect_event(result.unwrap())
+                    );
                 """,
             )
 
@@ -102,14 +102,14 @@ class EventStreamUnmarshallerGeneratorTest {
                 writer.unitTest(
                     "unknown_message",
                     """
-                let message = msg("event", "NewUnmodeledMessageType", "application/octet-stream", b"hello, world!");
-                let result = ${writer.format(generator.render())}().unmarshall(&message);
-                assert!(result.is_ok(), "expected ok, got: {:?}", result);
-                assert_eq!(
-                    TestStream::Unknown,
-                    expect_event(result.unwrap())
-                );
-                """,
+                    let message = msg("event", "NewUnmodeledMessageType", "application/octet-stream", b"hello, world!");
+                    let result = ${writer.format(generator.render())}().unmarshall(&message);
+                    assert!(result.is_ok(), "expected ok, got: {:?}", result);
+                    assert_eq!(
+                        TestStream::Unknown,
+                        expect_event(result.unwrap())
+                    );
+                    """,
                 )
             }
 
@@ -180,7 +180,7 @@ class EventStreamUnmarshallerGeneratorTest {
                     .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64)))
                     .add_header(Header::new("short", HeaderValue::Int16(16_000i16)))
                     .add_header(Header::new("string", HeaderValue::String("test".into())))
-                    .add_header(Header::new("timestamp", HeaderValue::Timestamp(Instant::from_epoch_seconds(5))));
+                    .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5))));
                 let result = ${writer.format(generator.render())}().unmarshall(&message);
                 assert!(result.is_ok(), "expected ok, got: {:?}", result);
                 assert_eq!(
@@ -192,7 +192,7 @@ class EventStreamUnmarshallerGeneratorTest {
                         .long(9_000_000_000i64)
                         .short(16_000i16)
                         .string("test")
-                        .timestamp(Instant::from_epoch_seconds(5))
+                        .timestamp(DateTime::from_secs(5))
                         .build()
                     ),
                     expect_event(result.unwrap())
diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt
index 0cdbf77fa..006273b9a 100644
--- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt
+++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt
@@ -54,7 +54,7 @@ class EventStreamMarshallerGeneratorTest {
                 """
                 use aws_smithy_eventstream::frame::{Message, Header, HeaderValue, MarshallMessage};
                 use std::collections::HashMap;
-                use aws_smithy_types::{Blob, Instant};
+                use aws_smithy_types::{Blob, DateTime};
                 use crate::error::*;
                 use crate::model::*;
 
@@ -171,7 +171,7 @@ class EventStreamMarshallerGeneratorTest {
                     .long(9_000_000_000i64)
                     .short(16_000i16)
                     .string("test")
-                    .timestamp(Instant::from_epoch_seconds(5))
+                    .timestamp(DateTime::from_secs(5))
                     .build()
                 );
                 let result = ${writer.format(generator.render())}().marshall(event);
@@ -187,7 +187,7 @@ class EventStreamMarshallerGeneratorTest {
                     .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64)))
                     .add_header(Header::new("short", HeaderValue::Int16(16_000i16)))
                     .add_header(Header::new("string", HeaderValue::String("test".into())))
-                    .add_header(Header::new("timestamp", HeaderValue::Timestamp(Instant::from_epoch_seconds(5))));
+                    .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5))));
                 assert_eq!(expected_message, actual_message);
                 """,
             )
diff --git a/design/src/smithy/simple_shapes.md b/design/src/smithy/simple_shapes.md
index 6a6be9962..171196d0d 100644
--- a/design/src/smithy/simple_shapes.md
+++ b/design/src/smithy/simple_shapes.md
@@ -12,7 +12,7 @@
 | double | `f64` |
 | [bigInteger](#big-numbers) | `BigInteger` (Not implemented yet) |
 | [bigDecimal](#big-numbers) | `BigDecimal` (Not implemented yet) |
-| [timestamp](#timestamps)  | [`Instant`](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-types/src/instant/mod.rs) |
+| [timestamp](#timestamps)  | [`DateTime`](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-types/src/date_time/mod.rs) |
 | [document](#documents) | [`Document`](https://github.com/awslabs/smithy-rs/blob/v0.14/rust-runtime/aws-smithy-types/src/lib.rs#L38-L52) |
 
 ### Big Numbers
@@ -27,13 +27,13 @@ This will enable us to add helpers over time as requested. Users will also be ab
 
 As of 5/23/2021 BigInteger / BigDecimal are not included in AWS models. Implementation is tracked [here](https://github.com/awslabs/smithy-rs/issues/312).
 ### Timestamps
-[chrono](https://github.com/chronotope/chrono) is the current de facto library for datetime in Rust, but it is pre-1.0. Instants are represented by an SDK defined structure modeled on `std::time::Duration` from the Rust standard library.
+[chrono](https://github.com/chronotope/chrono) is the current de facto library for datetime in Rust, but it is pre-1.0. DateTimes are represented by an SDK defined structure modeled on `std::time::Duration` from the Rust standard library.
 
 ```rust
-{{#include ../../../rust-runtime/aws-smithy-types/src/instant/mod.rs:instant}}
+{{#include ../../../rust-runtime/aws-smithy-types/src/date_time/mod.rs:date_time}}
 ```
 
-A `to_chrono()` method on `Instant` enables conversion from SDK instants to `chrono` dates.
+Functions in the `aws-smithy-types-convert` crate provide conversions to other crates, such as `time` or `chrono`.
 
 ### Strings
 Rust has two different String representations:
diff --git a/rust-runtime/Cargo.toml b/rust-runtime/Cargo.toml
index bce0a7421..54b111ca9 100644
--- a/rust-runtime/Cargo.toml
+++ b/rust-runtime/Cargo.toml
@@ -11,6 +11,7 @@ members = [
     "aws-smithy-protocol-test",
     "aws-smithy-query",
     "aws-smithy-types",
+    "aws-smithy-types-convert",
     "aws-smithy-xml",
     "aws-smithy-http-server"
 ]
diff --git a/rust-runtime/aws-smithy-eventstream/fuzz/fuzz_targets/mutated_headers.rs b/rust-runtime/aws-smithy-eventstream/fuzz/fuzz_targets/mutated_headers.rs
index 64c6737ff..ef5af62a3 100644
--- a/rust-runtime/aws-smithy-eventstream/fuzz/fuzz_targets/mutated_headers.rs
+++ b/rust-runtime/aws-smithy-eventstream/fuzz/fuzz_targets/mutated_headers.rs
@@ -6,7 +6,7 @@
 #![no_main]
 
 use aws_smithy_eventstream::frame::{Header, HeaderValue, Message};
-use aws_smithy_types::Instant;
+use aws_smithy_types::DateTime;
 use bytes::{Buf, BufMut};
 use crc32fast::Hasher as Crc;
 use libfuzzer_sys::{fuzz_mutator, fuzz_target};
@@ -35,7 +35,7 @@ fn mutate(data: &mut [u8], size: usize, max_size: usize) -> usize {
             .add_header(Header::new("str", HeaderValue::String("some str".into())))
             .add_header(Header::new(
                 "time",
-                HeaderValue::Timestamp(Instant::from_epoch_seconds(5_000_000_000)),
+                HeaderValue::Timestamp(DateTime::from_secs(5_000_000_000)),
             ))
             .add_header(Header::new(
                 "uuid",
diff --git a/rust-runtime/aws-smithy-eventstream/src/error.rs b/rust-runtime/aws-smithy-eventstream/src/error.rs
index 05911c384..023e8d511 100644
--- a/rust-runtime/aws-smithy-eventstream/src/error.rs
+++ b/rust-runtime/aws-smithy-eventstream/src/error.rs
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0.
  */
 
-use aws_smithy_types::Instant;
+use aws_smithy_types::DateTime;
 use std::error::Error as StdError;
 use std::fmt;
 
@@ -22,7 +22,7 @@ pub enum Error {
     MessageTooLong,
     PayloadTooLong,
     PreludeChecksumMismatch(u32, u32),
-    TimestampValueTooLarge(Instant),
+    TimestampValueTooLarge(DateTime),
     Marshalling(String),
     Unmarshalling(String),
 }
diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs
index 9d8fa3cca..9783d5635 100644
--- a/rust-runtime/aws-smithy-eventstream/src/frame.rs
+++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs
@@ -62,7 +62,7 @@ mod value {
     use crate::error::Error;
     use crate::frame::checked;
     use crate::str_bytes::StrBytes;
-    use aws_smithy_types::Instant;
+    use aws_smithy_types::DateTime;
     use bytes::{Buf, BufMut, Bytes};
     use std::convert::TryInto;
     use std::mem::size_of;
@@ -89,7 +89,7 @@ mod value {
         Int64(i64),
         ByteArray(Bytes),
         String(StrBytes),
-        Timestamp(Instant),
+        Timestamp(DateTime),
         Uuid(u128),
     }
 
@@ -143,7 +143,7 @@ mod value {
             }
         }
 
-        pub fn as_timestamp(&self) -> Result<Instant, &Self> {
+        pub fn as_timestamp(&self) -> Result<DateTime, &Self> {
             match self {
                 HeaderValue::Timestamp(value) => Ok(*value),
                 _ => Err(self),
@@ -199,9 +199,7 @@ mod value {
                 TYPE_TIMESTAMP => {
                     if buffer.remaining() >= size_of::<i64>() {
                         let epoch_millis = buffer.get_i64();
-                        Ok(HeaderValue::Timestamp(Instant::from_epoch_millis(
-                            epoch_millis,
-                        )))
+                        Ok(HeaderValue::Timestamp(DateTime::from_millis(epoch_millis)))
                     } else {
                         Err(Error::InvalidHeaderValue)
                     }
@@ -244,7 +242,7 @@ mod value {
                 Timestamp(time) => {
                     buffer.put_u8(TYPE_TIMESTAMP);
                     buffer.put_i64(
-                        time.to_epoch_millis()
+                        time.to_millis()
                             .map_err(|_| Error::TimestampValueTooLarge(*time))?,
                     );
                 }
@@ -273,7 +271,7 @@ mod value {
                 }
                 TYPE_STRING => HeaderValue::String(StrBytes::from(String::arbitrary(unstruct)?)),
                 TYPE_TIMESTAMP => {
-                    HeaderValue::Timestamp(Instant::from_epoch_seconds(i64::arbitrary(unstruct)?))
+                    HeaderValue::Timestamp(DateTime::from_secs(i64::arbitrary(unstruct)?))
                 }
                 TYPE_UUID => HeaderValue::Uuid(u128::arbitrary(unstruct)?),
                 _ => unreachable!(),
@@ -526,7 +524,7 @@ fn payload_len(total_len: u32, header_len: u32) -> Result<u32, Error> {
 mod message_tests {
     use crate::error::Error;
     use crate::frame::{Header, HeaderValue, Message};
-    use aws_smithy_types::Instant;
+    use aws_smithy_types::DateTime;
     use bytes::Bytes;
 
     macro_rules! read_message_expect_err {
@@ -639,7 +637,7 @@ mod message_tests {
                 Header::new("str", HeaderValue::String("some str".into())),
                 Header::new(
                     "time",
-                    HeaderValue::Timestamp(Instant::from_epoch_seconds(5_000_000))
+                    HeaderValue::Timestamp(DateTime::from_secs(5_000_000))
                 ),
                 Header::new(
                     "uuid",
@@ -667,7 +665,7 @@ mod message_tests {
             .add_header(Header::new("str", HeaderValue::String("some str".into())))
             .add_header(Header::new(
                 "time",
-                HeaderValue::Timestamp(Instant::from_epoch_seconds(5_000_000)),
+                HeaderValue::Timestamp(DateTime::from_secs(5_000_000)),
             ))
             .add_header(Header::new(
                 "uuid",
diff --git a/rust-runtime/aws-smithy-eventstream/src/smithy.rs b/rust-runtime/aws-smithy-eventstream/src/smithy.rs
index 53a867bb8..2ccaed072 100644
--- a/rust-runtime/aws-smithy-eventstream/src/smithy.rs
+++ b/rust-runtime/aws-smithy-eventstream/src/smithy.rs
@@ -6,7 +6,7 @@
 use crate::error::Error;
 use crate::frame::{Header, HeaderValue, Message};
 use crate::str_bytes::StrBytes;
-use aws_smithy_types::{Blob, Instant};
+use aws_smithy_types::{Blob, DateTime};
 
 macro_rules! expect_shape_fn {
     (fn $fn_name:ident[$val_typ:ident] -> $result_typ:ident { $val_name:ident -> $val_expr:expr }) => {
@@ -30,7 +30,7 @@ expect_shape_fn!(fn expect_int32[Int32] -> i32 { value -> *value });
 expect_shape_fn!(fn expect_int64[Int64] -> i64 { value -> *value });
 expect_shape_fn!(fn expect_byte_array[ByteArray] -> Blob { bytes -> Blob::new(bytes.as_ref()) });
 expect_shape_fn!(fn expect_string[String] -> String { value -> value.as_str().into() });
-expect_shape_fn!(fn expect_timestamp[Timestamp] -> Instant { value -> *value });
+expect_shape_fn!(fn expect_timestamp[Timestamp] -> DateTime { value -> *value });
 
 #[derive(Debug)]
 pub struct ResponseHeaders<'a> {
diff --git a/rust-runtime/aws-smithy-http/src/header.rs b/rust-runtime/aws-smithy-http/src/header.rs
index a59f2c5c3..bd3d42b2c 100644
--- a/rust-runtime/aws-smithy-http/src/header.rs
+++ b/rust-runtime/aws-smithy-http/src/header.rs
@@ -15,9 +15,9 @@ use std::str::FromStr;
 use http::header::{HeaderName, ValueIter};
 use http::HeaderValue;
 
-use aws_smithy_types::instant::Format;
+use aws_smithy_types::date_time::Format;
 use aws_smithy_types::primitive::Parse;
-use aws_smithy_types::Instant;
+use aws_smithy_types::DateTime;
 
 #[derive(Debug, Eq, PartialEq)]
 #[non_exhaustive]
@@ -52,19 +52,19 @@ impl Error for ParseError {}
 
 /// Read all the dates from the header map at `key` according the `format`
 ///
-/// This is separate from `read_many` below because we need to invoke `Instant::read` to take advantage
+/// This is separate from `read_many` below because we need to invoke `DateTime::read` to take advantage
 /// of comma-aware parsing
 pub fn many_dates(
     values: ValueIter<HeaderValue>,
     format: Format,
-) -> Result<Vec<Instant>, ParseError> {
+) -> Result<Vec<DateTime>, ParseError> {
     let mut out = vec![];
     for header in values {
         let mut header = header
             .to_str()
             .map_err(|_| ParseError::new_with_message("header was not valid utf-8 string"))?;
         while !header.is_empty() {
-            let (v, next) = Instant::read(header, format, ',').map_err(|err| {
+            let (v, next) = DateTime::read(header, format, ',').map_err(|err| {
                 ParseError::new_with_message(format!("header could not be parsed as date: {}", err))
             })?;
             out.push(v);
diff --git a/rust-runtime/aws-smithy-http/src/label.rs b/rust-runtime/aws-smithy-http/src/label.rs
index db15128ed..5b445f0a7 100644
--- a/rust-runtime/aws-smithy-http/src/label.rs
+++ b/rust-runtime/aws-smithy-http/src/label.rs
@@ -7,7 +7,8 @@
 //! [httpLabel](https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httplabel-trait)
 
 use crate::urlencode::BASE_SET;
-use aws_smithy_types::Instant;
+use aws_smithy_types::date_time::{DateTimeFormatError, Format};
+use aws_smithy_types::DateTime;
 use percent_encoding::AsciiSet;
 
 const GREEDY: &AsciiSet = &BASE_SET.remove(b'/');
@@ -17,8 +18,8 @@ pub fn fmt_string<T: AsRef<str>>(t: T, greedy: bool) -> String {
     percent_encoding::utf8_percent_encode(t.as_ref(), uri_set).to_string()
 }
 
-pub fn fmt_timestamp(t: &Instant, format: aws_smithy_types::instant::Format) -> String {
-    crate::query::fmt_string(t.fmt(format))
+pub fn fmt_timestamp(t: &DateTime, format: Format) -> Result<String, DateTimeFormatError> {
+    Ok(crate::query::fmt_string(t.fmt(format)?))
 }
 
 #[cfg(test)]
diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs
index 50a9c03b4..a30bb299e 100644
--- a/rust-runtime/aws-smithy-http/src/operation.rs
+++ b/rust-runtime/aws-smithy-http/src/operation.rs
@@ -5,6 +5,7 @@
 
 use crate::body::SdkBody;
 use crate::property_bag::{PropertyBag, SharedPropertyBag};
+use aws_smithy_types::date_time::DateTimeFormatError;
 use http::uri::InvalidUri;
 use std::borrow::Cow;
 use std::error::Error;
@@ -85,6 +86,12 @@ impl From<SerializationError> for BuildError {
     }
 }
 
+impl From<DateTimeFormatError> for BuildError {
+    fn from(err: DateTimeFormatError) -> Self {
+        BuildError::from(SerializationError::from(err))
+    }
+}
+
 impl Display for BuildError {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
@@ -126,6 +133,8 @@ impl Error for BuildError {
 pub enum SerializationError {
     #[non_exhaustive]
     CannotSerializeUnknownVariant { union: &'static str },
+    #[non_exhaustive]
+    DateTimeFormatError { cause: DateTimeFormatError },
 }
 
 impl SerializationError {
@@ -137,16 +146,26 @@ impl SerializationError {
 impl Display for SerializationError {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
-            SerializationError::CannotSerializeUnknownVariant { union } => write!(f, "Cannot serialize `{}::Unknown`.\
-             Unknown union variants cannot be serialized. This can occur when round-tripping a \
-             response from the server that was not recognized by the SDK. Consider upgrading to the \
-             latest version of the SDK.", union)
+            Self::CannotSerializeUnknownVariant { union } => write!(
+                f,
+                "Cannot serialize `{}::Unknown`. Unknown union variants cannot be serialized. \
+                This can occur when round-tripping a response from the server that was not \
+                recognized by the SDK. Consider upgrading to the latest version of the SDK.",
+                union
+            ),
+            Self::DateTimeFormatError { cause } => write!(f, "{}", cause),
         }
     }
 }
 
 impl Error for SerializationError {}
 
+impl From<DateTimeFormatError> for SerializationError {
+    fn from(err: DateTimeFormatError) -> SerializationError {
+        SerializationError::DateTimeFormatError { cause: err }
+    }
+}
+
 #[derive(Debug)]
 pub struct Operation<H, R> {
     request: Request,
diff --git a/rust-runtime/aws-smithy-http/src/query.rs b/rust-runtime/aws-smithy-http/src/query.rs
index ea5baa282..4a2f16069 100644
--- a/rust-runtime/aws-smithy-http/src/query.rs
+++ b/rust-runtime/aws-smithy-http/src/query.rs
@@ -3,18 +3,22 @@
  * SPDX-License-Identifier: Apache-2.0.
  */
 
+//! Utilities for writing Smithy values into a query string.
+//!
+//! Formatting values into the query string as specified in
+//! [httpQuery](https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httpquery-trait)
+
 use crate::urlencode::BASE_SET;
-/// Formatting values into the query string as specified in
-/// [httpQuery](https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httpquery-trait)
-use aws_smithy_types::Instant;
+use aws_smithy_types::date_time::{DateTimeFormatError, Format};
+use aws_smithy_types::DateTime;
 use percent_encoding::utf8_percent_encode;
 
 pub fn fmt_string<T: AsRef<str>>(t: T) -> String {
     utf8_percent_encode(t.as_ref(), BASE_SET).to_string()
 }
 
-pub fn fmt_timestamp(t: &Instant, format: aws_smithy_types::instant::Format) -> String {
-    fmt_string(t.fmt(format))
+pub fn fmt_timestamp(t: &DateTime, format: Format) -> Result<String, DateTimeFormatError> {
+    Ok(fmt_string(t.fmt(format)?))
 }
 
 /// Simple abstraction to enable appending params to a string as query params
diff --git a/rust-runtime/aws-smithy-json/src/deserialize/token.rs b/rust-runtime/aws-smithy-json/src/deserialize/token.rs
index 18eb5e9e6..915dd0e88 100644
--- a/rust-runtime/aws-smithy-json/src/deserialize/token.rs
+++ b/rust-runtime/aws-smithy-json/src/deserialize/token.rs
@@ -5,8 +5,8 @@
 
 use crate::deserialize::error::{Error, ErrorReason};
 use crate::escape::unescape_string;
-use aws_smithy_types::instant::Format;
-use aws_smithy_types::{base64, Blob, Document, Instant, Number};
+use aws_smithy_types::date_time::Format;
+use aws_smithy_types::{base64, Blob, DateTime, Document, Number};
 use std::borrow::Cow;
 
 use crate::deserialize::must_not_be_finite;
@@ -209,17 +209,17 @@ pub fn expect_blob_or_null(token: Option<Result<Token<'_>, Error>>) -> Result<Op
 
 /// Expects a [Token::ValueNull], [Token::ValueString], or [Token::ValueNumber] depending
 /// on the passed in `timestamp_format`. If there is a non-null value, it interprets it as an
-/// [Instant] in the requested format.
+/// [`DateTime` ] in the requested format.
 pub fn expect_timestamp_or_null(
     token: Option<Result<Token<'_>, Error>>,
     timestamp_format: Format,
-) -> Result<Option<Instant>, Error> {
+) -> Result<Option<DateTime>, Error> {
     Ok(match timestamp_format {
         Format::EpochSeconds => {
-            expect_number_or_null(token)?.map(|v| Instant::from_f64(v.to_f64()))
+            expect_number_or_null(token)?.map(|v| DateTime::from_secs_f64(v.to_f64()))
         }
         Format::DateTime | Format::HttpDate => expect_string_or_null(token)?
-            .map(|v| Instant::from_str(v.as_escaped_str(), timestamp_format))
+            .map(|v| DateTime::from_str(v.as_escaped_str(), timestamp_format))
             .transpose()
             .map_err(|err| {
                 Error::new(
@@ -578,18 +578,18 @@ pub mod test {
             expect_timestamp_or_null(value_null(0), Format::HttpDate)
         );
         assert_eq!(
-            Ok(Some(Instant::from_f64(2048.0))),
+            Ok(Some(DateTime::from_secs_f64(2048.0))),
             expect_timestamp_or_null(value_number(0, Number::Float(2048.0)), Format::EpochSeconds)
         );
         assert_eq!(
-            Ok(Some(Instant::from_f64(1445412480.0))),
+            Ok(Some(DateTime::from_secs_f64(1445412480.0))),
             expect_timestamp_or_null(
                 value_string(0, "Wed, 21 Oct 2015 07:28:00 GMT"),
                 Format::HttpDate
             )
         );
         assert_eq!(
-            Ok(Some(Instant::from_f64(1445412480.0))),
+            Ok(Some(DateTime::from_secs_f64(1445412480.0))),
             expect_timestamp_or_null(value_string(0, "2015-10-21T07:28:00Z"), Format::DateTime)
         );
         let err = Error::new(
diff --git a/rust-runtime/aws-smithy-json/src/serialize.rs b/rust-runtime/aws-smithy-json/src/serialize.rs
index 23c74aa87..affbccb25 100644
--- a/rust-runtime/aws-smithy-json/src/serialize.rs
+++ b/rust-runtime/aws-smithy-json/src/serialize.rs
@@ -4,9 +4,9 @@
  */
 
 use crate::escape::escape_string;
-use aws_smithy_types::instant::Format;
+use aws_smithy_types::date_time::{DateTimeFormatError, Format};
 use aws_smithy_types::primitive::Encoder;
-use aws_smithy_types::{Document, Instant, Number};
+use aws_smithy_types::{DateTime, Document, Number};
 use std::borrow::Cow;
 
 pub struct JsonValueWriter<'a> {
@@ -94,13 +94,18 @@ impl<'a> JsonValueWriter<'a> {
         }
     }
 
-    /// Writes an Instant `value` with the given `format`.
-    pub fn instant(self, instant: &Instant, format: Format) {
-        let formatted = instant.fmt(format);
+    /// Writes a date-time `value` with the given `format`.
+    pub fn date_time(
+        self,
+        date_time: &DateTime,
+        format: Format,
+    ) -> Result<(), DateTimeFormatError> {
+        let formatted = date_time.fmt(format)?;
         match format {
             Format::EpochSeconds => self.output.push_str(&formatted),
             _ => self.string(&formatted),
         }
+        Ok(())
     }
 
     /// Starts an array.
@@ -185,8 +190,8 @@ impl<'a> JsonArrayWriter<'a> {
 mod tests {
     use super::{JsonArrayWriter, JsonObjectWriter};
     use crate::serialize::JsonValueWriter;
-    use aws_smithy_types::instant::Format;
-    use aws_smithy_types::{Document, Instant, Number};
+    use aws_smithy_types::date_time::Format;
+    use aws_smithy_types::{DateTime, Document, Number};
     use proptest::proptest;
 
     #[test]
@@ -279,21 +284,28 @@ mod tests {
     }
 
     #[test]
-    fn object_instants() {
+    fn object_date_times() {
         let mut output = String::new();
 
         let mut object = JsonObjectWriter::new(&mut output);
         object
             .key("epoch_seconds")
-            .instant(&Instant::from_f64(5.2), Format::EpochSeconds);
-        object.key("date_time").instant(
-            &Instant::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
-            Format::DateTime,
-        );
-        object.key("http_date").instant(
-            &Instant::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
-            Format::HttpDate,
-        );
+            .date_time(&DateTime::from_secs_f64(5.2), Format::EpochSeconds)
+            .unwrap();
+        object
+            .key("date_time")
+            .date_time(
+                &DateTime::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
+                Format::DateTime,
+            )
+            .unwrap();
+        object
+            .key("http_date")
+            .date_time(
+                &DateTime::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
+                Format::HttpDate,
+            )
+            .unwrap();
         object.finish();
 
         assert_eq!(
@@ -303,21 +315,28 @@ mod tests {
     }
 
     #[test]
-    fn array_instants() {
+    fn array_date_times() {
         let mut output = String::new();
 
         let mut array = JsonArrayWriter::new(&mut output);
         array
             .value()
-            .instant(&Instant::from_f64(5.2), Format::EpochSeconds);
-        array.value().instant(
-            &Instant::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
-            Format::DateTime,
-        );
-        array.value().instant(
-            &Instant::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
-            Format::HttpDate,
-        );
+            .date_time(&DateTime::from_secs_f64(5.2), Format::EpochSeconds)
+            .unwrap();
+        array
+            .value()
+            .date_time(
+                &DateTime::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
+                Format::DateTime,
+            )
+            .unwrap();
+        array
+            .value()
+            .date_time(
+                &DateTime::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
+                Format::HttpDate,
+            )
+            .unwrap();
         array.finish();
 
         assert_eq!(
diff --git a/rust-runtime/aws-smithy-query/src/lib.rs b/rust-runtime/aws-smithy-query/src/lib.rs
index 4ea901b4e..0ba567b9a 100644
--- a/rust-runtime/aws-smithy-query/src/lib.rs
+++ b/rust-runtime/aws-smithy-query/src/lib.rs
@@ -5,9 +5,9 @@
 
 //! Abstractions for the Smithy AWS Query protocol
 
-use aws_smithy_types::instant::Format;
+use aws_smithy_types::date_time::{DateTimeFormatError, Format};
 use aws_smithy_types::primitive::Encoder;
-use aws_smithy_types::{Instant, Number};
+use aws_smithy_types::{DateTime, Number};
 use std::borrow::Cow;
 use urlencoding::encode;
 
@@ -178,9 +178,14 @@ impl<'a> QueryValueWriter<'a> {
         }
     }
 
-    /// Writes an Instant `value` with the given `format`.
-    pub fn instant(self, instant: &Instant, format: Format) {
-        self.string(&instant.fmt(format));
+    /// Writes a date-time `value` with the given `format`.
+    pub fn date_time(
+        self,
+        date_time: &DateTime,
+        format: Format,
+    ) -> Result<(), DateTimeFormatError> {
+        self.string(&date_time.fmt(format)?);
+        Ok(())
     }
 
     /// Starts a map.
@@ -208,8 +213,8 @@ impl<'a> QueryValueWriter<'a> {
 #[cfg(test)]
 mod tests {
     use crate::QueryWriter;
-    use aws_smithy_types::instant::Format;
-    use aws_smithy_types::{Instant, Number};
+    use aws_smithy_types::date_time::Format;
+    use aws_smithy_types::{DateTime, Number};
 
     #[test]
     fn no_params() {
@@ -327,15 +332,22 @@ mod tests {
 
         writer
             .prefix("epoch_seconds")
-            .instant(&Instant::from_f64(5.2), Format::EpochSeconds);
-        writer.prefix("date_time").instant(
-            &Instant::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
-            Format::DateTime,
-        );
-        writer.prefix("http_date").instant(
-            &Instant::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
-            Format::HttpDate,
-        );
+            .date_time(&DateTime::from_secs_f64(5.2), Format::EpochSeconds)
+            .unwrap();
+        writer
+            .prefix("date_time")
+            .date_time(
+                &DateTime::from_str("2021-05-24T15:34:50.123Z", Format::DateTime).unwrap(),
+                Format::DateTime,
+            )
+            .unwrap();
+        writer
+            .prefix("http_date")
+            .date_time(
+                &DateTime::from_str("Wed, 21 Oct 2015 07:28:00 GMT", Format::HttpDate).unwrap(),
+                Format::HttpDate,
+            )
+            .unwrap();
         writer.finish();
 
         assert_eq!(
diff --git a/rust-runtime/aws-smithy-types-convert/Cargo.toml b/rust-runtime/aws-smithy-types-convert/Cargo.toml
new file mode 100644
index 000000000..ff07823c6
--- /dev/null
+++ b/rust-runtime/aws-smithy-types-convert/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "aws-smithy-types-convert"
+version = "0.0.0-smithy-rs-head"
+authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
+description = "Conversion of types from aws-smithy-types to other libraries."
+edition = "2018"
+license = "Apache-2.0"
+repository = "https://github.com/awslabs/smithy-rs"
+
+[features]
+convert-chrono = ["chrono"]
+convert-time = ["time"]
+default = []
+
+[dependencies]
+aws-smithy-types = { path = "../aws-smithy-types" }
+chrono = { version = "0.4.19", optional = true }
+time = { version = "0.3.4", optional = true }
diff --git a/rust-runtime/aws-smithy-types-convert/LICENSE b/rust-runtime/aws-smithy-types-convert/LICENSE
new file mode 100644
index 000000000..67db85882
--- /dev/null
+++ b/rust-runtime/aws-smithy-types-convert/LICENSE
@@ -0,0 +1,175 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/rust-runtime/aws-smithy-types-convert/src/date_time.rs b/rust-runtime/aws-smithy-types-convert/src/date_time.rs
new file mode 100644
index 000000000..165718888
--- /dev/null
+++ b/rust-runtime/aws-smithy-types-convert/src/date_time.rs
@@ -0,0 +1,237 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+//! Conversions from [`DateTime`] to the types in the
+//! [`time`](https://crates.io/crates/time) or
+//! [`chrono`](https://crates.io/crates/chrono)
+//! crates.
+
+use aws_smithy_types::DateTime;
+use std::error::Error as StdError;
+use std::fmt;
+
+/// Conversion error
+#[non_exhaustive]
+#[derive(Debug)]
+pub enum Error {
+    /// Conversion failed because the value being converted is out of range for its destination
+    #[non_exhaustive]
+    OutOfRange(Box<dyn StdError + Send + Sync + 'static>),
+}
+
+impl StdError for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::OutOfRange(cause) => {
+                write!(
+                    f,
+                    "conversion failed because the value is out of range for its destination: {}",
+                    cause
+                )
+            }
+        }
+    }
+}
+
+/// Adds functions to [`DateTime`] to convert it to `time` or `chrono` types.
+///
+#[cfg_attr(
+    feature = "convert-time",
+    doc = r##"
+# Example with `time`
+
+Make sure your **Cargo.toml** enables the `convert-time` feature:
+```toml
+[dependencies]
+aws-smithy-types-convert = { version = "VERSION", features = ["convert-time"] }
+```
+
+Then import [`DateTimeExt`] to use the conversions:
+```rust
+# fn test_fn() -> Result<(), aws_smithy_types_convert::date_time::Error> {
+# use aws_smithy_types::DateTime;
+use aws_smithy_types_convert::date_time::DateTimeExt;
+use time::OffsetDateTime;
+
+let offset_date_time: OffsetDateTime = DateTime::from_secs(5).to_time()?;
+let date_time: DateTime  = DateTime::from_time(offset_date_time);
+# Ok(())
+# }
+```
+"##
+)]
+#[cfg_attr(
+    feature = "convert-chrono",
+    doc = r##"
+# Example with `chrono`
+
+Make sure your **Cargo.toml** enables the `convert-chrono` feature:
+```toml
+[dependencies]
+aws-smithy-types-convert = { version = "VERSION", features = ["convert-chrono"] }
+```
+
+Then import [`DateTimeExt`] to use the conversions:
+```rust
+# use aws_smithy_types::DateTime;
+use aws_smithy_types_convert::date_time::DateTimeExt;
+use chrono::{Utc};
+
+let chrono_date_time: chrono::DateTime<Utc> = DateTime::from_secs(5).to_chrono_utc();
+let date_time: DateTime = DateTime::from_chrono_utc(chrono_date_time);
+```
+"##
+)]
+pub trait DateTimeExt {
+    /// Converts a [`DateTime`] to a [`chrono::DateTime`] with timezone UTC.
+    #[cfg(feature = "convert-chrono")]
+    fn to_chrono_utc(&self) -> chrono::DateTime<chrono::Utc>;
+
+    /// Converts a [`chrono::DateTime`] with timezone UTC to a [`DateTime`].
+    #[cfg(feature = "convert-chrono")]
+    fn from_chrono_utc(time: chrono::DateTime<chrono::Utc>) -> DateTime;
+
+    /// Converts a [`chrono::DateTime`] with an offset timezone to a [`DateTime`].
+    #[cfg(feature = "convert-chrono")]
+    fn from_chrono_fixed(time: chrono::DateTime<chrono::FixedOffset>) -> DateTime;
+
+    /// Converts a [`DateTime`] to a [`time::OffsetDateTime`].
+    ///
+    /// Returns an [`Error::OutOfRange`] if the time is after
+    /// `9999-12-31T23:59:59.999Z` or before `-9999-01-01T00:00:00.000Z`.
+    #[cfg(feature = "convert-time")]
+    fn to_time(&self) -> Result<time::OffsetDateTime, Error>;
+
+    /// Converts a [`time::OffsetDateTime`] to a [`DateTime`].
+    #[cfg(feature = "convert-time")]
+    fn from_time(time: time::OffsetDateTime) -> DateTime;
+}
+
+impl DateTimeExt for DateTime {
+    #[cfg(feature = "convert-chrono")]
+    fn to_chrono_utc(&self) -> chrono::DateTime<chrono::Utc> {
+        chrono::DateTime::<chrono::Utc>::from_utc(
+            chrono::NaiveDateTime::from_timestamp(self.secs(), self.subsec_nanos()),
+            chrono::Utc,
+        )
+    }
+
+    #[cfg(feature = "convert-chrono")]
+    fn from_chrono_utc(value: chrono::DateTime<chrono::Utc>) -> DateTime {
+        DateTime::from_secs_and_nanos(value.timestamp(), value.timestamp_subsec_nanos())
+    }
+
+    #[cfg(feature = "convert-chrono")]
+    fn from_chrono_fixed(value: chrono::DateTime<chrono::FixedOffset>) -> DateTime {
+        Self::from_chrono_utc(value.with_timezone(&chrono::Utc))
+    }
+
+    #[cfg(feature = "convert-time")]
+    fn to_time(&self) -> Result<time::OffsetDateTime, Error> {
+        time::OffsetDateTime::from_unix_timestamp_nanos(self.as_nanos())
+            .map_err(|err| Error::OutOfRange(err.into()))
+    }
+
+    #[cfg(feature = "convert-time")]
+    fn from_time(time: time::OffsetDateTime) -> DateTime {
+        DateTime::from_nanos(time.unix_timestamp_nanos())
+            .expect("DateTime supports a greater range than OffsetDateTime")
+    }
+}
+
+#[cfg(all(test, any(feature = "convert-chrono", feature = "convert-time")))]
+mod test {
+    use super::DateTimeExt;
+    use aws_smithy_types::date_time::{DateTime, Format};
+
+    #[cfg(feature = "convert-time")]
+    use super::Error;
+
+    #[test]
+    #[cfg(feature = "convert-chrono")]
+    fn from_chrono() {
+        use chrono::{FixedOffset, TimeZone, Utc};
+
+        let chrono = Utc.ymd(2039, 7, 8).and_hms_nano(9, 3, 11, 123_000_000);
+        let expected = DateTime::from_str("2039-07-08T09:03:11.123Z", Format::DateTime).unwrap();
+        assert_eq!(expected, DateTime::from_chrono_utc(chrono));
+
+        let chrono = Utc.ymd(1000, 7, 8).and_hms_nano(9, 3, 11, 456_000_000);
+        let expected = DateTime::from_str("1000-07-08T09:03:11.456Z", Format::DateTime).unwrap();
+        assert_eq!(expected, DateTime::from_chrono_utc(chrono));
+
+        let chrono =
+            FixedOffset::west(2 * 3600)
+                .ymd(2039, 7, 8)
+                .and_hms_nano(9, 3, 11, 123_000_000);
+        let expected = DateTime::from_str("2039-07-08T11:03:11.123Z", Format::DateTime).unwrap();
+        assert_eq!(expected, DateTime::from_chrono_fixed(chrono));
+    }
+
+    #[test]
+    #[cfg(feature = "convert-chrono")]
+    fn to_chrono() {
+        use chrono::{TimeZone, Utc};
+
+        let date_time = DateTime::from_str("2039-07-08T09:03:11.123Z", Format::DateTime).unwrap();
+        let expected = Utc.ymd(2039, 7, 8).and_hms_nano(9, 3, 11, 123_000_000);
+        assert_eq!(expected, date_time.to_chrono_utc());
+
+        let date_time = DateTime::from_str("1000-07-08T09:03:11.456Z", Format::DateTime).unwrap();
+        let expected = Utc.ymd(1000, 7, 8).and_hms_nano(9, 3, 11, 456_000_000);
+        assert_eq!(expected, date_time.to_chrono_utc());
+    }
+
+    #[test]
+    #[cfg(feature = "convert-time")]
+    fn from_time() {
+        use time::{Date, Month, PrimitiveDateTime, Time};
+
+        let time = PrimitiveDateTime::new(
+            Date::from_calendar_date(2039, Month::July, 8).unwrap(),
+            Time::from_hms_milli(9, 3, 11, 123).unwrap(),
+        )
+        .assume_utc();
+        let expected = DateTime::from_str("2039-07-08T09:03:11.123Z", Format::DateTime).unwrap();
+        assert_eq!(expected, DateTime::from_time(time));
+
+        let time = PrimitiveDateTime::new(
+            Date::from_calendar_date(1000, Month::July, 8).unwrap(),
+            Time::from_hms_milli(9, 3, 11, 456).unwrap(),
+        )
+        .assume_utc();
+        let expected = DateTime::from_str("1000-07-08T09:03:11.456Z", Format::DateTime).unwrap();
+        assert_eq!(expected, DateTime::from_time(time));
+    }
+
+    #[test]
+    #[cfg(feature = "convert-time")]
+    fn to_time() {
+        use time::{Date, Month, PrimitiveDateTime, Time};
+
+        let date_time = DateTime::from_str("2039-07-08T09:03:11.123Z", Format::DateTime).unwrap();
+        let expected = PrimitiveDateTime::new(
+            Date::from_calendar_date(2039, Month::July, 8).unwrap(),
+            Time::from_hms_milli(9, 3, 11, 123).unwrap(),
+        )
+        .assume_utc();
+        assert_eq!(expected, date_time.to_time().unwrap());
+
+        let date_time = DateTime::from_str("1000-07-08T09:03:11.456Z", Format::DateTime).unwrap();
+        let expected = PrimitiveDateTime::new(
+            Date::from_calendar_date(1000, Month::July, 8).unwrap(),
+            Time::from_hms_milli(9, 3, 11, 456).unwrap(),
+        )
+        .assume_utc();
+        assert_eq!(expected, date_time.to_time().unwrap());
+
+        let date_time = DateTime::from_secs_and_nanos(i64::MAX, 0);
+        assert!(matches!(date_time.to_time(), Err(Error::OutOfRange(_))));
+        let date_time = DateTime::from_secs_and_nanos(i64::MIN, 0);
+        assert!(matches!(date_time.to_time(), Err(Error::OutOfRange(_))));
+    }
+}
diff --git a/rust-runtime/aws-smithy-types-convert/src/lib.rs b/rust-runtime/aws-smithy-types-convert/src/lib.rs
new file mode 100644
index 000000000..fffcfa252
--- /dev/null
+++ b/rust-runtime/aws-smithy-types-convert/src/lib.rs
@@ -0,0 +1,17 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+//! Conversions between `aws-smithy-types` and the types of frequently used Rust libraries.
+
+#![warn(
+    missing_docs,
+    missing_crate_level_docs,
+    missing_debug_implementations,
+    rust_2018_idioms,
+    unreachable_pub
+)]
+
+#[cfg(any(feature = "convert-time", feature = "convert-chrono"))]
+pub mod date_time;
diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml
index b7fa74ff8..2144c6975 100644
--- a/rust-runtime/aws-smithy-types/Cargo.toml
+++ b/rust-runtime/aws-smithy-types/Cargo.toml
@@ -7,19 +7,14 @@ edition = "2018"
 license = "Apache-2.0"
 repository = "https://github.com/awslabs/smithy-rs"
 
-[features]
-chrono-conversions = []
-default = ["chrono-conversions"]
-
 [dependencies]
-chrono = { version = "0.4", default-features = false, features = [] }
 itoa = "0.4.0"
 num-integer = "0.1"
 ryu = "1.0.5"
+time = { version = "0.3.4", features = ["parsing"] }
 
 [dev-dependencies]
 base64 = "0.13.0"
-chrono = { version = "0.4", default-features = false, features = ["alloc"] }
 lazy_static = "1.4"
 proptest = "1"
 serde = { version = "1", features = ["derive"] }
diff --git a/rust-runtime/aws-smithy-types/fuzz/Cargo.toml b/rust-runtime/aws-smithy-types/fuzz/Cargo.toml
index 3077f819b..1d37a074b 100644
--- a/rust-runtime/aws-smithy-types/fuzz/Cargo.toml
+++ b/rust-runtime/aws-smithy-types/fuzz/Cargo.toml
@@ -35,3 +35,15 @@ name = "parse_date_time"
 path = "fuzz_targets/parse_date_time.rs"
 test = false
 doc = false
+
+[[bin]]
+name = "read_date_time"
+path = "fuzz_targets/read_date_time.rs"
+test = false
+doc = false
+
+[[bin]]
+name = "read_http_date"
+path = "fuzz_targets/read_http_date.rs"
+test = false
+doc = false
diff --git a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_date_time.rs b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_date_time.rs
index 67014d4e3..9b5876f7e 100644
--- a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_date_time.rs
+++ b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_date_time.rs
@@ -5,12 +5,12 @@
 
 #![no_main]
 
-use aws_smithy_types::instant::{Format, Instant};
+use aws_smithy_types::date_time::{DateTime, Format};
 use libfuzzer_sys::fuzz_target;
 
 fuzz_target!(|data: &[u8]| {
     if let Ok(value) = std::str::from_utf8(data) {
         // Looking for panics. Don't care if the parsing fails.
-        let _ = Instant::from_str(value, Format::DateTime);
+        let _ = DateTime::from_str(value, Format::DateTime);
     }
 });
diff --git a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_epoch_seconds.rs b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_epoch_seconds.rs
index e24c04f95..276281796 100644
--- a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_epoch_seconds.rs
+++ b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_epoch_seconds.rs
@@ -5,12 +5,12 @@
 
 #![no_main]
 
-use aws_smithy_types::instant::{Format, Instant};
+use aws_smithy_types::date_time::{DateTime, Format};
 use libfuzzer_sys::fuzz_target;
 
 fuzz_target!(|data: &[u8]| {
     if let Ok(value) = std::str::from_utf8(data) {
         // Looking for panics. Don't care if the parsing fails.
-        let _ = Instant::from_str(value, Format::EpochSeconds);
+        let _ = DateTime::from_str(value, Format::EpochSeconds);
     }
 });
diff --git a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_http_date.rs b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_http_date.rs
index 661e37c55..ec1db862d 100644
--- a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_http_date.rs
+++ b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/parse_http_date.rs
@@ -5,12 +5,12 @@
 
 #![no_main]
 
-use aws_smithy_types::instant::{Format, Instant};
+use aws_smithy_types::date_time::{DateTime, Format};
 use libfuzzer_sys::fuzz_target;
 
 fuzz_target!(|data: &[u8]| {
     if let Ok(value) = std::str::from_utf8(data) {
         // Looking for panics. Don't care if the parsing fails.
-        let _ = Instant::from_str(value, Format::HttpDate);
+        let _ = DateTime::from_str(value, Format::HttpDate);
     }
 });
diff --git a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_date_time.rs b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_date_time.rs
new file mode 100644
index 000000000..70cbc69b2
--- /dev/null
+++ b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_date_time.rs
@@ -0,0 +1,18 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#![no_main]
+
+use aws_smithy_types::date_time::{DateTime, Format};
+use libfuzzer_sys::fuzz_target;
+
+fuzz_target!(|data: &[u8]| {
+    if let Ok(mut value) = std::str::from_utf8(data) {
+        // Looking for panics. Don't care if the parsing fails.
+        while let Ok((_, next)) = DateTime::read(value, Format::DateTime, ',') {
+            value = next;
+        }
+    }
+});
diff --git a/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_http_date.rs b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_http_date.rs
new file mode 100644
index 000000000..6879d2767
--- /dev/null
+++ b/rust-runtime/aws-smithy-types/fuzz/fuzz_targets/read_http_date.rs
@@ -0,0 +1,18 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#![no_main]
+
+use aws_smithy_types::date_time::{DateTime, Format};
+use libfuzzer_sys::fuzz_target;
+
+fuzz_target!(|data: &[u8]| {
+    if let Ok(mut value) = std::str::from_utf8(data) {
+        // Looking for panics. Don't care if the parsing fails.
+        while let Ok((_, next)) = DateTime::read(value, Format::HttpDate, ',') {
+            value = next;
+        }
+    }
+});
diff --git a/rust-runtime/aws-smithy-types/proptest-regressions/instant/format.txt b/rust-runtime/aws-smithy-types/proptest-regressions/instant/format.txt
new file mode 100644
index 000000000..409c1ce38
--- /dev/null
+++ b/rust-runtime/aws-smithy-types/proptest-regressions/instant/format.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 274da3290b70eec94751bb4ebb152160811daea25f46211ebf54bba47bd3a2e6 # shrinks to secs = -1, nanos = 2
diff --git a/rust-runtime/aws-smithy-types/src/instant/format.rs b/rust-runtime/aws-smithy-types/src/date_time/format.rs
similarity index 54%
rename from rust-runtime/aws-smithy-types/src/instant/format.rs
rename to rust-runtime/aws-smithy-types/src/date_time/format.rs
index 1fc9a24b5..df2a3239f 100644
--- a/rust-runtime/aws-smithy-types/src/instant/format.rs
+++ b/rust-runtime/aws-smithy-types/src/date_time/format.rs
@@ -3,30 +3,59 @@
  * SPDX-License-Identifier: Apache-2.0.
  */
 
+use std::borrow::Cow;
 use std::error::Error;
 use std::fmt;
 
 const NANOS_PER_SECOND: u32 = 1_000_000_000;
 
+/// Error returned when date-time parsing fails.
 #[non_exhaustive]
-#[derive(Debug, Eq, PartialEq)]
-pub enum DateParseError {
-    Invalid(&'static str),
+#[derive(Debug)]
+pub enum DateTimeParseError {
+    /// The given date-time string was invalid.
+    #[non_exhaustive]
+    Invalid(Cow<'static, str>),
+    /// Failed to parse an integer inside the given date-time string.
+    #[non_exhaustive]
     IntParseError,
 }
 
-impl Error for DateParseError {}
+impl Error for DateTimeParseError {}
 
-impl fmt::Display for DateParseError {
+impl fmt::Display for DateTimeParseError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        use DateParseError::*;
+        use DateTimeParseError::*;
         match self {
-            Invalid(msg) => write!(f, "invalid date: {}", msg),
+            Invalid(msg) => write!(f, "invalid date-time: {}", msg),
             IntParseError => write!(f, "failed to parse int"),
         }
     }
 }
 
+/// Error returned when date-time formatting fails.
+#[non_exhaustive]
+#[derive(Debug)]
+pub enum DateTimeFormatError {
+    /// The given date-time cannot be represented in the requested date format.
+    #[non_exhaustive]
+    OutOfRange(Cow<'static, str>),
+}
+
+impl Error for DateTimeFormatError {}
+
+impl fmt::Display for DateTimeFormatError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::OutOfRange(msg) => write!(
+                f,
+                "date-time cannot be formatted since it is out of range: {}",
+                msg
+            ),
+        }
+    }
+}
+
 fn remove_trailing_zeros(string: &mut String) {
     while let Some(b'0') = string.as_bytes().last() {
         string.pop();
@@ -35,101 +64,117 @@ fn remove_trailing_zeros(string: &mut String) {
 
 pub(crate) mod epoch_seconds {
     use super::remove_trailing_zeros;
-    use super::DateParseError;
-    use crate::Instant;
+    use super::DateTimeParseError;
+    use crate::DateTime;
     use std::str::FromStr;
 
-    /// Formats an `Instant` into the Smithy epoch seconds date-time format.
-    pub(crate) fn format(instant: &Instant) -> String {
-        if instant.subsecond_nanos == 0 {
-            format!("{}", instant.seconds)
+    /// Formats a `DateTime` into the Smithy epoch seconds date-time format.
+    pub(crate) fn format(date_time: &DateTime) -> String {
+        if date_time.subsecond_nanos == 0 {
+            format!("{}", date_time.seconds)
         } else {
-            let mut result = format!("{}.{:0>9}", instant.seconds, instant.subsecond_nanos);
+            let mut result = format!("{}.{:0>9}", date_time.seconds, date_time.subsecond_nanos);
             remove_trailing_zeros(&mut result);
             result
         }
     }
 
-    /// Parses the Smithy epoch seconds date-time format into an `Instant`.
-    pub(crate) fn parse(value: &str) -> Result<Instant, DateParseError> {
+    /// Parses the Smithy epoch seconds date-time format into a `DateTime`.
+    pub(crate) fn parse(value: &str) -> Result<DateTime, DateTimeParseError> {
         let mut parts = value.splitn(2, '.');
         let (mut whole, mut decimal) = (0i64, 0u32);
         if let Some(whole_str) = parts.next() {
-            whole = <i64>::from_str(whole_str).map_err(|_| DateParseError::IntParseError)?;
+            whole = <i64>::from_str(whole_str).map_err(|_| DateTimeParseError::IntParseError)?;
         }
         if let Some(decimal_str) = parts.next() {
             if decimal_str.starts_with('+') || decimal_str.starts_with('-') {
-                return Err(DateParseError::Invalid("invalid epoch-seconds timestamp"));
+                return Err(DateTimeParseError::Invalid(
+                    "invalid epoch-seconds timestamp".into(),
+                ));
             }
             if decimal_str.len() > 9 {
-                return Err(DateParseError::Invalid("decimal is longer than 9 digits"));
+                return Err(DateTimeParseError::Invalid(
+                    "decimal is longer than 9 digits".into(),
+                ));
             }
             let missing_places = 9 - decimal_str.len() as isize;
-            decimal = <u32>::from_str(decimal_str).map_err(|_| DateParseError::IntParseError)?;
+            decimal =
+                <u32>::from_str(decimal_str).map_err(|_| DateTimeParseError::IntParseError)?;
             for _ in 0..missing_places {
                 decimal *= 10;
             }
         }
-        Ok(Instant::from_secs_and_nanos(whole, decimal))
+        Ok(DateTime::from_secs_and_nanos(whole, decimal))
     }
 }
 
 pub(crate) mod http_date {
     use super::remove_trailing_zeros;
-    use crate::instant::format::{DateParseError, NANOS_PER_SECOND};
-    use crate::Instant;
-    use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Weekday};
+    use crate::date_time::format::{DateTimeFormatError, DateTimeParseError, NANOS_PER_SECOND};
+    use crate::DateTime;
     use std::str::FromStr;
+    use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
 
     // This code is taken from https://github.com/pyfisch/httpdate and modified under an
     // Apache 2.0 License. Modifications:
     // - Removed use of unsafe
     // - Add serialization and deserialization of subsecond nanos
     //
-    /// Format an `instant` in the HTTP date format (imf-fixdate) with added support for subsecond precision
+    /// Format a `DateTime` in the HTTP date format (imf-fixdate) with added support for subsecond precision
     ///
     /// Example: "Mon, 16 Dec 2019 23:48:18 GMT"
     ///
     /// Some notes:
-    /// - HTTP date does not support years before `0000`—this will cause a panic.
+    /// - HTTP date does not support years before `0001`—this will cause a panic.
     /// - If you _don't_ want subsecond precision (e.g. if you want strict adherence to the spec),
-    ///   you need to zero-out the instant before formatting
+    ///   you need to zero-out the date-time before formatting
     /// - If subsecond nanos are 0, no fractional seconds are added
     /// - If subsecond nanos are nonzero, 3 digits of fractional seconds are added
-    pub(crate) fn format(instant: &Instant) -> String {
-        let structured = instant.to_chrono_internal();
+    pub(crate) fn format(date_time: &DateTime) -> Result<String, DateTimeFormatError> {
+        fn out_of_range<E: std::fmt::Display>(cause: E) -> DateTimeFormatError {
+            DateTimeFormatError::OutOfRange(
+                format!(
+                    "HTTP dates support dates between Mon, 01 Jan 0001 00:00:00 GMT \
+                            and Fri, 31 Dec 9999 23:59:59.999 GMT. {}",
+                    cause
+                )
+                .into(),
+            )
+        }
+        let structured = OffsetDateTime::from_unix_timestamp_nanos(date_time.as_nanos())
+            .map_err(out_of_range)?;
         let weekday = match structured.weekday() {
-            Weekday::Mon => "Mon",
-            Weekday::Tue => "Tue",
-            Weekday::Wed => "Wed",
-            Weekday::Thu => "Thu",
-            Weekday::Fri => "Fri",
-            Weekday::Sat => "Sat",
-            Weekday::Sun => "Sun",
+            Weekday::Monday => "Mon",
+            Weekday::Tuesday => "Tue",
+            Weekday::Wednesday => "Wed",
+            Weekday::Thursday => "Thu",
+            Weekday::Friday => "Fri",
+            Weekday::Saturday => "Sat",
+            Weekday::Sunday => "Sun",
         };
         let month = match structured.month() {
-            1 => "Jan",
-            2 => "Feb",
-            3 => "Mar",
-            4 => "Apr",
-            5 => "May",
-            6 => "Jun",
-            7 => "Jul",
-            8 => "Aug",
-            9 => "Sep",
-            10 => "Oct",
-            11 => "Nov",
-            12 => "Dec",
-            _ => unreachable!(),
+            Month::January => "Jan",
+            Month::February => "Feb",
+            Month::March => "Mar",
+            Month::April => "Apr",
+            Month::May => "May",
+            Month::June => "Jun",
+            Month::July => "Jul",
+            Month::August => "Aug",
+            Month::September => "Sep",
+            Month::October => "Oct",
+            Month::November => "Nov",
+            Month::December => "Dec",
         };
         let mut out = String::with_capacity(32);
         fn push_digit(out: &mut String, digit: u8) {
+            debug_assert!(digit < 10);
             out.push((b'0' + digit as u8) as char);
         }
 
         out.push_str(weekday);
         out.push_str(", ");
-        let day = structured.date().day() as u8;
+        let day = structured.day();
         push_digit(&mut out, day / 10);
         push_digit(&mut out, day % 10);
 
@@ -139,10 +184,9 @@ pub(crate) mod http_date {
         out.push(' ');
 
         let year = structured.year();
-        // Although chrono can handle extremely early years, HTTP date does not support
-        // years before 0000
-        let year = if year < 0 {
-            panic!("negative years not supported")
+        // HTTP date does not support years before 0001
+        let year = if year < 1 {
+            return Err(out_of_range("HTTP dates cannot be before the year 0001"));
         } else {
             year as u32
         };
@@ -155,7 +199,7 @@ pub(crate) mod http_date {
 
         out.push(' ');
 
-        let hour = structured.time().hour() as u8;
+        let hour = structured.hour();
 
         // Extract the individual digits from hour
         push_digit(&mut out, hour / 10);
@@ -164,32 +208,31 @@ pub(crate) mod http_date {
         out.push(':');
 
         // Extract the individual digits from minute
-        let minute = structured.minute() as u8;
+        let minute = structured.minute();
         push_digit(&mut out, minute / 10);
         push_digit(&mut out, minute % 10);
 
         out.push(':');
 
-        let second = structured.second() as u8;
+        let second = structured.second();
         push_digit(&mut out, second / 10);
         push_digit(&mut out, second % 10);
 
         // If non-zero nanos, push a 3-digit fractional second
-        let nanos = structured.timestamp_subsec_nanos();
-        if nanos / (NANOS_PER_SECOND / 1000) != 0 {
+        let millis = structured.millisecond();
+        if millis != 0 {
             out.push('.');
-            push_digit(&mut out, (nanos / (NANOS_PER_SECOND / 10)) as u8);
-            push_digit(&mut out, (nanos / (NANOS_PER_SECOND / 100) % 10) as u8);
-            push_digit(&mut out, (nanos / (NANOS_PER_SECOND / 1000) % 10) as u8);
+            push_digit(&mut out, (millis / 100 % 10) as u8);
+            push_digit(&mut out, (millis / 10 % 10) as u8);
+            push_digit(&mut out, (millis % 10) as u8);
             remove_trailing_zeros(&mut out);
         }
 
         out.push_str(" GMT");
-
-        out
+        Ok(out)
     }
 
-    /// Parse an IMF-fixdate formatted date into an Instant
+    /// Parse an IMF-fixdate formatted date into a DateTime
     ///
     /// This function has a few caveats:
     /// 1. It DOES NOT support the "deprecated" formats supported by HTTP date
@@ -199,23 +242,27 @@ pub(crate) mod http_date {
     /// Ok: "Mon, 16 Dec 2019 23:48:18.123 GMT"
     /// Ok: "Mon, 16 Dec 2019 23:48:18.12 GMT"
     /// Not Ok: "Mon, 16 Dec 2019 23:48:18.1234 GMT"
-    pub(crate) fn parse(s: &str) -> Result<Instant, DateParseError> {
+    pub(crate) fn parse(s: &str) -> Result<DateTime, DateTimeParseError> {
         if !s.is_ascii() {
-            return Err(DateParseError::Invalid("not ascii"));
+            return Err(DateTimeParseError::Invalid(
+                "date-time must be ASCII".into(),
+            ));
         }
         let x = s.trim().as_bytes();
         parse_imf_fixdate(x)
     }
 
-    pub(crate) fn read(s: &str) -> Result<(Instant, &str), DateParseError> {
+    pub(crate) fn read(s: &str) -> Result<(DateTime, &str), DateTimeParseError> {
         if !s.is_ascii() {
-            return Err(DateParseError::Invalid("Date must be valid ascii"));
+            return Err(DateTimeParseError::Invalid(
+                "date-time must be ASCII".into(),
+            ));
         }
         let (first_date, rest) = match find_subsequence(s.as_bytes(), b" GMT") {
             // split_at is correct because we asserted that this date is only valid ASCII so the byte index is
             // the same as the char index
             Some(idx) => s.split_at(idx),
-            None => return Err(DateParseError::Invalid("Date did not end in GMT")),
+            None => return Err(DateTimeParseError::Invalid("date-time is not GMT".into())),
         };
         Ok((parse(first_date)?, rest))
     }
@@ -227,7 +274,7 @@ pub(crate) mod http_date {
             .map(|idx| idx + needle.len())
     }
 
-    fn parse_imf_fixdate(s: &[u8]) -> Result<Instant, DateParseError> {
+    fn parse_imf_fixdate(s: &[u8]) -> Result<DateTime, DateTimeParseError> {
         // Example: `Sun, 06 Nov 1994 08:49:37 GMT`
         if s.len() < 29
             || s.len() > 33
@@ -236,7 +283,9 @@ pub(crate) mod http_date {
             || s[19] != b':'
             || s[22] != b':'
         {
-            return Err(DateParseError::Invalid("incorrectly shaped string"));
+            return Err(DateTimeParseError::Invalid(
+                "incorrectly shaped string".into(),
+            ));
         }
         let nanos: u32 = match &s[25] {
             b'.' => {
@@ -245,7 +294,9 @@ pub(crate) mod http_date {
                 let fraction_slice = &s[26..s.len() - 4];
                 if fraction_slice.len() > 3 {
                     // Only thousandths are supported
-                    return Err(DateParseError::Invalid("too much precision"));
+                    return Err(DateTimeParseError::Invalid(
+                        "Smithy http-date only supports millisecond precision".into(),
+                    ));
                 }
                 let fraction: u32 = parse_slice(fraction_slice)?;
                 // We need to convert the fractional second to nanoseconds, so we need to scale
@@ -254,41 +305,55 @@ pub(crate) mod http_date {
                 fraction * (NANOS_PER_SECOND / multiplier[fraction_slice.len() - 1])
             }
             b' ' => 0,
-            _ => return Err(DateParseError::Invalid("incorrectly shaped string")),
+            _ => {
+                return Err(DateTimeParseError::Invalid(
+                    "incorrectly shaped string".into(),
+                ))
+            }
         };
 
         let hours = parse_slice(&s[17..19])?;
-
         let minutes = parse_slice(&s[20..22])?;
         let seconds = parse_slice(&s[23..25])?;
-        let time = NaiveTime::from_hms_nano(hours, minutes, seconds, nanos);
+        let time = Time::from_hms_nano(hours, minutes, seconds, nanos).map_err(|err| {
+            DateTimeParseError::Invalid(format!("time components are out of range: {}", err).into())
+        })?;
+
         let month = match &s[7..12] {
-            b" Jan " => 1,
-            b" Feb " => 2,
-            b" Mar " => 3,
-            b" Apr " => 4,
-            b" May " => 5,
-            b" Jun " => 6,
-            b" Jul " => 7,
-            b" Aug " => 8,
-            b" Sep " => 9,
-            b" Oct " => 10,
-            b" Nov " => 11,
-            b" Dec " => 12,
-            _ => return Err(DateParseError::Invalid("invalid month")),
+            b" Jan " => Month::January,
+            b" Feb " => Month::February,
+            b" Mar " => Month::March,
+            b" Apr " => Month::April,
+            b" May " => Month::May,
+            b" Jun " => Month::June,
+            b" Jul " => Month::July,
+            b" Aug " => Month::August,
+            b" Sep " => Month::September,
+            b" Oct " => Month::October,
+            b" Nov " => Month::November,
+            b" Dec " => Month::December,
+            month => {
+                return Err(DateTimeParseError::Invalid(
+                    format!(
+                        "invalid month: {}",
+                        std::str::from_utf8(month).unwrap_or_default()
+                    )
+                    .into(),
+                ))
+            }
         };
         let year = parse_slice(&s[12..16])?;
         let day = parse_slice(&s[5..7])?;
-        let date = NaiveDate::from_ymd(year, month, day);
-        let datetime = NaiveDateTime::new(date, time);
+        let date = Date::from_calendar_date(year, month, day).map_err(|err| {
+            DateTimeParseError::Invalid(format!("date components are out of range: {}", err).into())
+        })?;
+        let date_time = PrimitiveDateTime::new(date, time).assume_offset(UtcOffset::UTC);
 
-        Ok(Instant::from_secs_and_nanos(
-            datetime.timestamp(),
-            datetime.timestamp_subsec_nanos(),
-        ))
+        Ok(DateTime::from_nanos(date_time.unix_timestamp_nanos())
+            .expect("this date format cannot produce out of range date-times"))
     }
 
-    fn parse_slice<T>(ascii_slice: &[u8]) -> Result<T, DateParseError>
+    fn parse_slice<T>(ascii_slice: &[u8]) -> Result<T, DateTimeParseError>
     where
         T: FromStr,
     {
@@ -296,66 +361,68 @@ pub(crate) mod http_date {
             std::str::from_utf8(ascii_slice).expect("should only be called on ascii strings");
         as_str
             .parse::<T>()
-            .map_err(|_| DateParseError::IntParseError)
+            .map_err(|_| DateTimeParseError::IntParseError)
     }
 }
 
 pub(crate) mod rfc3339 {
-    use chrono::format;
-
-    use crate::instant::format::DateParseError;
-    use crate::Instant;
-    use chrono::{Datelike, Timelike};
+    use crate::date_time::format::{DateTimeFormatError, DateTimeParseError};
+    use crate::DateTime;
+    use time::format_description::well_known::Rfc3339;
+    use time::OffsetDateTime;
 
     // OK: 1985-04-12T23:20:50.52Z
     // OK: 1985-04-12T23:20:50Z
     //
     // Timezones not supported:
     // Not OK: 1985-04-12T23:20:50-02:00
-    pub(crate) fn parse(s: &str) -> Result<Instant, DateParseError> {
-        let mut date = format::Parsed::new();
-        let format = format::StrftimeItems::new("%Y-%m-%dT%H:%M:%S%.fZ");
-        // TODO: it may be helpful for debugging to keep these errors around
-        chrono::format::parse(&mut date, s, format)
-            .map_err(|_| DateParseError::Invalid("invalid rfc3339 date"))?;
-        let utc_date = date
-            .to_naive_datetime_with_offset(0)
-            .map_err(|_| DateParseError::Invalid("invalid date"))?;
-        Ok(Instant::from_secs_and_nanos(
-            utc_date.timestamp(),
-            utc_date.timestamp_subsec_nanos(),
-        ))
+    pub(crate) fn parse(s: &str) -> Result<DateTime, DateTimeParseError> {
+        let date_time = OffsetDateTime::parse(s, &Rfc3339).map_err(|err| {
+            DateTimeParseError::Invalid(format!("invalid RFC-3339 date-time: {}", err).into())
+        })?;
+        Ok(DateTime::from_nanos(date_time.unix_timestamp_nanos())
+            .expect("this date format cannot produce out of range date-times"))
     }
 
     /// Read 1 RFC-3339 date from &str and return the remaining str
-    pub(crate) fn read(s: &str) -> Result<(Instant, &str), DateParseError> {
+    pub(crate) fn read(s: &str) -> Result<(DateTime, &str), DateTimeParseError> {
         let delim = s.find('Z').map(|idx| idx + 1).unwrap_or_else(|| s.len());
         let (head, rest) = s.split_at(delim);
         Ok((parse(head)?, rest))
     }
 
-    /// Format an [Instant] in the RFC-3339 date format
-    pub(crate) fn format(instant: &Instant) -> String {
+    /// Format a [DateTime] in the RFC-3339 date format
+    pub(crate) fn format(date_time: &DateTime) -> Result<String, DateTimeFormatError> {
         use std::fmt::Write;
-        let (year, month, day, hour, minute, second, nanos) = {
-            let s = instant.to_chrono_internal();
+        fn out_of_range<E: std::fmt::Display>(cause: E) -> DateTimeFormatError {
+            DateTimeFormatError::OutOfRange(
+                format!(
+                    "RFC-3339 timestamps support dates between 0001-01-01T00:00:00.000Z \
+                            and 9999-12-31T23:59:59.999Z. {}",
+                    cause
+                )
+                .into(),
+            )
+        }
+        let (year, month, day, hour, minute, second, micros) = {
+            let s = OffsetDateTime::from_unix_timestamp_nanos(date_time.as_nanos())
+                .map_err(out_of_range)?;
             (
                 s.year(),
-                s.month(),
+                u8::from(s.month()),
                 s.day(),
-                s.time().hour(),
-                s.time().minute(),
-                s.time().second(),
-                s.timestamp_subsec_nanos(),
+                s.hour(),
+                s.minute(),
+                s.second(),
+                s.microsecond(),
             )
         };
 
         // This is stated in the assumptions for RFC-3339. ISO-8601 allows for years
         // between -99,999 and 99,999 inclusive, but RFC-3339 is bound between 0 and 9,999.
-        assert!(
-            (0..=9_999).contains(&year),
-            "years must be between 0 and 9,999 in RFC-3339"
-        );
+        if !(1..=9_999).contains(&year) {
+            return Err(out_of_range(""));
+        }
 
         let mut out = String::with_capacity(33);
         write!(
@@ -364,17 +431,15 @@ pub(crate) mod rfc3339 {
             year, month, day, hour, minute, second
         )
         .unwrap();
-        format_subsecond_fraction(&mut out, nanos);
+        format_subsecond_fraction(&mut out, micros);
         out.push('Z');
-        out
+        Ok(out)
     }
 
     /// Formats sub-second fraction for RFC-3339 (including the '.').
-    /// Expects to be called with a number of `nanos` between 0 and 999_999_999 inclusive.
-    /// The formatted fraction will be truncated to microseconds.
-    fn format_subsecond_fraction(into: &mut String, nanos: u32) {
-        debug_assert!(nanos < 1_000_000_000);
-        let micros = nanos / 1000;
+    /// Expects to be called with a number of `micros` between 0 and 999_999 inclusive.
+    fn format_subsecond_fraction(into: &mut String, micros: u32) {
+        debug_assert!(micros < 1_000_000);
         if micros > 0 {
             into.push('.');
             let (mut remaining, mut place) = (micros, 100_000);
@@ -391,7 +456,7 @@ pub(crate) mod rfc3339 {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::Instant;
+    use crate::DateTime;
     use lazy_static::lazy_static;
     use proptest::prelude::*;
     use std::fs::File;
@@ -407,8 +472,8 @@ mod tests {
         smithy_format_value: Option<String>,
     }
     impl TestCase {
-        fn time(&self) -> Instant {
-            Instant::from_secs_and_nanos(
+        fn time(&self) -> DateTime {
+            DateTime::from_secs_and_nanos(
                 <i64>::from_str(&self.canonical_seconds).unwrap(),
                 self.canonical_nanos,
             )
@@ -438,7 +503,7 @@ mod tests {
 
     fn format_test<F>(test_cases: &[TestCase], format: F)
     where
-        F: Fn(&Instant) -> String,
+        F: Fn(&DateTime) -> String,
     {
         for test_case in test_cases {
             if let Some(expected) = test_case.smithy_format_value.as_ref() {
@@ -452,7 +517,7 @@ mod tests {
 
     fn parse_test<F>(test_cases: &[TestCase], parse: F)
     where
-        F: Fn(&str) -> Result<Instant, DateParseError>,
+        F: Fn(&str) -> Result<DateTime, DateTimeParseError>,
     {
         for test_case in test_cases {
             let expected = test_case.time();
@@ -490,7 +555,10 @@ mod tests {
 
     #[test]
     fn format_http_date() {
-        format_test(&TEST_CASES.format_http_date, http_date::format);
+        fn do_format(date_time: &DateTime) -> String {
+            http_date::format(date_time).unwrap()
+        }
+        format_test(&TEST_CASES.format_http_date, do_format);
     }
 
     #[test]
@@ -498,9 +566,33 @@ mod tests {
         parse_test(&TEST_CASES.parse_http_date, http_date::parse);
     }
 
+    #[test]
+    fn date_time_out_of_range() {
+        assert_eq!(
+            "0001-01-01T00:00:00Z",
+            rfc3339::format(&DateTime::from_secs(-62_135_596_800)).unwrap()
+        );
+        assert_eq!(
+            "9999-12-31T23:59:59.999999Z",
+            rfc3339::format(&DateTime::from_secs_and_nanos(253402300799, 999_999_999)).unwrap()
+        );
+
+        assert!(matches!(
+            rfc3339::format(&DateTime::from_secs(-62_135_596_800 - 1)),
+            Err(DateTimeFormatError::OutOfRange(_))
+        ));
+        assert!(matches!(
+            rfc3339::format(&DateTime::from_secs(253402300799 + 1)),
+            Err(DateTimeFormatError::OutOfRange(_))
+        ));
+    }
+
     #[test]
     fn format_date_time() {
-        format_test(&TEST_CASES.format_date_time, rfc3339::format);
+        fn do_format(date_time: &DateTime) -> String {
+            rfc3339::format(date_time).unwrap()
+        }
+        format_test(&TEST_CASES.format_date_time, do_format);
     }
 
     #[test]
@@ -530,35 +622,56 @@ mod tests {
         let (e2, date2) = rfc3339::read(&date[1..]).expect("should succeed");
         assert_eq!(date2, "");
         assert_eq!(date, ",1985-04-12T23:20:51Z");
-        let expected = Instant::from_secs_and_nanos(482196050, 0);
+        let expected = DateTime::from_secs_and_nanos(482196050, 0);
         assert_eq!(e1, expected);
-        let expected = Instant::from_secs_and_nanos(482196051, 0);
+        let expected = DateTime::from_secs_and_nanos(482196051, 0);
         assert_eq!(e2, expected);
     }
 
+    #[test]
+    fn http_date_out_of_range() {
+        assert_eq!(
+            "Mon, 01 Jan 0001 00:00:00 GMT",
+            http_date::format(&DateTime::from_secs(-62_135_596_800)).unwrap()
+        );
+        assert_eq!(
+            "Fri, 31 Dec 9999 23:59:59.999 GMT",
+            http_date::format(&DateTime::from_secs_and_nanos(253402300799, 999_999_999)).unwrap()
+        );
+
+        assert!(matches!(
+            http_date::format(&DateTime::from_secs(-62_135_596_800 - 1)),
+            Err(DateTimeFormatError::OutOfRange(_))
+        ));
+        assert!(matches!(
+            http_date::format(&DateTime::from_secs(253402300799 + 1)),
+            Err(DateTimeFormatError::OutOfRange(_))
+        ));
+    }
+
     #[test]
     fn http_date_too_much_fraction() {
         let fractional = "Mon, 16 Dec 2019 23:48:18.1212 GMT";
-        assert_eq!(
+        assert!(matches!(
             http_date::parse(fractional),
-            Err(DateParseError::Invalid("incorrectly shaped string"))
-        );
+            Err(DateTimeParseError::Invalid(_))
+        ));
     }
 
     #[test]
     fn http_date_bad_fraction() {
         let fractional = "Mon, 16 Dec 2019 23:48:18. GMT";
-        assert_eq!(
+        assert!(matches!(
             http_date::parse(fractional),
-            Err(DateParseError::IntParseError)
-        );
+            Err(DateTimeParseError::IntParseError)
+        ));
     }
 
     #[test]
     fn http_date_read_date() {
         let fractional = "Mon, 16 Dec 2019 23:48:18.123 GMT,some more stuff";
         let ts = 1576540098;
-        let expected = Instant::from_fractional_seconds(ts, 0.123);
+        let expected = DateTime::from_fractional_secs(ts, 0.123);
         let (actual, rest) = http_date::read(fractional).expect("valid");
         assert_eq!(rest, ",some more stuff");
         assert_eq!(expected, actual);
@@ -567,8 +680,8 @@ mod tests {
 
     #[track_caller]
     fn http_date_check_roundtrip(epoch_secs: i64, subsecond_nanos: u32) {
-        let instant = Instant::from_secs_and_nanos(epoch_secs, subsecond_nanos);
-        let formatted = http_date::format(&instant);
+        let date_time = DateTime::from_secs_and_nanos(epoch_secs, subsecond_nanos);
+        let formatted = http_date::format(&date_time).unwrap();
         let parsed = http_date::parse(&formatted);
         let read = http_date::read(&formatted);
         match parsed {
@@ -576,9 +689,9 @@ mod tests {
             Ok(date) => {
                 assert!(read.is_ok());
                 if date.subsecond_nanos != subsecond_nanos {
-                    assert_eq!(http_date::format(&instant), formatted);
+                    assert_eq!(http_date::format(&date_time).unwrap(), formatted);
                 } else {
-                    assert_eq!(date, instant)
+                    assert_eq!(date, date_time)
                 }
             }
         }
diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs
new file mode 100644
index 000000000..1c66c9f11
--- /dev/null
+++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs
@@ -0,0 +1,546 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+//! DateTime type for representing Smithy timestamps.
+
+use num_integer::div_mod_floor;
+use num_integer::Integer;
+use std::convert::TryFrom;
+use std::error::Error as StdError;
+use std::fmt;
+use std::time::Duration;
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+mod format;
+pub use self::format::DateTimeFormatError;
+pub use self::format::DateTimeParseError;
+
+const MILLIS_PER_SECOND: i64 = 1000;
+const NANOS_PER_MILLI: u32 = 1_000_000;
+const NANOS_PER_SECOND: i128 = 1_000_000_000;
+const NANOS_PER_SECOND_U32: u32 = 1_000_000_000;
+
+/* ANCHOR: date_time */
+
+/// DateTime in time.
+///
+/// DateTime in time represented as seconds and sub-second nanos since
+/// the Unix epoch (January 1, 1970 at midnight UTC/GMT).
+///
+/// This type can be converted to/from the standard library's [`SystemTime`](std::time::SystemTime):
+/// ```rust
+/// # fn doc_fn() -> Result<(), aws_smithy_types::date_time::ConversionError> {
+/// # use aws_smithy_types::date_time::DateTime;
+/// # use std::time::SystemTime;
+/// use std::convert::TryFrom;
+///
+/// let the_millennium_as_system_time = SystemTime::try_from(DateTime::from_secs(946_713_600))?;
+/// let now_as_date_time = DateTime::from(SystemTime::now());
+/// # Ok(())
+/// # }
+/// ```
+///
+/// The [`aws-smithy-types-convert`](https://crates.io/crates/aws-smithy-types-convert) crate
+/// can be used for conversions to/from other libraries, such as
+/// [`time`](https://crates.io/crates/time) or [`chrono`](https://crates.io/crates/chrono).
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct DateTime {
+    seconds: i64,
+    subsecond_nanos: u32,
+}
+
+/* ANCHOR_END: date_time */
+
+impl DateTime {
+    /// Creates a `DateTime` from a number of seconds since the Unix epoch.
+    pub fn from_secs(epoch_seconds: i64) -> Self {
+        DateTime {
+            seconds: epoch_seconds,
+            subsecond_nanos: 0,
+        }
+    }
+
+    /// Creates a `DateTime` from a number of milliseconds since the Unix epoch.
+    pub fn from_millis(epoch_millis: i64) -> DateTime {
+        let (seconds, millis) = div_mod_floor(epoch_millis, MILLIS_PER_SECOND);
+        DateTime::from_secs_and_nanos(seconds, millis as u32 * NANOS_PER_MILLI)
+    }
+
+    /// Creates a `DateTime` from a number of nanoseconds since the Unix epoch.
+    pub fn from_nanos(epoch_nanos: i128) -> Result<Self, ConversionError> {
+        let (seconds, subsecond_nanos) = epoch_nanos.div_mod_floor(&NANOS_PER_SECOND);
+        let seconds = i64::try_from(seconds).map_err(|_| {
+            ConversionError("given epoch nanos are too large to fit into a DateTime")
+        })?;
+        let subsecond_nanos = subsecond_nanos as u32; // safe cast because of the modulus
+        Ok(DateTime {
+            seconds,
+            subsecond_nanos,
+        })
+    }
+
+    /// Returns the number of nanoseconds since the Unix epoch that this `DateTime` represents.
+    pub fn as_nanos(&self) -> i128 {
+        let seconds = self.seconds as i128 * NANOS_PER_SECOND;
+        if seconds < 0 {
+            let adjusted_nanos = self.subsecond_nanos as i128 - NANOS_PER_SECOND;
+            seconds + NANOS_PER_SECOND + adjusted_nanos
+        } else {
+            seconds + self.subsecond_nanos as i128
+        }
+    }
+
+    /// Creates a `DateTime` from a number of seconds and a fractional second since the Unix epoch.
+    ///
+    /// # Example
+    /// ```
+    /// # use aws_smithy_types::DateTime;
+    /// assert_eq!(
+    ///     DateTime::from_secs_and_nanos(1, 500_000_000u32),
+    ///     DateTime::from_fractional_secs(1, 0.5),
+    /// );
+    /// ```
+    pub fn from_fractional_secs(epoch_seconds: i64, fraction: f64) -> Self {
+        let subsecond_nanos = (fraction * 1_000_000_000_f64) as u32;
+        DateTime::from_secs_and_nanos(epoch_seconds, subsecond_nanos)
+    }
+
+    /// Creates a `DateTime` from a number of seconds and sub-second nanos since the Unix epoch.
+    ///
+    /// # Example
+    /// ```
+    /// # use aws_smithy_types::DateTime;
+    /// assert_eq!(
+    ///     DateTime::from_fractional_secs(1, 0.5),
+    ///     DateTime::from_secs_and_nanos(1, 500_000_000u32),
+    /// );
+    /// ```
+    pub fn from_secs_and_nanos(seconds: i64, subsecond_nanos: u32) -> Self {
+        if subsecond_nanos >= 1_000_000_000 {
+            panic!("{} is > 1_000_000_000", subsecond_nanos)
+        }
+        DateTime {
+            seconds,
+            subsecond_nanos,
+        }
+    }
+
+    /// Returns the `DateTime` value as an `f64` representing the seconds since the Unix epoch.
+    ///
+    /// _Note: This conversion will lose precision due to the nature of floating point numbers._
+    pub fn as_secs_f64(&self) -> f64 {
+        self.seconds as f64 + self.subsecond_nanos as f64 / 1_000_000_000_f64
+    }
+
+    /// Creates a `DateTime` from an `f64` representing the number of seconds since the Unix epoch.
+    ///
+    /// # Example
+    /// ```
+    /// # use aws_smithy_types::DateTime;
+    /// assert_eq!(
+    ///     DateTime::from_fractional_secs(1, 0.5),
+    ///     DateTime::from_secs_f64(1.5),
+    /// );
+    /// ```
+    pub fn from_secs_f64(epoch_seconds: f64) -> Self {
+        let seconds = epoch_seconds.floor() as i64;
+        let rem = epoch_seconds - epoch_seconds.floor();
+        DateTime::from_fractional_secs(seconds, rem)
+    }
+
+    /// Parses a `DateTime` from a string using the given `format`.
+    pub fn from_str(s: &str, format: Format) -> Result<Self, DateTimeParseError> {
+        match format {
+            Format::DateTime => format::rfc3339::parse(s),
+            Format::HttpDate => format::http_date::parse(s),
+            Format::EpochSeconds => format::epoch_seconds::parse(s),
+        }
+    }
+
+    /// Returns true if sub-second nanos is greater than zero.
+    pub fn has_subsec_nanos(&self) -> bool {
+        self.subsecond_nanos != 0
+    }
+
+    /// Returns the epoch seconds component of the `DateTime`.
+    ///
+    /// _Note: this does not include the sub-second nanos._
+    pub fn secs(&self) -> i64 {
+        self.seconds
+    }
+
+    /// Returns the sub-second nanos component of the `DateTime`.
+    ///
+    /// _Note: this does not include the number of seconds since the epoch._
+    pub fn subsec_nanos(&self) -> u32 {
+        self.subsecond_nanos
+    }
+
+    /// Converts the `DateTime` to the number of milliseconds since the Unix epoch.
+    ///
+    /// This is fallible since `DateTime` holds more precision than an `i64`, and will
+    /// return a `ConversionError` for `DateTime` values that can't be converted.
+    pub fn to_millis(self) -> Result<i64, ConversionError> {
+        let subsec_millis =
+            Integer::div_floor(&i64::from(self.subsecond_nanos), &(NANOS_PER_MILLI as i64));
+        if self.seconds < 0 {
+            self.seconds
+                .checked_add(1)
+                .and_then(|seconds| seconds.checked_mul(MILLIS_PER_SECOND))
+                .and_then(|millis| millis.checked_sub(1000 - subsec_millis))
+        } else {
+            self.seconds
+                .checked_mul(MILLIS_PER_SECOND)
+                .and_then(|millis| millis.checked_add(subsec_millis))
+        }
+        .ok_or(ConversionError(
+            "DateTime value too large to fit into i64 epoch millis",
+        ))
+    }
+
+    /// Read 1 date of `format` from `s`, expecting either `delim` or EOF
+    ///
+    /// Enable parsing multiple dates from the same string
+    pub fn read(s: &str, format: Format, delim: char) -> Result<(Self, &str), DateTimeParseError> {
+        let (inst, next) = match format {
+            Format::DateTime => format::rfc3339::read(s)?,
+            Format::HttpDate => format::http_date::read(s)?,
+            Format::EpochSeconds => {
+                let split_point = s.find(delim).unwrap_or_else(|| s.len());
+                let (s, rest) = s.split_at(split_point);
+                (Self::from_str(s, format)?, rest)
+            }
+        };
+        if next.is_empty() {
+            Ok((inst, next))
+        } else if next.starts_with(delim) {
+            Ok((inst, &next[1..]))
+        } else {
+            Err(DateTimeParseError::Invalid(
+                "didn't find expected delimiter".into(),
+            ))
+        }
+    }
+
+    /// Formats the `DateTime` to a string using the given `format`.
+    ///
+    /// Returns an error if the given `DateTime` cannot be represented by the desired format.
+    pub fn fmt(&self, format: Format) -> Result<String, DateTimeFormatError> {
+        match format {
+            Format::DateTime => format::rfc3339::format(self),
+            Format::EpochSeconds => Ok(format::epoch_seconds::format(self)),
+            Format::HttpDate => format::http_date::format(self),
+        }
+    }
+}
+
+/// Tries to convert a [`DateTime`] into a [`SystemTime`].
+///
+/// This can fail if the the `DateTime` value is larger or smaller than what the `SystemTime`
+/// can represent on the operating system it's compiled for. On Linux, for example, it will only
+/// fail on `Instant::from_secs(i64::MIN)` (with any nanoseconds value). On Windows, however,
+/// Rust's standard library uses a smaller precision type for `SystemTime`, and it will fail
+/// conversion for a much larger range of date-times. This is only an issue if dealing with
+/// date-times beyond several thousands of years from now.
+impl TryFrom<DateTime> for SystemTime {
+    type Error = ConversionError;
+
+    fn try_from(date_time: DateTime) -> Result<Self, Self::Error> {
+        if date_time.secs() < 0 {
+            let mut secs = date_time.secs().unsigned_abs();
+            let mut nanos = date_time.subsec_nanos();
+            if date_time.has_subsec_nanos() {
+                // This is safe because we just went from a negative number to a positive and are subtracting
+                secs -= 1;
+                // This is safe because nanos are < 999,999,999
+                nanos = NANOS_PER_SECOND_U32 - nanos;
+            }
+            UNIX_EPOCH
+                .checked_sub(Duration::new(secs, nanos))
+                .ok_or(ConversionError(
+                    "overflow occurred when subtracting duration from UNIX_EPOCH",
+                ))
+        } else {
+            UNIX_EPOCH
+                .checked_add(Duration::new(
+                    date_time.secs().unsigned_abs(),
+                    date_time.subsec_nanos(),
+                ))
+                .ok_or(ConversionError(
+                    "overflow occurred when adding duration to UNIX_EPOCH",
+                ))
+        }
+    }
+}
+
+impl From<SystemTime> for DateTime {
+    fn from(time: SystemTime) -> Self {
+        if time < UNIX_EPOCH {
+            let duration = UNIX_EPOCH.duration_since(time).expect("time < UNIX_EPOCH");
+            let mut secs = -(duration.as_secs() as i128);
+            let mut nanos = duration.subsec_nanos() as i128;
+            if nanos != 0 {
+                secs -= 1;
+                nanos = NANOS_PER_SECOND - nanos;
+            }
+            DateTime::from_nanos(secs * NANOS_PER_SECOND + nanos)
+                .expect("SystemTime has same precision as DateTime")
+        } else {
+            let duration = time.duration_since(UNIX_EPOCH).expect("UNIX_EPOCH <= time");
+            DateTime::from_secs_and_nanos(
+                i64::try_from(duration.as_secs())
+                    .expect("SystemTime has same precision as DateTime"),
+                duration.subsec_nanos(),
+            )
+        }
+    }
+}
+
+/// Failure to convert a `DateTime` to or from another type.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct ConversionError(&'static str);
+
+impl StdError for ConversionError {}
+
+impl fmt::Display for ConversionError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+/// Formats for representing a `DateTime` in the Smithy protocols.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Format {
+    /// RFC-3339 Date Time.
+    DateTime,
+    /// Date format used by the HTTP `Date` header, specified in RFC-7231.
+    HttpDate,
+    /// Number of seconds since the Unix epoch formatted as a floating point.
+    EpochSeconds,
+}
+
+#[cfg(test)]
+mod test {
+    use crate::date_time::Format;
+    use crate::DateTime;
+    use std::convert::TryFrom;
+    use std::time::SystemTime;
+    use time::format_description::well_known::Rfc3339;
+    use time::OffsetDateTime;
+
+    #[test]
+    fn test_fmt() {
+        let date_time = DateTime::from_secs(1576540098);
+        assert_eq!(
+            date_time.fmt(Format::DateTime).unwrap(),
+            "2019-12-16T23:48:18Z"
+        );
+        assert_eq!(date_time.fmt(Format::EpochSeconds).unwrap(), "1576540098");
+        assert_eq!(
+            date_time.fmt(Format::HttpDate).unwrap(),
+            "Mon, 16 Dec 2019 23:48:18 GMT"
+        );
+
+        let date_time = DateTime::from_fractional_secs(1576540098, 0.52);
+        assert_eq!(
+            date_time.fmt(Format::DateTime).unwrap(),
+            "2019-12-16T23:48:18.52Z"
+        );
+        assert_eq!(
+            date_time.fmt(Format::EpochSeconds).unwrap(),
+            "1576540098.52"
+        );
+        assert_eq!(
+            date_time.fmt(Format::HttpDate).unwrap(),
+            "Mon, 16 Dec 2019 23:48:18.52 GMT"
+        );
+    }
+
+    #[test]
+    fn test_fmt_zero_seconds() {
+        let date_time = DateTime::from_secs(1576540080);
+        assert_eq!(
+            date_time.fmt(Format::DateTime).unwrap(),
+            "2019-12-16T23:48:00Z"
+        );
+        assert_eq!(date_time.fmt(Format::EpochSeconds).unwrap(), "1576540080");
+        assert_eq!(
+            date_time.fmt(Format::HttpDate).unwrap(),
+            "Mon, 16 Dec 2019 23:48:00 GMT"
+        );
+    }
+
+    #[test]
+    fn test_read_single_http_date() {
+        let s = "Mon, 16 Dec 2019 23:48:18 GMT";
+        let (_, next) = DateTime::read(s, Format::HttpDate, ',').expect("valid");
+        assert_eq!(next, "");
+    }
+
+    #[test]
+    fn test_read_single_float() {
+        let s = "1576540098.52";
+        let (_, next) = DateTime::read(s, Format::EpochSeconds, ',').expect("valid");
+        assert_eq!(next, "");
+    }
+
+    #[test]
+    fn test_read_many_float() {
+        let s = "1576540098.52,1576540098.53";
+        let (_, next) = DateTime::read(s, Format::EpochSeconds, ',').expect("valid");
+        assert_eq!(next, "1576540098.53");
+    }
+
+    #[test]
+    fn test_ready_many_http_date() {
+        let s = "Mon, 16 Dec 2019 23:48:18 GMT,Tue, 17 Dec 2019 23:48:18 GMT";
+        let (_, next) = DateTime::read(s, Format::HttpDate, ',').expect("valid");
+        assert_eq!(next, "Tue, 17 Dec 2019 23:48:18 GMT");
+    }
+
+    #[derive(Debug)]
+    struct EpochMillisTestCase {
+        rfc3339: &'static str,
+        epoch_millis: i64,
+        epoch_seconds: i64,
+        epoch_subsec_nanos: u32,
+    }
+
+    // These test case values were generated from the following Kotlin JVM code:
+    // ```kotlin
+    // val date_time = DateTime.ofEpochMilli(<epoch milli value>);
+    // println(DateTimeFormatter.ISO_DATE_TIME.format(date_time.atOffset(ZoneOffset.UTC)))
+    // println(date_time.epochSecond)
+    // println(date_time.nano)
+    // ```
+    const EPOCH_MILLIS_TEST_CASES: &[EpochMillisTestCase] = &[
+        EpochMillisTestCase {
+            rfc3339: "2021-07-30T21:20:04.123Z",
+            epoch_millis: 1627680004123,
+            epoch_seconds: 1627680004,
+            epoch_subsec_nanos: 123000000,
+        },
+        EpochMillisTestCase {
+            rfc3339: "1918-06-04T02:39:55.877Z",
+            epoch_millis: -1627680004123,
+            epoch_seconds: -1627680005,
+            epoch_subsec_nanos: 877000000,
+        },
+        EpochMillisTestCase {
+            rfc3339: "+292278994-08-17T07:12:55.807Z",
+            epoch_millis: i64::MAX,
+            epoch_seconds: 9223372036854775,
+            epoch_subsec_nanos: 807000000,
+        },
+        EpochMillisTestCase {
+            rfc3339: "-292275055-05-16T16:47:04.192Z",
+            epoch_millis: i64::MIN,
+            epoch_seconds: -9223372036854776,
+            epoch_subsec_nanos: 192000000,
+        },
+    ];
+
+    #[test]
+    fn to_millis() {
+        for test_case in EPOCH_MILLIS_TEST_CASES {
+            println!("Test case: {:?}", test_case);
+            let date_time = DateTime::from_secs_and_nanos(
+                test_case.epoch_seconds,
+                test_case.epoch_subsec_nanos,
+            );
+            assert_eq!(test_case.epoch_seconds, date_time.secs());
+            assert_eq!(test_case.epoch_subsec_nanos, date_time.subsec_nanos());
+            assert_eq!(test_case.epoch_millis, date_time.to_millis().unwrap());
+        }
+
+        assert!(DateTime::from_secs_and_nanos(i64::MAX, 0)
+            .to_millis()
+            .is_err());
+    }
+
+    #[test]
+    fn from_millis() {
+        for test_case in EPOCH_MILLIS_TEST_CASES {
+            println!("Test case: {:?}", test_case);
+            let date_time = DateTime::from_millis(test_case.epoch_millis);
+            assert_eq!(test_case.epoch_seconds, date_time.secs());
+            assert_eq!(test_case.epoch_subsec_nanos, date_time.subsec_nanos());
+        }
+    }
+
+    #[test]
+    fn to_from_millis_round_trip() {
+        for millis in &[0, 1627680004123, -1627680004123, i64::MAX, i64::MIN] {
+            assert_eq!(*millis, DateTime::from_millis(*millis).to_millis().unwrap());
+        }
+    }
+
+    #[test]
+    fn as_nanos() {
+        assert_eq!(
+            -9_223_372_036_854_775_807_000_000_001_i128,
+            DateTime::from_secs_and_nanos(i64::MIN, 999_999_999).as_nanos()
+        );
+        assert_eq!(
+            -10_876_543_211,
+            DateTime::from_secs_and_nanos(-11, 123_456_789).as_nanos()
+        );
+        assert_eq!(0, DateTime::from_secs_and_nanos(0, 0).as_nanos());
+        assert_eq!(
+            11_123_456_789,
+            DateTime::from_secs_and_nanos(11, 123_456_789).as_nanos()
+        );
+        assert_eq!(
+            9_223_372_036_854_775_807_999_999_999_i128,
+            DateTime::from_secs_and_nanos(i64::MAX, 999_999_999).as_nanos()
+        );
+    }
+
+    #[test]
+    fn from_nanos() {
+        assert_eq!(
+            DateTime::from_secs_and_nanos(i64::MIN, 999_999_999),
+            DateTime::from_nanos(-9_223_372_036_854_775_807_000_000_001_i128).unwrap(),
+        );
+        assert_eq!(
+            DateTime::from_secs_and_nanos(-11, 123_456_789),
+            DateTime::from_nanos(-10_876_543_211).unwrap(),
+        );
+        assert_eq!(
+            DateTime::from_secs_and_nanos(0, 0),
+            DateTime::from_nanos(0).unwrap(),
+        );
+        assert_eq!(
+            DateTime::from_secs_and_nanos(11, 123_456_789),
+            DateTime::from_nanos(11_123_456_789).unwrap(),
+        );
+        assert_eq!(
+            DateTime::from_secs_and_nanos(i64::MAX, 999_999_999),
+            DateTime::from_nanos(9_223_372_036_854_775_807_999_999_999_i128).unwrap(),
+        );
+        assert!(DateTime::from_nanos(-10_000_000_000_000_000_000_999_999_999_i128).is_err());
+        assert!(DateTime::from_nanos(10_000_000_000_000_000_000_999_999_999_i128).is_err());
+    }
+
+    #[test]
+    fn system_time_conversions() {
+        // Check agreement
+        let date_time = DateTime::from_str("1000-01-02T01:23:10.123Z", Format::DateTime).unwrap();
+        let off_date_time = OffsetDateTime::parse("1000-01-02T01:23:10.123Z", &Rfc3339).unwrap();
+        assert_eq!(
+            SystemTime::from(off_date_time),
+            SystemTime::try_from(date_time).unwrap()
+        );
+
+        let date_time = DateTime::from_str("2039-10-31T23:23:10.456Z", Format::DateTime).unwrap();
+        let off_date_time = OffsetDateTime::parse("2039-10-31T23:23:10.456Z", &Rfc3339).unwrap();
+        assert_eq!(
+            SystemTime::from(off_date_time),
+            SystemTime::try_from(date_time).unwrap()
+        );
+    }
+}
diff --git a/rust-runtime/aws-smithy-types/src/instant/mod.rs b/rust-runtime/aws-smithy-types/src/instant/mod.rs
deleted file mode 100644
index dc13bc149..000000000
--- a/rust-runtime/aws-smithy-types/src/instant/mod.rs
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
-
-//! Instant value for representing Smithy timestamps.
-//!
-//! Unlike [`std::time::Instant`], this instant is not opaque. The time inside of it can be
-//! read and modified. It also holds logic for parsing and formatting timestamps in any of
-//! the timestamp formats that [Smithy](https://awslabs.github.io/smithy/) supports.
-
-use crate::instant::format::DateParseError;
-use chrono::{DateTime, NaiveDateTime, Utc};
-use num_integer::div_mod_floor;
-use num_integer::Integer;
-use std::error::Error as StdError;
-use std::fmt;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
-
-mod format;
-
-const MILLIS_PER_SECOND: i64 = 1000;
-const NANOS_PER_MILLI: u32 = 1_000_000;
-
-/* ANCHOR: instant */
-
-/// Instant in time.
-///
-/// Instant in time represented as seconds and sub-second nanos since
-/// the Unix epoch (January 1, 1970 at midnight UTC/GMT).
-#[derive(Debug, PartialEq, Clone, Copy)]
-pub struct Instant {
-    seconds: i64,
-    subsecond_nanos: u32,
-}
-
-/* ANCHOR_END: instant */
-
-impl Instant {
-    /// Creates an `Instant` from a number of seconds since the Unix epoch.
-    pub fn from_epoch_seconds(epoch_seconds: i64) -> Self {
-        Instant {
-            seconds: epoch_seconds,
-            subsecond_nanos: 0,
-        }
-    }
-
-    /// Creates an `Instant` from a number of seconds and a fractional second since the Unix epoch.
-    ///
-    /// # Example
-    /// ```
-    /// # use aws_smithy_types::Instant;
-    /// assert_eq!(
-    ///     Instant::from_secs_and_nanos(1, 500_000_000u32),
-    ///     Instant::from_fractional_seconds(1, 0.5),
-    /// );
-    /// ```
-    pub fn from_fractional_seconds(epoch_seconds: i64, fraction: f64) -> Self {
-        let subsecond_nanos = (fraction * 1_000_000_000_f64) as u32;
-        Instant::from_secs_and_nanos(epoch_seconds, subsecond_nanos)
-    }
-
-    /// Creates an `Instant` from a number of seconds and sub-second nanos since the Unix epoch.
-    ///
-    /// # Example
-    /// ```
-    /// # use aws_smithy_types::Instant;
-    /// assert_eq!(
-    ///     Instant::from_fractional_seconds(1, 0.5),
-    ///     Instant::from_secs_and_nanos(1, 500_000_000u32),
-    /// );
-    /// ```
-    pub fn from_secs_and_nanos(seconds: i64, subsecond_nanos: u32) -> Self {
-        if subsecond_nanos >= 1_000_000_000 {
-            panic!("{} is > 1_000_000_000", subsecond_nanos)
-        }
-        Instant {
-            seconds,
-            subsecond_nanos,
-        }
-    }
-
-    /// Creates an `Instant` from an `f64` representing the number of seconds since the Unix epoch.
-    ///
-    /// # Example
-    /// ```
-    /// # use aws_smithy_types::Instant;
-    /// assert_eq!(
-    ///     Instant::from_fractional_seconds(1, 0.5),
-    ///     Instant::from_f64(1.5),
-    /// );
-    /// ```
-    pub fn from_f64(epoch_seconds: f64) -> Self {
-        let seconds = epoch_seconds.floor() as i64;
-        let rem = epoch_seconds - epoch_seconds.floor();
-        Instant::from_fractional_seconds(seconds, rem)
-    }
-
-    /// Creates an `Instant` from a [`SystemTime`].
-    pub fn from_system_time(system_time: SystemTime) -> Self {
-        let duration = system_time
-            .duration_since(UNIX_EPOCH)
-            .expect("SystemTime can never represent a time before the Unix Epoch");
-        Instant {
-            seconds: duration.as_secs() as i64,
-            subsecond_nanos: duration.subsec_nanos(),
-        }
-    }
-
-    /// Parses an `Instant` from a string using the given `format`.
-    pub fn from_str(s: &str, format: Format) -> Result<Self, DateParseError> {
-        match format {
-            Format::DateTime => format::rfc3339::parse(s),
-            Format::HttpDate => format::http_date::parse(s),
-            Format::EpochSeconds => format::epoch_seconds::parse(s),
-        }
-    }
-
-    /// Read 1 date of `format` from `s`, expecting either `delim` or EOF
-    ///
-    /// Enable parsing multiple dates from the same string
-    pub fn read(s: &str, format: Format, delim: char) -> Result<(Self, &str), DateParseError> {
-        let (inst, next) = match format {
-            Format::DateTime => format::rfc3339::read(s)?,
-            Format::HttpDate => format::http_date::read(s)?,
-            Format::EpochSeconds => {
-                let split_point = s.find(delim).unwrap_or_else(|| s.len());
-                let (s, rest) = s.split_at(split_point);
-                (Self::from_str(s, format)?, rest)
-            }
-        };
-        if next.is_empty() {
-            Ok((inst, next))
-        } else if next.starts_with(delim) {
-            Ok((inst, &next[1..]))
-        } else {
-            Err(DateParseError::Invalid("didn't find expected delimiter"))
-        }
-    }
-
-    /// Converts the `Instant` to a chrono `DateTime<Utc>`.
-    #[cfg(feature = "chrono-conversions")]
-    pub fn to_chrono(self) -> DateTime<Utc> {
-        self.to_chrono_internal()
-    }
-
-    fn to_chrono_internal(self) -> DateTime<Utc> {
-        DateTime::<Utc>::from_utc(
-            NaiveDateTime::from_timestamp(self.seconds, self.subsecond_nanos),
-            Utc,
-        )
-    }
-
-    /// Convert this `Instant` to a [`SystemTime`](std::time::SystemTime)
-    ///
-    /// Since SystemTime cannot represent times prior to the unix epoch, if this time is before
-    /// 1/1/1970, this function will return `None`.
-    pub fn to_system_time(self) -> Option<SystemTime> {
-        if self.seconds < 0 {
-            None
-        } else {
-            Some(
-                UNIX_EPOCH
-                    + Duration::from_secs(self.seconds as u64)
-                    + Duration::from_nanos(self.subsecond_nanos as u64),
-            )
-        }
-    }
-
-    /// Returns true if sub-second nanos is greater than zero.
-    pub fn has_nanos(&self) -> bool {
-        self.subsecond_nanos != 0
-    }
-
-    /// Returns the `Instant` value as an `f64` representing the seconds since the Unix epoch.
-    pub fn epoch_fractional_seconds(&self) -> f64 {
-        self.seconds as f64 + self.subsecond_nanos as f64 / 1_000_000_000_f64
-    }
-
-    /// Returns the epoch seconds component of the `Instant`.
-    ///
-    /// _Note: this does not include the sub-second nanos._
-    pub fn epoch_seconds(&self) -> i64 {
-        self.seconds
-    }
-
-    /// Returns the sub-second nanos component of the `Instant`.
-    ///
-    /// _Note: this does not include the number of seconds since the epoch._
-    pub fn epoch_subsecond_nanos(&self) -> u32 {
-        self.subsecond_nanos
-    }
-
-    /// Converts the `Instant` to the number of milliseconds since the Unix epoch.
-    /// This is fallible since `Instant` holds more precision than an `i64`, and will
-    /// return a `ConversionError` for `Instant` values that can't be converted.
-    pub fn to_epoch_millis(self) -> Result<i64, ConversionError> {
-        let subsec_millis =
-            Integer::div_floor(&i64::from(self.subsecond_nanos), &(NANOS_PER_MILLI as i64));
-        if self.seconds < 0 {
-            self.seconds
-                .checked_add(1)
-                .and_then(|seconds| seconds.checked_mul(MILLIS_PER_SECOND))
-                .and_then(|millis| millis.checked_sub(1000 - subsec_millis))
-        } else {
-            self.seconds
-                .checked_mul(MILLIS_PER_SECOND)
-                .and_then(|millis| millis.checked_add(subsec_millis))
-        }
-        .ok_or(ConversionError(
-            "Instant value too large to fit into i64 epoch millis",
-        ))
-    }
-
-    /// Converts number of milliseconds since the Unix epoch into an `Instant`.
-    pub fn from_epoch_millis(epoch_millis: i64) -> Instant {
-        let (seconds, millis) = div_mod_floor(epoch_millis, MILLIS_PER_SECOND);
-        Instant::from_secs_and_nanos(seconds, millis as u32 * NANOS_PER_MILLI)
-    }
-
-    /// Formats the `Instant` to a string using the given `format`.
-    pub fn fmt(&self, format: Format) -> String {
-        match format {
-            Format::DateTime => format::rfc3339::format(self),
-            Format::EpochSeconds => format::epoch_seconds::format(self),
-            Format::HttpDate => format::http_date::format(self),
-        }
-    }
-}
-
-/// Failure to convert an `Instant` to or from another type.
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct ConversionError(&'static str);
-
-impl StdError for ConversionError {}
-
-impl fmt::Display for ConversionError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.0)
-    }
-}
-
-#[cfg(feature = "chrono-conversions")]
-impl From<DateTime<Utc>> for Instant {
-    fn from(value: DateTime<Utc>) -> Instant {
-        Instant::from_secs_and_nanos(value.timestamp(), value.timestamp_subsec_nanos())
-    }
-}
-
-#[cfg(feature = "chrono-conversions")]
-impl From<DateTime<chrono::FixedOffset>> for Instant {
-    fn from(value: DateTime<chrono::FixedOffset>) -> Instant {
-        value.with_timezone(&Utc).into()
-    }
-}
-
-/// Formats for representing an `Instant` in the Smithy protocols.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Format {
-    /// RFC-3339 Date Time.
-    DateTime,
-    /// Date format used by the HTTP `Date` header, specified in RFC-7231.
-    HttpDate,
-    /// Number of seconds since the Unix epoch formatted as a floating point.
-    EpochSeconds,
-}
-
-#[cfg(test)]
-mod test {
-    use crate::instant::Format;
-    use crate::Instant;
-
-    #[test]
-    fn test_instant_fmt() {
-        let instant = Instant::from_epoch_seconds(1576540098);
-        assert_eq!(instant.fmt(Format::DateTime), "2019-12-16T23:48:18Z");
-        assert_eq!(instant.fmt(Format::EpochSeconds), "1576540098");
-        assert_eq!(
-            instant.fmt(Format::HttpDate),
-            "Mon, 16 Dec 2019 23:48:18 GMT"
-        );
-
-        let instant = Instant::from_fractional_seconds(1576540098, 0.52);
-        assert_eq!(instant.fmt(Format::DateTime), "2019-12-16T23:48:18.52Z");
-        assert_eq!(instant.fmt(Format::EpochSeconds), "1576540098.52");
-        assert_eq!(
-            instant.fmt(Format::HttpDate),
-            "Mon, 16 Dec 2019 23:48:18.52 GMT"
-        );
-    }
-
-    #[test]
-    fn test_instant_fmt_zero_seconds() {
-        let instant = Instant::from_epoch_seconds(1576540080);
-        assert_eq!(instant.fmt(Format::DateTime), "2019-12-16T23:48:00Z");
-        assert_eq!(instant.fmt(Format::EpochSeconds), "1576540080");
-        assert_eq!(
-            instant.fmt(Format::HttpDate),
-            "Mon, 16 Dec 2019 23:48:00 GMT"
-        );
-    }
-
-    #[test]
-    fn test_read_single_http_date() {
-        let s = "Mon, 16 Dec 2019 23:48:18 GMT";
-        let (_, next) = Instant::read(s, Format::HttpDate, ',').expect("valid");
-        assert_eq!(next, "");
-    }
-
-    #[test]
-    fn test_read_single_float() {
-        let s = "1576540098.52";
-        let (_, next) = Instant::read(s, Format::EpochSeconds, ',').expect("valid");
-        assert_eq!(next, "");
-    }
-
-    #[test]
-    fn test_read_many_float() {
-        let s = "1576540098.52,1576540098.53";
-        let (_, next) = Instant::read(s, Format::EpochSeconds, ',').expect("valid");
-        assert_eq!(next, "1576540098.53");
-    }
-
-    #[test]
-    fn test_ready_many_http_date() {
-        let s = "Mon, 16 Dec 2019 23:48:18 GMT,Tue, 17 Dec 2019 23:48:18 GMT";
-        let (_, next) = Instant::read(s, Format::HttpDate, ',').expect("valid");
-        assert_eq!(next, "Tue, 17 Dec 2019 23:48:18 GMT");
-    }
-
-    #[test]
-    #[cfg(feature = "chrono-conversions")]
-    fn chrono_conversions_round_trip() {
-        for (seconds, nanos) in &[(1234, 56789), (-1234, 4321)] {
-            let instant = Instant::from_secs_and_nanos(*seconds, *nanos);
-            let chrono = instant.to_chrono();
-            let instant_again: Instant = chrono.into();
-            assert_eq!(instant, instant_again);
-        }
-    }
-
-    #[derive(Debug)]
-    struct EpochMillisTestCase {
-        rfc3339: &'static str,
-        epoch_millis: i64,
-        epoch_seconds: i64,
-        epoch_subsec_nanos: u32,
-    }
-
-    // These test case values were generated from the following Kotlin JVM code:
-    // ```kotlin
-    // val instant = Instant.ofEpochMilli(<epoch milli value>);
-    // println(DateTimeFormatter.ISO_DATE_TIME.format(instant.atOffset(ZoneOffset.UTC)))
-    // println(instant.epochSecond)
-    // println(instant.nano)
-    // ```
-    const EPOCH_MILLIS_TEST_CASES: &[EpochMillisTestCase] = &[
-        EpochMillisTestCase {
-            rfc3339: "2021-07-30T21:20:04.123Z",
-            epoch_millis: 1627680004123,
-            epoch_seconds: 1627680004,
-            epoch_subsec_nanos: 123000000,
-        },
-        EpochMillisTestCase {
-            rfc3339: "1918-06-04T02:39:55.877Z",
-            epoch_millis: -1627680004123,
-            epoch_seconds: -1627680005,
-            epoch_subsec_nanos: 877000000,
-        },
-        EpochMillisTestCase {
-            rfc3339: "+292278994-08-17T07:12:55.807Z",
-            epoch_millis: i64::MAX,
-            epoch_seconds: 9223372036854775,
-            epoch_subsec_nanos: 807000000,
-        },
-        EpochMillisTestCase {
-            rfc3339: "-292275055-05-16T16:47:04.192Z",
-            epoch_millis: i64::MIN,
-            epoch_seconds: -9223372036854776,
-            epoch_subsec_nanos: 192000000,
-        },
-    ];
-
-    #[test]
-    fn to_epoch_millis() {
-        for test_case in EPOCH_MILLIS_TEST_CASES {
-            println!("Test case: {:?}", test_case);
-            let instant =
-                Instant::from_secs_and_nanos(test_case.epoch_seconds, test_case.epoch_subsec_nanos);
-            assert_eq!(test_case.epoch_seconds, instant.epoch_seconds());
-            assert_eq!(
-                test_case.epoch_subsec_nanos,
-                instant.epoch_subsecond_nanos()
-            );
-            assert_eq!(test_case.epoch_millis, instant.to_epoch_millis().unwrap());
-        }
-
-        assert!(Instant::from_secs_and_nanos(i64::MAX, 0)
-            .to_epoch_millis()
-            .is_err());
-    }
-
-    #[test]
-    fn from_epoch_millis() {
-        for test_case in EPOCH_MILLIS_TEST_CASES {
-            println!("Test case: {:?}", test_case);
-            let instant = Instant::from_epoch_millis(test_case.epoch_millis);
-            assert_eq!(test_case.epoch_seconds, instant.epoch_seconds());
-            assert_eq!(
-                test_case.epoch_subsec_nanos,
-                instant.epoch_subsecond_nanos()
-            );
-        }
-    }
-
-    #[test]
-    fn to_from_epoch_millis_round_trip() {
-        for millis in &[0, 1627680004123, -1627680004123, i64::MAX, i64::MIN] {
-            assert_eq!(
-                *millis,
-                Instant::from_epoch_millis(*millis)
-                    .to_epoch_millis()
-                    .unwrap()
-            );
-        }
-    }
-}
diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs
index b3fe34c61..356edae96 100644
--- a/rust-runtime/aws-smithy-types/src/lib.rs
+++ b/rust-runtime/aws-smithy-types/src/lib.rs
@@ -16,11 +16,11 @@
 use std::collections::HashMap;
 
 pub mod base64;
-pub mod instant;
+pub mod date_time;
 pub mod primitive;
 pub mod retry;
 
-pub use crate::instant::Instant;
+pub use crate::date_time::DateTime;
 
 /// Binary Blob Type
 ///
-- 
GitLab