Repository: smithy-lang/smithy-rs (language: rust)

Issue:
Update `aws_smithy_types::date_time::Format::HttpDate` to omit fractional seconds
Per https://github.com/awslabs/smithy/pull/1847, we need to update [`aws_smithy_types::date_time::Format::HttpDate`](https://docs.rs/aws-smithy-types/latest/aws_smithy_types/date_time/enum.Format.html#variant.HttpDate) to omit fractional seconds.


Discussion / hints:
This was marked as a good first issue? This strikes me as potentially very complicated, in terms of its implications.
I think the implications are outside of our control here. We need to update the date time serializer to remove the fractional part because services are currently _rejecting_ requests when they have them. If more subtle behavior is required, then we need an update to the Smithy spec.

The implementation itself seems straightforward enough since it's just deleting https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-types/src/date_time/format.rs#L482 I think?
Ok, makes sense.

Fix patch:
diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml
index 5468952f15..b08ebefd4e 100644
--- a/CHANGELOG.next.toml
+++ b/CHANGELOG.next.toml
@@ -166,6 +166,12 @@ references = ["smithy-rs#2926", "smithy-rs#2972"]
 meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }
 author = "ysaito1001"
 
+[[smithy-rs]]
+message = "Omit fractional seconds from `http-date` format."
+references = ["smithy-rs#2831", "aws-sdk-rust#818"]
+meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all" }
+author = "rschmitt"
+
 [[smithy-rs]]
 message = "Source defaults from the default trait instead of implicitly based on type. This has minimal changes in the generated code."
 references = ["smithy-rs#2985"]
