diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs
index 2d237a5b8f..63e9aa7e7b 100644
--- a/aws/rust-runtime/aws-config/src/lib.rs
+++ b/aws/rust-runtime/aws-config/src/lib.rs
@@ -155,6 +155,7 @@ mod loader {
     use aws_credential_types::cache::CredentialsCache;
     use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider};
     use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep};
+    use aws_smithy_async::time::{SharedTimeSource, TimeSource};
     use aws_smithy_client::http_connector::HttpConnector;
     use aws_smithy_types::retry::RetryConfig;
     use aws_smithy_types::timeout::TimeoutConfig;
@@ -192,6 +193,7 @@ mod loader {
         profile_files_override: Option<ProfileFiles>,
         use_fips: Option<bool>,
         use_dual_stack: Option<bool>,
+        time_source: Option<SharedTimeSource>,
     }
 
     impl ConfigLoader {
@@ -262,6 +264,12 @@ mod loader {
             self
         }
 
+        /// Set the time source used for tasks like signing requests
+        pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self {
+            self.time_source = Some(SharedTimeSource::new(time_source));
+            self
+        }
+
         /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used when
         /// sending operations. This **does not set** the HTTP connector used by config providers.
         /// To change that connector, use [ConfigLoader::configure].
@@ -588,12 +596,15 @@ mod loader {
                 SharedCredentialsProvider::new(builder.build().await)
             };
 
+            let ts = self.time_source.unwrap_or_default();
+
             let mut builder = SdkConfig::builder()
                 .region(region)
                 .retry_config(retry_config)
                 .timeout_config(timeout_config)
                 .credentials_cache(credentials_cache)
                 .credentials_provider(credentials_provider)
+                .time_source(ts)
                 .http_connector(http_connector);
 
             builder.set_app_name(app_name);
diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml
index f471975d82..9a7890190c 100644
--- a/aws/rust-runtime/aws-inlineable/Cargo.toml
+++ b/aws/rust-runtime/aws-inlineable/Cargo.toml
@@ -19,7 +19,7 @@ aws-sig-auth = { path = "../aws-sig-auth" }
 aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" }
 aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" }
 aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" }
-aws-smithy-http-tower= { path = "../../../rust-runtime/aws-smithy-http-tower" }
+aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" }
 aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" }
 aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" }
 aws-types = { path = "../aws-types" }
@@ -41,6 +41,7 @@ aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features
 aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] }
 tempfile = "3.2.0"
 tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
+aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] }
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs
index 29db9af0fa..407fd9fd2b 100644
--- a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs
+++ b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs
@@ -28,6 +28,7 @@ use aws_http::retry::AwsResponseRetryClassifier;
 use aws_http::user_agent::AwsUserAgent;
 use aws_inlineable::middleware::DefaultMiddleware;
 use aws_sig_auth::signer::OperationSigningConfig;
+use aws_smithy_async::time::SharedTimeSource;
 use aws_types::region::SigningRegion;
 use aws_types::SigningService;
 
@@ -94,7 +95,9 @@ fn test_operation() -> Operation<TestOperationParser, AwsResponseRetryClassifier
         conf.insert(SigningRegion::from_static("test-region"));
         conf.insert(OperationSigningConfig::default_config());
         conf.insert(SigningService::from_static("test-service-signing"));
-        conf.insert(UNIX_EPOCH + Duration::from_secs(1613414417));
+        conf.insert(SharedTimeSource::new(
+            UNIX_EPOCH + Duration::from_secs(1613414417),
+        ));
         conf.insert(AwsUserAgent::for_tests());
         Result::<_, Infallible>::Ok(req)
     })
diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs
index f436a5f028..d81adf95ce 100644
--- a/aws/rust-runtime/aws-runtime/src/auth.rs
+++ b/aws/rust-runtime/aws-runtime/src/auth.rs
@@ -313,7 +313,7 @@ pub mod sigv4 {
         ) -> Result<(), BoxError> {
             let operation_config =
                 Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?;
-            let request_time = config_bag.request_time().unwrap_or_default().system_time();
+            let request_time = config_bag.request_time().unwrap_or_default().now();
 
             let credentials = if let Some(creds) = identity.data::<Credentials>() {
                 creds
diff --git a/aws/rust-runtime/aws-sig-auth/Cargo.toml b/aws/rust-runtime/aws-sig-auth/Cargo.toml
index cb9695f7db..6ef8e4374f 100644
--- a/aws/rust-runtime/aws-sig-auth/Cargo.toml
+++ b/aws/rust-runtime/aws-sig-auth/Cargo.toml
@@ -15,6 +15,7 @@ aws-credential-types = { path = "../aws-credential-types" }
 aws-sigv4 = { path = "../aws-sigv4" }
 aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true }
 aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" }
+aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" }
 aws-types = { path = "../aws-types" }
 http = "0.2.2"
 tracing = "0.1"
@@ -22,8 +23,9 @@ tracing = "0.1"
 [dev-dependencies]
 aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] }
 aws-endpoint = { path = "../aws-endpoint" }
-aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types"}
+aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" }
 tracing-test = "0.2.1"
+aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] }
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs
index d7ec53454c..bdaf1e8e1f 100644
--- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs
+++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs
@@ -5,7 +5,6 @@
 
 use std::error::Error;
 use std::fmt::{Display, Formatter};
-use std::time::SystemTime;
 
 use aws_smithy_http::middleware::MapRequest;
 use aws_smithy_http::operation::Request;
@@ -13,6 +12,7 @@ use aws_smithy_http::property_bag::PropertyBag;
 
 use aws_credential_types::Credentials;
 use aws_sigv4::http_request::SignableBody;
+use aws_smithy_async::time::SharedTimeSource;
 use aws_types::region::SigningRegion;
 use aws_types::SigningService;
 