diff --git a/rust-runtime/aws-smithy-types/src/date_time/format.rs b/rust-runtime/aws-smithy-types/src/date_time/format.rs
index 7253dec1ac..86ffd42ac7 100644
--- a/rust-runtime/aws-smithy-types/src/date_time/format.rs
+++ b/rust-runtime/aws-smithy-types/src/date_time/format.rs
@@ -129,7 +129,6 @@ pub(crate) mod epoch_seconds {
 }
 
 pub(crate) mod http_date {
-    use super::remove_trailing_zeros;
     use crate::date_time::format::{
         DateTimeFormatError, DateTimeFormatErrorKind, DateTimeParseError, DateTimeParseErrorKind,
         NANOS_PER_SECOND,
@@ -141,18 +140,15 @@ pub(crate) mod http_date {
     // 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
+    // - Add deserialization of subsecond nanos
     //
-    /// Format a `DateTime` in the HTTP date format (imf-fixdate) with added support for subsecond precision
+    /// Format a `DateTime` in the HTTP date format (imf-fixdate)
     ///
     /// Example: "Mon, 16 Dec 2019 23:48:18 GMT"
     ///
     /// Some notes:
     /// - 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 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
+    /// - Subsecond nanos are not emitted
     pub(crate) fn format(date_time: &DateTime) -> Result<String, DateTimeFormatError> {
         fn out_of_range<E: std::fmt::Display>(cause: E) -> DateTimeFormatError {
             DateTimeFormatErrorKind::OutOfRange(
@@ -242,16 +238,6 @@ pub(crate) mod http_date {
         push_digit(&mut out, second / 10);
         push_digit(&mut out, second % 10);
 
-        // If non-zero nanos, push a 3-digit fractional second
-        let millis = structured.millisecond();
-        if millis != 0 {
-            out.push('.');
-            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");
         Ok(out)
     }
@@ -707,7 +693,7 @@ mod tests {
             http_date::format(&DateTime::from_secs(-62_135_596_800)).unwrap()
         );
         assert_eq!(
-            "Fri, 31 Dec 9999 23:59:59.999 GMT",
+            "Fri, 31 Dec 9999 23:59:59 GMT",
             http_date::format(&DateTime::from_secs_and_nanos(253402300799, 999_999_999)).unwrap()
         );
 
diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs
index b5e740cf5b..c412be065e 100644
--- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs
+++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs
@@ -396,7 +396,7 @@ mod test {
         );
         assert_eq!(
             date_time.fmt(Format::HttpDate).unwrap(),
-            "Mon, 16 Dec 2019 23:48:18.52 GMT"
+            "Mon, 16 Dec 2019 23:48:18 GMT"
         );
     }
 


Test patch:
diff --git a/aws/sdk/aws-models/s3-tests.smithy b/aws/sdk/aws-models/s3-tests.smithy
index 6e934ecfda..437e37491a 100644
--- a/aws/sdk/aws-models/s3-tests.smithy
+++ b/aws/sdk/aws-models/s3-tests.smithy
@@ -235,3 +235,27 @@ apply HeadObject @httpRequestTests([
         }
     }
 ])
+
+apply GetObject @httpRequestTests([
+    {
+        id: "GetObjectIfModifiedSince",
+        documentation: "https://github.com/awslabs/aws-sdk-rust/issues/818",
+
+        method: "GET",
+        protocol: "aws.protocols#restXml",
+        uri: "/object.txt",
+        headers: { "if-modified-since": "Fri, 16 Jul 2021 16:20:53 GMT" }
+        params: {
+            Bucket: "test-bucket",
+            Key: "object.txt"
+            IfModifiedSince: 1626452453.123,
+        },
+        vendorParams: {
+            "endpointParams": {
+                "builtInParams": {
+                    "AWS::Region": "us-east-1"
+                }
+            }
+        }
+    }
+])
diff --git a/rust-runtime/aws-smithy-types/test_data/date_time_format_test_suite.json b/rust-runtime/aws-smithy-types/test_data/date_time_format_test_suite.json
index a79e23e4aa..fea94cb637 100644
--- a/rust-runtime/aws-smithy-types/test_data/date_time_format_test_suite.json
+++ b/rust-runtime/aws-smithy-types/test_data/date_time_format_test_suite.json
@@ -1123,7 +1123,7 @@
           "canonical_seconds": "-62133482201",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Thu, 25 Jan 0001 11:23:19.123 GMT"
+          "smithy_format_value": "Thu, 25 Jan 0001 11:23:19 GMT"
       },
       {
           "iso8601": "0001-02-01T12:24:12Z",
@@ -1179,28 +1179,28 @@
           "canonical_seconds": "-62114444917",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Sun, 02 Sep 0001 19:31:23.001 GMT"
+          "smithy_format_value": "Sun, 02 Sep 0001 19:31:23 GMT"
       },
       {
           "iso8601": "0001-10-03T20:32:16.01Z",
           "canonical_seconds": "-62111762864",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Wed, 03 Oct 0001 20:32:16.01 GMT"
+          "smithy_format_value": "Wed, 03 Oct 0001 20:32:16 GMT"
       },
       {
           "iso8601": "0001-11-04T21:33:09.1Z",
           "canonical_seconds": "-62108994411",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Sun, 04 Nov 0001 21:33:09.1 GMT"
+          "smithy_format_value": "Sun, 04 Nov 0001 21:33:09 GMT"
       },
       {
           "iso8601": "0001-12-05T22:34:02.123456789Z",
           "canonical_seconds": "-62106312358",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Wed, 05 Dec 0001 22:34:02.123 GMT"
+          "smithy_format_value": "Wed, 05 Dec 0001 22:34:02 GMT"
       },
       {
           "iso8601": "0100-01-06T23:35:55Z",
@@ -1256,28 +1256,28 @@
           "canonical_seconds": "-58992081474",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Fri, 13 Aug 0100 06:42:06.001 GMT"
+          "smithy_format_value": "Fri, 13 Aug 0100 06:42:06 GMT"
       },
       {
           "iso8601": "0100-09-14T07:43:59.01Z",
           "canonical_seconds": "-58989312961",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Tue, 14 Sep 0100 07:43:59.01 GMT"
+          "smithy_format_value": "Tue, 14 Sep 0100 07:43:59 GMT"
       },
       {
           "iso8601": "0100-10-15T08:44:52.1Z",
           "canonical_seconds": "-58986630908",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Fri, 15 Oct 0100 08:44:52.1 GMT"
+          "smithy_format_value": "Fri, 15 Oct 0100 08:44:52 GMT"
       },
       {
           "iso8601": "0100-11-16T09:45:45.123456789Z",
           "canonical_seconds": "-58983862455",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Tue, 16 Nov 0100 09:45:45.123 GMT"
+          "smithy_format_value": "Tue, 16 Nov 0100 09:45:45 GMT"
       },
       {
           "iso8601": "0100-12-17T10:46:38Z",
@@ -1291,7 +1291,7 @@
           "canonical_seconds": "-12175833601",
           "canonical_nanos": 999000000,
           "error": false,
-          "smithy_format_value": "Wed, 29 Feb 1584 23:59:59.999 GMT"
+          "smithy_format_value": "Wed, 29 Feb 1584 23:59:59 GMT"
       },
       {
           "iso8601": "1969-01-18T11:47:31.000000001Z",
@@ -1340,28 +1340,28 @@
           "canonical_seconds": "-13845971",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Thu, 24 Jul 1969 17:53:49.001 GMT"
+          "smithy_format_value": "Thu, 24 Jul 1969 17:53:49 GMT"
       },
       {
           "iso8601": "1969-08-25T18:54:42.01Z",
           "canonical_seconds": "-11077518",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Mon, 25 Aug 1969 18:54:42.01 GMT"
+          "smithy_format_value": "Mon, 25 Aug 1969 18:54:42 GMT"
       },
       {
           "iso8601": "1969-09-26T19:55:35.1Z",
           "canonical_seconds": "-8309065",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Fri, 26 Sep 1969 19:55:35.1 GMT"
+          "smithy_format_value": "Fri, 26 Sep 1969 19:55:35 GMT"
       },
       {
           "iso8601": "1969-10-27T20:56:28.123456789Z",
           "canonical_seconds": "-5627012",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Mon, 27 Oct 1969 20:56:28.123 GMT"
+          "smithy_format_value": "Mon, 27 Oct 1969 20:56:28 GMT"
       },
       {
           "iso8601": "1969-11-28T21:57:21Z",
@@ -1417,28 +1417,28 @@
           "canonical_seconds": "13406672",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Fri, 05 Jun 1970 04:04:32.001 GMT"
+          "smithy_format_value": "Fri, 05 Jun 1970 04:04:32 GMT"
       },
       {
           "iso8601": "1970-07-05T05:05:25.01Z",
           "canonical_seconds": "16002325",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Sun, 05 Jul 1970 05:05:25.01 GMT"
+          "smithy_format_value": "Sun, 05 Jul 1970 05:05:25 GMT"
       },
       {
           "iso8601": "1970-08-06T06:06:18.1Z",
           "canonical_seconds": "18770778",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Thu, 06 Aug 1970 06:06:18.1 GMT"
+          "smithy_format_value": "Thu, 06 Aug 1970 06:06:18 GMT"
       },
       {
           "iso8601": "1970-09-08T07:07:11.123456789Z",
           "canonical_seconds": "21625631",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Tue, 08 Sep 1970 07:07:11.123 GMT"
+          "smithy_format_value": "Tue, 08 Sep 1970 07:07:11 GMT"
       },
       {
           "iso8601": "1970-10-08T08:08:04Z",
@@ -1466,7 +1466,7 @@
           "canonical_seconds": "1078099199",
           "canonical_nanos": 999000000,
           "error": false,
-          "smithy_format_value": "Sun, 29 Feb 2004 23:59:59.999 GMT"
+          "smithy_format_value": "Sun, 29 Feb 2004 23:59:59 GMT"
       },
       {
           "iso8601": "2037-01-11T11:11:43.0000001Z",
@@ -1501,28 +1501,28 @@
           "canonical_seconds": "2126013315",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Fri, 15 May 2037 15:15:15.001 GMT"
+          "smithy_format_value": "Fri, 15 May 2037 15:15:15 GMT"
       },
       {
           "iso8601": "2037-06-17T16:16:08.01Z",
           "canonical_seconds": "2128868168",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Wed, 17 Jun 2037 16:16:08.01 GMT"
+          "smithy_format_value": "Wed, 17 Jun 2037 16:16:08 GMT"
       },
       {
           "iso8601": "2037-07-17T17:17:01.1Z",
           "canonical_seconds": "2131463821",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Fri, 17 Jul 2037 17:17:01.1 GMT"
+          "smithy_format_value": "Fri, 17 Jul 2037 17:17:01 GMT"
       },
       {
           "iso8601": "2037-08-18T18:18:54.123456789Z",
           "canonical_seconds": "2134232334",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Tue, 18 Aug 2037 18:18:54.123 GMT"
+          "smithy_format_value": "Tue, 18 Aug 2037 18:18:54 GMT"
       },
       {
           "iso8601": "2037-09-20T19:19:47Z",
@@ -1578,28 +1578,28 @@
           "canonical_seconds": "2155948018",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Tue, 27 Apr 2038 02:26:58.001 GMT"
+          "smithy_format_value": "Tue, 27 Apr 2038 02:26:58 GMT"
       },
       {
           "iso8601": "2038-05-27T03:27:51.01Z",
           "canonical_seconds": "2158543671",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Thu, 27 May 2038 03:27:51.01 GMT"
+          "smithy_format_value": "Thu, 27 May 2038 03:27:51 GMT"
       },
       {
           "iso8601": "2038-06-29T04:28:44.1Z",
           "canonical_seconds": "2161398524",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Tue, 29 Jun 2038 04:28:44.1 GMT"
+          "smithy_format_value": "Tue, 29 Jun 2038 04:28:44 GMT"
       },
       {
           "iso8601": "2038-07-29T05:29:37.123456789Z",
           "canonical_seconds": "2163994177",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Thu, 29 Jul 2038 05:29:37.123 GMT"
+          "smithy_format_value": "Thu, 29 Jul 2038 05:29:37 GMT"
       },
       {
           "iso8601": "2038-08-30T06:30:30Z",
@@ -1655,28 +1655,28 @@
           "canonical_seconds": "253376343461",
           "canonical_nanos": 1000000,
           "error": false,
-          "smithy_format_value": "Sat, 06 Mar 9999 13:37:41.001 GMT"
+          "smithy_format_value": "Sat, 06 Mar 9999 13:37:41 GMT"
       },
       {
           "iso8601": "9999-04-09T14:38:34.01Z",
           "canonical_seconds": "253379284714",
           "canonical_nanos": 10000000,
           "error": false,
-          "smithy_format_value": "Fri, 09 Apr 9999 14:38:34.01 GMT"
+          "smithy_format_value": "Fri, 09 Apr 9999 14:38:34 GMT"
       },
       {
           "iso8601": "9999-05-08T15:39:27.1Z",
           "canonical_seconds": "253381793967",
           "canonical_nanos": 100000000,
           "error": false,
-          "smithy_format_value": "Sat, 08 May 9999 15:39:27.1 GMT"
+          "smithy_format_value": "Sat, 08 May 9999 15:39:27 GMT"
       },
       {
           "iso8601": "9999-06-11T16:40:20.123456789Z",
           "canonical_seconds": "253384735220",
           "canonical_nanos": 123456789,
           "error": false,
-          "smithy_format_value": "Fri, 11 Jun 9999 16:40:20.123 GMT"
+          "smithy_format_value": "Fri, 11 Jun 9999 16:40:20 GMT"
       },
       {
           "iso8601": "9999-07-10T17:41:13Z",