@@ -145,9 +145,10 @@ fn signing_config(
     let payload_override = config.get::<SignableBody<'static>>();
     let request_config = RequestConfig {
         request_ts: config
-            .get::<SystemTime>()
-            .copied()
-            .unwrap_or_else(SystemTime::now),
+            .get::<SharedTimeSource>()
+            .map(|t| t.now())
+            // TODO(enableNewSmithyRuntime): Remove this fallback
+            .unwrap_or_else(|| SharedTimeSource::default().now()),
         region,
         payload_override,
         service: signing_service,
@@ -199,6 +200,7 @@ mod test {
 
     use aws_credential_types::Credentials;
     use aws_endpoint::AwsAuthStage;
+    use aws_smithy_async::time::SharedTimeSource;
     use aws_types::region::{Region, SigningRegion};
     use aws_types::SigningService;
 
@@ -249,7 +251,9 @@ mod test {
         let req = operation::Request::new(req)
             .augment(|req, conf| {
                 conf.insert(region.clone());
-                conf.insert(UNIX_EPOCH + Duration::new(1611160427, 0));
+                conf.insert(SharedTimeSource::new(
+                    UNIX_EPOCH + Duration::new(1611160427, 0),
+                ));
                 conf.insert(SigningService::from_static("kinesis"));
                 conf.insert(endpoint);
                 Result::<_, Infallible>::Ok(req)
diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs
index f3a0301ae8..0d613f1967 100644
--- a/aws/rust-runtime/aws-types/src/sdk_config.rs
+++ b/aws/rust-runtime/aws-types/src/sdk_config.rs
@@ -14,6 +14,7 @@ use std::sync::Arc;
 use aws_credential_types::cache::CredentialsCache;
 use aws_credential_types::provider::SharedCredentialsProvider;
 use aws_smithy_async::rt::sleep::AsyncSleep;
+use aws_smithy_async::time::{SharedTimeSource, TimeSource};
 use aws_smithy_client::http_connector::HttpConnector;
 use aws_smithy_types::retry::RetryConfig;
 use aws_smithy_types::timeout::TimeoutConfig;
@@ -40,6 +41,8 @@ If no dual-stack endpoint is available the request MAY return an error.
 **Note**: Some services do not offer dual-stack as a configurable parameter (e.g. Code Catalyst). For
 these services, this setting has no effect"
         };
+
+        (time_source) => { "The time source use to use for this client. This only needs to be required for creating deterministic tests or platforms where `SystemTime::now()` is not supported." };
     }
 }
 
@@ -53,6 +56,7 @@ pub struct SdkConfig {
     endpoint_url: Option<String>,
     retry_config: Option<RetryConfig>,
     sleep_impl: Option<Arc<dyn AsyncSleep>>,
+    time_source: Option<SharedTimeSource>,
     timeout_config: Option<TimeoutConfig>,
     http_connector: Option<HttpConnector>,
     use_fips: Option<bool>,
@@ -73,6 +77,7 @@ pub struct Builder {
     endpoint_url: Option<String>,
     retry_config: Option<RetryConfig>,
     sleep_impl: Option<Arc<dyn AsyncSleep>>,
+    time_source: Option<SharedTimeSource>,
     timeout_config: Option<TimeoutConfig>,
     http_connector: Option<HttpConnector>,
     use_fips: Option<bool>,
@@ -499,6 +504,18 @@ impl Builder {
         self
     }
 
+    #[doc = docs_for!(time_source)]
+    pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self {
+        self.set_time_source(Some(SharedTimeSource::new(time_source)));
+        self
+    }
+
+    #[doc = docs_for!(time_source)]
+    pub fn set_time_source(&mut self, time_source: Option<SharedTimeSource>) -> &mut Self {
+        self.time_source = time_source;
+        self
+    }
+
     /// Build a [`SdkConfig`](SdkConfig) from this builder
     pub fn build(self) -> SdkConfig {
         SdkConfig {
@@ -513,6 +530,7 @@ impl Builder {
             http_connector: self.http_connector,
             use_fips: self.use_fips,
             use_dual_stack: self.use_dual_stack,
+            time_source: self.time_source,
         }
     }
 }
@@ -554,6 +572,11 @@ impl SdkConfig {
         self.credentials_provider.as_ref()
     }
 
+    /// Configured time source
+    pub fn time_source(&self) -> Option<SharedTimeSource> {
+        self.time_source.clone()
+    }
+
     /// Configured app name
     pub fn app_name(&self) -> Option<&AppName> {
         self.app_name.as_ref()
diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt
index 70cc99e52c..7f9f5234cf 100644
--- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt
+++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt
@@ -23,11 +23,13 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) :
         "BeforeTransmitInterceptorContextMut" to RuntimeType.smithyRuntimeApi(runtimeConfig)
             .resolve("client::interceptors::BeforeTransmitInterceptorContextMut"),
         "ConfigBag" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("config_bag::ConfigBag"),
-        "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::ConfigBagAccessors"),
+        "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig)
+            .resolve("client::orchestrator::ConfigBagAccessors"),
         "http" to CargoDependency.Http.toType(),
         "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig)
             .resolve("client::interceptors::InterceptorContext"),
-        "RequestTime" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::RequestTime"),
+        "StaticTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType()
+            .resolve("test_util::StaticTimeSource"),
         "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig)
             .resolve("client::interceptors::SharedInterceptor"),
         "TestParamsSetterInterceptor" to CargoDependency.smithyRuntime(runtimeConfig).withFeature("test-util")
@@ -48,7 +50,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) :
                         ##[doc(hidden)]
                         // This is a temporary method for testing. NEVER use it in production
                         pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self {
-                            self.operation.properties_mut().insert(request_time);
+                            self.operation.properties_mut().insert(#{StaticTimeSource}::new(request_time));
                             self
                         }
 
@@ -70,7 +72,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) :
                         pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self {
                             use #{ConfigBagAccessors};
                             let interceptor = #{TestParamsSetterInterceptor}::new(move |_: &mut #{BeforeTransmitInterceptorContextMut}<'_>, cfg: &mut #{ConfigBag}| {
-                                cfg.set_request_time(#{RequestTime}::new(request_time));
+                                cfg.set_request_time(#{StaticTimeSource}::new(request_time));
                             });
                             self.interceptors.push(#{SharedInterceptor}::new(interceptor));
                             self
diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt
index f5d8afcbe2..740d9367c9 100644
--- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt
+++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt
@@ -77,6 +77,7 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator {
                     ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl());
 
                     ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned());
+                    ${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source().clone());
                     """,
                 )
             },
diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml
index 8820956d27..b1e831a58f 100644
--- a/aws/sdk/integration-tests/kms/Cargo.toml
+++ b/aws/sdk/integration-tests/kms/Cargo.toml
@@ -15,9 +15,10 @@ aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types",
 aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
 aws-sdk-kms = { path = "../../build/aws-sdk/sdk/kms" }
 aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] }
+aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] }
 aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
 aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
 bytes = "1.0.0"
 http = "0.2.0"
-tokio = { version = "1.23.1", features = ["full", "test-util"]}
+tokio = { version = "1.23.1", features = ["full", "test-util"] }
 tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs
index 8d17e13d48..6aa23fee5c 100644
--- a/aws/sdk/integration-tests/kms/tests/integration.rs
+++ b/aws/sdk/integration-tests/kms/tests/integration.rs
@@ -7,6 +7,7 @@ use aws_http::user_agent::AwsUserAgent;
 use aws_sdk_kms as kms;
 use aws_sdk_kms::middleware::DefaultMiddleware;
 use aws_sdk_kms::operation::RequestId;
+use aws_smithy_async::time::SharedTimeSource;
 use aws_smithy_client::test_connection::TestConnection;
 use aws_smithy_client::{Client as CoreClient, SdkError};
 use aws_smithy_http::body::SdkBody;
@@ -72,6 +73,9 @@ async fn generate_random() {
     let conf = Config::builder()
         .region(Region::new("us-east-1"))
         .credentials_provider(Credentials::for_tests())
+        .time_source(SharedTimeSource::new(
+            UNIX_EPOCH + Duration::from_secs(1614952162),
+        ))
         .build();
     let mut op = GenerateRandomInput::builder()
         .number_of_bytes(64)
@@ -80,8 +84,6 @@ async fn generate_random() {
         .make_operation(&conf)
         .await
         .expect("valid operation");
-    op.properties_mut()
-        .insert(UNIX_EPOCH + Duration::from_secs(1614952162));
     op.properties_mut().insert(AwsUserAgent::for_tests());
     let resp = client.call(op).await.expect("request should succeed");
     // primitive checksum
@@ -126,6 +128,9 @@ async fn generate_random_keystore_not_found() {
     let conf = Config::builder()
         .region(Region::new("us-east-1"))
         .credentials_provider(Credentials::for_tests())
+        .time_source(SharedTimeSource::new(
+            UNIX_EPOCH + Duration::from_secs(1614955644),
+        ))
         .build();
     let conn = TestConnection::new(vec![(
         http::Request::builder()
@@ -160,8 +165,6 @@ async fn generate_random_keystore_not_found() {
         .await
         .expect("valid operation");
 
-    op.properties_mut()
-        .insert(UNIX_EPOCH + Duration::from_secs(1614955644));
     op.properties_mut().insert(AwsUserAgent::for_tests());
     let client = Client::new(conn.clone());
     let err = client.call(op).await.expect_err("key store doesn't exist");
diff --git a/aws/sdk/integration-tests/s3/tests/checksums.rs b/aws/sdk/integration-tests/s3/tests/checksums.rs
index ae533964a1..be9c860628 100644
--- a/aws/sdk/integration-tests/s3/tests/checksums.rs
+++ b/aws/sdk/integration-tests/s3/tests/checksums.rs
@@ -59,6 +59,7 @@ async fn test_checksum_on_streaming_response(
     );
     let sdk_config = SdkConfig::builder()
         .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests()))
+        .time_source(UNIX_EPOCH + Duration::from_secs(1624036048))
         .region(Region::new("us-east-1"))
         .http_connector(conn.clone())
         .build();
@@ -73,7 +74,6 @@ async fn test_checksum_on_streaming_response(
         .customize()
         .await
         .unwrap()
-        .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048))
         .user_agent_for_tests()
         .send()
         .await
diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs
index b1e708aad1..b69d67b3f3 100644
--- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs
+++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs
@@ -8,6 +8,7 @@ mod util;
 use aws_http::user_agent::AwsUserAgent;
 use aws_sdk_s3::config::{Credentials, Region};
 use aws_sdk_s3::Client;
+use aws_smithy_async::test_util::StaticTimeSource;
 use aws_smithy_client::dvr;
 use aws_smithy_client::dvr::MediaType;
 use aws_smithy_client::erase::DynConnector;
@@ -15,7 +16,6 @@ use aws_smithy_runtime_api::client::interceptors::{
     BeforeTransmitInterceptorContextMut, Interceptor,
 };
 use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors;
-use aws_smithy_runtime_api::client::orchestrator::RequestTime;
 use aws_smithy_runtime_api::config_bag::ConfigBag;
 use http::header::USER_AGENT;
 use http::HeaderValue;
@@ -66,7 +66,7 @@ impl Interceptor for RequestTimeResetInterceptor {
         _context: &mut BeforeTransmitInterceptorContextMut<'_>,
         cfg: &mut ConfigBag,
     ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> {
-        cfg.set_request_time(RequestTime::new(UNIX_EPOCH));
+        cfg.set_request_time(StaticTimeSource::new(UNIX_EPOCH));
 
         Ok(())
     }
@@ -81,7 +81,7 @@ impl Interceptor for RequestTimeAdvanceInterceptor {
         cfg: &mut ConfigBag,
     ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> {
         let request_time = cfg.request_time().unwrap();
-        let request_time = RequestTime::new(request_time.system_time() + self.0);
+        let request_time = StaticTimeSource::new(request_time.now() + self.0);
         cfg.set_request_time(request_time);
 
         Ok(())
diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs
index a5138f40de..afaf9d2718 100644
--- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs
+++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs
@@ -13,15 +13,14 @@ use aws_smithy_client::dvr::MediaType;
 use aws_smithy_client::erase::DynConnector;
 use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy;
 use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar;
-use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime};
+use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors;
 use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
 use aws_smithy_runtime_api::config_bag::ConfigBag;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
+use std::time::{Duration, UNIX_EPOCH};
 
 #[derive(Debug)]
 struct FixupPlugin {
     client: Client,
-    timestamp: SystemTime,
 }
 
 // # One SDK operation invocation.
@@ -44,7 +43,6 @@ async fn three_retries_and_then_success() {
                 .bucket("test-bucket");
 
             cfg.put(params_builder);
-            cfg.set_request_time(RequestTime::new(self.timestamp.clone()));
             cfg.put(AwsUserAgent::for_tests());
             cfg.put(InvocationId::for_tests());
             cfg.set_retry_strategy(FixedDelayRetryStrategy::one_second_delay());
@@ -58,11 +56,11 @@ async fn three_retries_and_then_success() {
         .credentials_provider(Credentials::for_tests())
         .region(Region::new("us-east-1"))
         .http_connector(DynConnector::new(conn.clone()))
+        .time_source(UNIX_EPOCH + Duration::from_secs(1624036048))
         .build();
     let client = Client::from_conf(config);
     let fixup = FixupPlugin {
         client: client.clone(),
-        timestamp: UNIX_EPOCH + Duration::from_secs(1624036048),
     };
 
     let resp = dbg!(
diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs
index 7ce99e3af3..4c9864ae84 100644
--- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs
+++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs
@@ -5,10 +5,11 @@
 
 use aws_http::user_agent::AwsUserAgent;
 use aws_runtime::invocation_id::InvocationId;
+use aws_smithy_async::test_util::StaticTimeSource;
 use aws_smithy_runtime_api::client::interceptors::{
     BeforeTransmitInterceptorContextMut, Interceptor, InterceptorRegistrar,
 };
-use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime};
+use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors;
 use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
 use aws_smithy_runtime_api::config_bag::ConfigBag;
 use http::header::USER_AGENT;
@@ -18,16 +19,13 @@ use std::time::SystemTime;
 pub const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent");
 
 #[derive(Debug)]
-pub struct FixupPlugin {
-    pub timestamp: SystemTime,
-}
+pub struct FixupPlugin;
 impl RuntimePlugin for FixupPlugin {
     fn configure(
         &self,
         cfg: &mut ConfigBag,
         _interceptors: &mut InterceptorRegistrar,
     ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> {
-        cfg.set_request_time(RequestTime::new(self.timestamp.clone()));
         cfg.put(InvocationId::for_tests());
         Ok(())
     }
diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt
index 5fcab7bd33..13cc1c26ba 100644
--- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt
+++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt
@@ -276,6 +276,7 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz
                 if let Some(timeout_config) = self.handle.conf.timeout_config() {
                     ${section.configBagName}.put(timeout_config.clone());
                 }
+                ${section.configBagName}.put(self.handle.conf.time_source.clone());
                 """,
             )
         }
diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt
index d43c5706ec..c6116f7326 100644
--- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt
+++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt
@@ -18,6 +18,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Resilien
 import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization
 import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
 import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
+import software.amazon.smithy.rust.codegen.client.smithy.generators.config.TimeSourceOperationCustomization
+import software.amazon.smithy.rust.codegen.client.smithy.generators.config.timeSourceCustomization
 import software.amazon.smithy.rust.codegen.core.rustlang.Feature
 import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
 import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization
@@ -47,7 +49,8 @@ class RequiredCustomizations : ClientCodegenDecorator {
             IdempotencyTokenGenerator(codegenContext, operation) +
             EndpointPrefixGenerator(codegenContext, operation) +
             HttpChecksumRequiredGenerator(codegenContext, operation) +
-            HttpVersionListCustomization(codegenContext, operation)
+            HttpVersionListCustomization(codegenContext, operation) +
+            TimeSourceOperationCustomization()
 
     override fun configCustomizations(
         codegenContext: ClientCodegenContext,
@@ -57,9 +60,9 @@ class RequiredCustomizations : ClientCodegenDecorator {
         if (codegenContext.smithyRuntimeMode.generateOrchestrator) {
             baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization(
                 codegenContext,
-            )
+            ) + timeSourceCustomization(codegenContext)
         } else {
-            baseCustomizations + ResiliencyConfigCustomization(codegenContext)
+            baseCustomizations + ResiliencyConfigCustomization(codegenContext) + timeSourceCustomization(codegenContext)
         }
 
     override fun libRsCustomizations(
diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt
index 3112798fe0..4af06de0bd 100644
--- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt
+++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt
@@ -29,6 +29,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomizat
 import software.amazon.smithy.rust.codegen.core.smithy.customize.Section
 import software.amazon.smithy.rust.codegen.core.smithy.makeOptional
 import software.amazon.smithy.rust.codegen.core.util.hasTrait
+import software.amazon.smithy.rust.codegen.core.util.letIf
 
 /**
  * [ServiceConfig] is the parent type of sections that can be overridden when generating a config for a service.
@@ -103,7 +104,13 @@ sealed class ServiceConfig(name: String) : Section(name) {
     data class DefaultForTests(val configBuilderRef: String) : ServiceConfig("DefaultForTests")
 }
 
-data class ConfigParam(val name: String, val type: Symbol, val setterDocs: Writable?, val getterDocs: Writable? = null)
+data class ConfigParam(
+    val name: String,
+    val type: Symbol,
+    val setterDocs: Writable?,
+    val getterDocs: Writable? = null,
+    val optional: Boolean = true,
+)
 
 /**
  * Config customization for a config param with no special behavior:
@@ -116,7 +123,11 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf
         return when (section) {
             is ServiceConfig.ConfigStruct -> writable {
                 docsOrFallback(param.getterDocs)
-                rust("pub (crate) ${param.name}: #T,", param.type.makeOptional())
+                val t = when (param.optional) {
+                    true -> param.type.makeOptional()
+                    false -> param.type
+                }
+                rust("pub (crate) ${param.name}: #T,", t)
             }
 
             ServiceConfig.ConfigImpl -> emptySection
@@ -148,7 +159,8 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf
             }
 
             ServiceConfig.BuilderBuild -> writable {
-                rust("${param.name}: self.${param.name},")
+                val default = "".letIf(!param.optional) { ".unwrap_or_default() " }
+                rust("${param.name}: self.${param.name}$default,")
             }
 
             ServiceConfig.ToRuntimePlugin -> emptySection
@@ -204,6 +216,7 @@ class ServiceConfigGenerator(private val customizations: List<ConfigCustomizatio
         customizations.forEach {
             it.section(ServiceConfig.ConfigStructAdditionalDocs)(writer)
         }
+        Attribute(Attribute.derive(RuntimeType.Clone)).render(writer)
         writer.rustBlock("pub struct Config") {
             customizations.forEach {
                 it.section(ServiceConfig.ConfigStruct)(this)
diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt
new file mode 100644
index 0000000000..89fff2034d
--- /dev/null
+++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rust.codegen.client.smithy.generators.config
+
+import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
+import software.amazon.smithy.rust.codegen.core.rustlang.Writable
+import software.amazon.smithy.rust.codegen.core.rustlang.docs
+import software.amazon.smithy.rust.codegen.core.rustlang.rust
+import software.amazon.smithy.rust.codegen.core.rustlang.writable
+import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
+import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization
+import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection
+
+fun timeSourceCustomization(codegenContext: ClientCodegenContext) = standardConfigParam(
+    ConfigParam(
+        "time_source",
+        RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource").toSymbol(),
+        setterDocs = writable { docs("""Sets the time source used for this service""") },
+        optional = false,
+    ),
+)
+
+class TimeSourceOperationCustomization : OperationCustomization() {
+    override fun section(section: OperationSection): Writable {
+        return when (section) {
+            is OperationSection.MutateRequest -> writable {
+                rust(
+                    """
+                    ${section.request}.properties_mut().insert(${section.config}.time_source.clone());
+                    """,
+                )
+            }
+
+            else -> emptySection
+        }
+    }
+}
diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs
index 5a8e864ada..7d3b4f78ce 100644
--- a/rust-runtime/aws-smithy-async/src/test_util.rs
+++ b/rust-runtime/aws-smithy-async/src/test_util.rs
@@ -13,7 +13,7 @@ use tokio::sync::Barrier;
 use tokio::time::timeout;
 
 use crate::rt::sleep::{AsyncSleep, Sleep};
-use crate::time::TimeSource;
+use crate::time::{SharedTimeSource, TimeSource};
 
 /// Manually controlled time source
 #[derive(Debug, Clone)]
@@ -24,7 +24,7 @@ pub struct ManualTimeSource {
 
 impl TimeSource for ManualTimeSource {
     fn now(&self) -> SystemTime {
-        self.start_time + dbg!(self.log.lock().unwrap()).iter().sum()
+        self.start_time + self.log.lock().unwrap().iter().sum()
     }
 }
 
@@ -194,6 +194,49 @@ pub fn controlled_time_and_sleep(
     (ManualTimeSource { start_time, log }, sleep, gate)
 }
 
+#[derive(Debug)]
+/// Time source that always returns the same time
+pub struct StaticTimeSource {
+    time: SystemTime,
+}
+
+impl StaticTimeSource {
+    /// Creates a new static time source that always returns the same time
+    pub fn new(time: SystemTime) -> Self {
+        Self { time }
+    }
+}
+
+impl TimeSource for StaticTimeSource {
+    fn now(&self) -> SystemTime {
+        self.time
+    }
+}
+
+impl TimeSource for SystemTime {
+    fn now(&self) -> SystemTime {
+        *self
+    }
+}
+
+impl From<StaticTimeSource> for SharedTimeSource {
+    fn from(value: StaticTimeSource) -> Self {
+        SharedTimeSource::new(value)
+    }
+}
+
+impl From<SystemTime> for SharedTimeSource {
+    fn from(value: SystemTime) -> Self {
+        SharedTimeSource::new(value)
+    }
+}
+
+impl From<ManualTimeSource> for SharedTimeSource {
+    fn from(value: ManualTimeSource) -> Self {
+        SharedTimeSource::new(value)
+    }
+}
+
 #[cfg(test)]
 mod test {
     use crate::rt::sleep::AsyncSleep;
diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs
index 85c2ff0519..d2e52c9a87 100644
--- a/rust-runtime/aws-smithy-async/src/time.rs
+++ b/rust-runtime/aws-smithy-async/src/time.rs
@@ -5,6 +5,7 @@
 
 //! Time source abstraction to support WASM and testing
 use std::fmt::Debug;
+use std::sync::Arc;
 use std::time::SystemTime;
 
 /// Trait with a `now()` function returning the current time
@@ -30,3 +31,33 @@ impl TimeSource for SystemTimeSource {
         SystemTime::now()
     }
 }
+
+impl Default for SharedTimeSource {
+    fn default() -> Self {
+        SharedTimeSource(Arc::new(SystemTimeSource))
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Time source structure used inside SDK
+///
+/// This implements Default—the default implementation will use `SystemTime::now()`
+pub struct SharedTimeSource(Arc<dyn TimeSource>);
+
+impl SharedTimeSource {
+    /// Returns the current time
+    pub fn now(&self) -> SystemTime {
+        self.0.now()
+    }
+
+    /// Creates a new shared time source
+    pub fn new(source: impl TimeSource + 'static) -> Self {
+        Self(Arc::new(source))
+    }
+}
+
+impl TimeSource for SharedTimeSource {
+    fn now(&self) -> SystemTime {
+        self.0.now()
+    }
+}
diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs
index 314fd57249..73d3963bb2 100644
--- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs
+++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs
@@ -15,13 +15,13 @@ use crate::config_bag::ConfigBag;
 use crate::type_erasure::{TypeErasedBox, TypedBox};
 use aws_smithy_async::future::now_or_later::NowOrLater;
 use aws_smithy_async::rt::sleep::AsyncSleep;
+use aws_smithy_async::time::{SharedTimeSource, TimeSource};
 use aws_smithy_http::body::SdkBody;
 use aws_smithy_types::endpoint::Endpoint;
 use std::fmt;
 use std::future::Future as StdFuture;
 use std::pin::Pin;
 use std::sync::Arc;
-use std::time::SystemTime;
 
 pub use error::OrchestratorError;
 
@@ -77,29 +77,6 @@ pub trait EndpointResolver: Send + Sync + fmt::Debug {
     fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result<Endpoint, BoxError>;
 }
 
-/// Time that the request is being made (so that time can be overridden in the [`ConfigBag`]).
-#[non_exhaustive]
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub struct RequestTime(SystemTime);
-
-impl Default for RequestTime {
-    fn default() -> Self {
-        Self(SystemTime::now())
-    }
-}
-
-impl RequestTime {
-    /// Create a new [`RequestTime`].
-    pub fn new(time: SystemTime) -> Self {
-        Self(time)
-    }
-
-    /// Returns the request time as a [`SystemTime`].
-    pub fn system_time(&self) -> SystemTime {
-        self.0
-    }
-}
-
 pub trait ConfigBagAccessors {
     fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams;
     fn set_auth_option_resolver_params(
@@ -140,8 +117,8 @@ pub trait ConfigBagAccessors {
     fn retry_strategy(&self) -> &dyn RetryStrategy;
     fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static);
 
-    fn request_time(&self) -> Option<RequestTime>;
-    fn set_request_time(&mut self, request_time: RequestTime);
+    fn request_time(&self) -> Option<SharedTimeSource>;
+    fn set_request_time(&mut self, time_source: impl TimeSource + 'static);
 
     fn sleep_impl(&self) -> Option<Arc<dyn AsyncSleep>>;
     fn set_sleep_impl(&mut self, async_sleep: Option<Arc<dyn AsyncSleep>>);
@@ -262,12 +239,12 @@ impl ConfigBagAccessors for ConfigBag {
         self.put::<Box<dyn RetryStrategy>>(Box::new(retry_strategy));
     }
 
-    fn request_time(&self) -> Option<RequestTime> {
-        self.get::<RequestTime>().cloned()
+    fn request_time(&self) -> Option<SharedTimeSource> {
+        self.get::<SharedTimeSource>().cloned()
     }
 
-    fn set_request_time(&mut self, request_time: RequestTime) {
-        self.put::<RequestTime>(request_time);
+    fn set_request_time(&mut self, request_time: impl TimeSource + 'static) {
+        self.put::<SharedTimeSource>(SharedTimeSource::new(request_time));
     }
 
     fn sleep_impl(&self) -> Option<Arc<dyn AsyncSleep>> {
diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml
index 496d50d84b..aadbfcac83 100644
--- a/rust-runtime/aws-smithy-runtime/Cargo.toml
+++ b/rust-runtime/aws-smithy-runtime/Cargo.toml
@@ -30,7 +30,7 @@ tokio = { version = "1.25", features = [] }
 tracing = "0.1.37"
 
 [dev-dependencies]
-aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] }
+aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] }
 tokio = { version = "1.25", features = ["macros", "rt", "test-util"] }
 tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
 tracing-test = "0.2.1"
diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs
index 74fa60bd70..543777ba6a 100644
--- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs
+++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs
@@ -47,7 +47,7 @@ mod tests {
     use super::*;
     use aws_smithy_http::body::SdkBody;
     use aws_smithy_runtime_api::client::interceptors::InterceptorContext;
-    use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime};
+    use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors;
     use aws_smithy_runtime_api::type_erasure::TypedBox;
     use std::time::{Duration, UNIX_EPOCH};
 
@@ -64,15 +64,12 @@ mod tests {
         let interceptor = TestParamsSetterInterceptor::new({
             let request_time = request_time.clone();
             move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| {
-                cfg.set_request_time(RequestTime::new(request_time));
+                cfg.set_request_time(request_time);
             }
         });
         interceptor
             .modify_before_signing(&mut ctx, &mut cfg)
             .unwrap();
-        assert_eq!(
-            request_time,
-            cfg.get::<RequestTime>().unwrap().system_time()
-        );
+        assert_eq!(cfg.request_time().unwrap().now(), request_time);
     }
 }