Repository: localstack/localstack

Issue:
bug: not able to get records from kinesis with "latest" docker image
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

Using the kinesis java client I'm not able to get records from kinesis. This is the code snippet of the client
                      ```
                      private GetRecordsResponse getStreamRecords() {
                              final KinesisAsyncClient client = createClient();
                              final String shardId = client.listShards(
                                              ListShardsRequest.builder()
                                                      .streamName(STREAM_NAME)
                                                      .build()
                                      ).get()
                                      .shards()
                                      .get(0)
                                      .shardId();
                      
                              final String iterator = client.getShardIterator(GetShardIteratorRequest.builder()
                                              .streamName(STREAM_NAME)
                                              .shardId(shardId)
                                              .shardIteratorType(ShardIteratorType.TRIM_HORIZON)
                                              .build())
                                      .get()
                                      .shardIterator();
                              final GetRecordsResponse response = client.getRecords(
                                              GetRecordsRequest
                                                      .builder()
                                                      .shardIterator(iterator)
                                                      .build())
                                      .get();
                              return response;
                          }
                      ```
I get this error

```
java.util.concurrent.ExecutionException: software.amazon.awssdk.core.exception.SdkClientException: Unable to parse date : 1.661885385488E9
  	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
  	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
  	at org.apache.pulsar.io.kinesis.KinesisSinkTest.getStreamRecords(KinesisSinkTest.java:177)
  	at org.apache.pulsar.io.kinesis.KinesisSinkTest.testWrite(KinesisSinkTest.java:116)
  	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
  	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
  	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:132)
  	at org.testng.internal.InvokeMethodRunnable.runOne(InvokeMethodRunnable.java:45)
  	at org.testng.internal.InvokeMethodRunnable.call(InvokeMethodRunnable.java:73)
  	at org.testng.internal.InvokeMethodRunnable.call(InvokeMethodRunnable.java:11)
  	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
  	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
  	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
  	at java.base/java.lang.Thread.run(Thread.java:833)
  Caused by: software.amazon.awssdk.core.exception.SdkClientException: Unable to parse date : 1.661885385488E9
  	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:97)
  	at software.amazon.awssdk.protocols.core.StringToInstant.lambda$safeParseDate$0(StringToInstant.java:77)
  	at software.amazon.awssdk.protocols.core.StringToInstant.convert(StringToInstant.java:56)
  	at software.amazon.awssdk.protocols.core.StringToInstant.convert(StringToInstant.java:32)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller$SimpleTypeJsonUnmarshaller.unmarshall(JsonProtocolUnmarshaller.java:160)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshallStructured(JsonProtocolUnmarshaller.java:210)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshallStructured(JsonProtocolUnmarshaller.java:114)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.lambda$unmarshallList$2(JsonProtocolUnmarshaller.java:143)
  	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
  	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
  	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
  	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
  	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
  	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshallList(JsonProtocolUnmarshaller.java:145)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshallStructured(JsonProtocolUnmarshaller.java:210)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshall(JsonProtocolUnmarshaller.java:197)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller.unmarshall(JsonProtocolUnmarshaller.java:168)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonResponseHandler.handle(JsonResponseHandler.java:79)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.JsonResponseHandler.handle(JsonResponseHandler.java:36)
  	at software.amazon.awssdk.protocols.json.internal.unmarshall.AwsJsonResponseHandler.handle(AwsJsonResponseHandler.java:43)
  	at software.amazon.awssdk.core.internal.handler.BaseClientHandler.lambda$resultTransformationResponseHandler$5(BaseClientHandler.java:232)
  	at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler.lambda$prepare$0(AsyncResponseHandler.java:88)
  	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
  	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
  	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
  	at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler$BaosSubscriber.onComplete(AsyncResponseHandler.java:129)
  	at software.amazon.awssdk.http.nio.netty.internal.ResponseHandler.runAndLogError(ResponseHandler.java:171)
  	at software.amazon.awssdk.http.nio.netty.internal.ResponseHandler.access$500(ResponseHandler.java:68)
  	at software.amazon.awssdk.http.nio.netty.internal.ResponseHandler$PublisherAdapter$1.onComplete(ResponseHandler.java:287)
  	at com.typesafe.netty.HandlerPublisher.complete(HandlerPublisher.java:408)
  	at com.typesafe.netty.HandlerPublisher.handlerRemoved(HandlerPublisher.java:395)
  	at io.netty.channel.AbstractChannelHandlerContext.callHandlerRemoved(AbstractChannelHandlerContext.java:946)
  	at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:637)
  	at io.netty.channel.DefaultChannelPipeline.remove(DefaultChannelPipeline.java:477)
  	at io.netty.channel.DefaultChannelPipeline.remove(DefaultChannelPipeline.java:423)
  	at com.typesafe.netty.http.HttpStreamsHandler.removeHandlerIfActive(HttpStreamsHandler.java:328)
  	at com.typesafe.netty.http.HttpStreamsHandler.handleReadHttpContent(HttpStreamsHandler.java:189)
  	at com.typesafe.netty.http.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:165)
  	at com.typesafe.netty.http.HttpStreamsClientHandler.channelRead(HttpStreamsClientHandler.java:148)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at software.amazon.awssdk.http.nio.netty.internal.LastHttpContentHandler.channelRead(LastHttpContentHandler.java:43)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at software.amazon.awssdk.http.nio.netty.internal.http2.Http2ToHttpInboundAdapter.onDataRead(Http2ToHttpInboundAdapter.java:66)
  	at software.amazon.awssdk.http.nio.netty.internal.http2.Http2ToHttpInboundAdapter.channelRead0(Http2ToHttpInboundAdapter.java:44)
  	at software.amazon.awssdk.http.nio.netty.internal.http2.Http2ToHttpInboundAdapter.channelRead0(Http2ToHttpInboundAdapter.java:38)
  	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
  	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel$Http2ChannelUnsafe.doRead0(AbstractHttp2StreamChannel.java:901)
  	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel.fireChildRead(AbstractHttp2StreamChannel.java:555)
  	at io.netty.handler.codec.http2.Http2MultiplexHandler.channelRead(Http2MultiplexHandler.java:180)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at io.netty.handler.codec.http2.Http2FrameCodec.onHttp2Frame(Http2FrameCodec.java:707)
  	at io.netty.handler.codec.http2.Http2FrameCodec$FrameListener.onDataRead(Http2FrameCodec.java:646)
  	at io.netty.handler.codec.http2.Http2FrameListenerDecorator.onDataRead(Http2FrameListenerDecorator.java:36)
  	at io.netty.handler.codec.http2.Http2EmptyDataFrameListener.onDataRead(Http2EmptyDataFrameListener.java:49)
  	at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder$FrameReadListener.onDataRead(DefaultHttp2ConnectionDecoder.java:307)
  	at io.netty.handler.codec.http2.Http2InboundFrameLogger$1.onDataRead(Http2InboundFrameLogger.java:48)
  	at io.netty.handler.codec.http2.DefaultHttp2FrameReader.readDataFrame(DefaultHttp2FrameReader.java:415)
  	at io.netty.handler.codec.http2.DefaultHttp2FrameReader.processPayloadState(DefaultHttp2FrameReader.java:250)
  	at io.netty.handler.codec.http2.DefaultHttp2FrameReader.readFrame(DefaultHttp2FrameReader.java:159)
  	at io.netty.handler.codec.http2.Http2InboundFrameLogger.readFrame(Http2InboundFrameLogger.java:41)
  	at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder.decodeFrame(DefaultHttp2ConnectionDecoder.java:173)
  	at io.netty.handler.codec.http2.DecoratingHttp2ConnectionDecoder.decodeFrame(DecoratingHttp2ConnectionDecoder.java:63)
  	at io.netty.handler.codec.http2.Http2ConnectionHandler$FrameDecoder.decode(Http2ConnectionHandler.java:378)
  	at io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
  	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:510)
  	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:449)
  	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:279)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
  	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
  	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
  	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
  	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
  	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
  	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995)
  	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
  	... 1 more
  Caused by: java.lang.NumberFormatException: For input string: "1.661885385488E9"
  	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
  	at java.base/java.lang.Long.parseLong(Long.java:711)
  	at java.base/java.lang.Long.parseLong(Long.java:836)
  	at software.amazon.awssdk.utils.DateUtils.parseUnixTimestampMillisInstant(DateUtils.java:146)
  	at software.amazon.awssdk.protocols.core.StringToInstant.lambda$safeParseDate$0(StringToInstant.java:72)
```
Note that just sending raw bytes and no dates are involved in the message.

This is a regression with the ["latest"](https://hub.docker.com/layers/localstack/localstack/latest/images/sha256-bcc915531722412c0b3987b0562c1851a6fabb125c306f3b2da1bae2e204957c?context=explore) docker image. Reverting to 1.0.4 everything works fine.
I suspect it's a regression introduced with https://github.com/localstack/localstack/pull/6166

### Expected Behavior

To work as in 1.0.4

### How are you starting LocalStack?

Custom (please describe below)

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

    TestContainers

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)


```
private GetRecordsResponse getStreamRecords() {
        final KinesisAsyncClient client = createClient();
        final String shardId = client.listShards(
                        ListShardsRequest.builder()
                                .streamName(STREAM_NAME)
                                .build()
                ).get()
                .shards()
                .get(0)
                .shardId();

        final String iterator = client.getShardIterator(GetShardIteratorRequest.builder()
                        .streamName(STREAM_NAME)
                        .shardId(shardId)
                        .shardIteratorType(ShardIteratorType.TRIM_HORIZON)
                        .build())
                .get()
                .shardIterator();
        final GetRecordsResponse response = client.getRecords(
                        GetRecordsRequest
                                .builder()
                                .shardIterator(iterator)
                                .build())
                .get();
        return response;
    }
```

### Environment

```markdown
- OS: MacOs
- LocalStack: latest
```


### Anything else?

_No response_


Discussion:
Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our [Slack Community](https://localstack.cloud/contact/) for real-time help and drop a message to LocalStack Pro Support if you are a Pro user! If you are willing to contribute towards fixing this issue, please have a look at our [contributing guidelines](https://github.com/localstack/.github/blob/main/CONTRIBUTING.md) and our [developer guide](https://docs.localstack.cloud/developer-guide/).
thanks for the report @nicoloboschi, we'll take a look tomorrow. for now you should be able to work around the issue by starting localstack with `PROVIDER_OVERRIDE_KINESIS=legacy`

Resolving patch:
diff --git a/localstack/aws/forwarder.py b/localstack/aws/forwarder.py
--- a/localstack/aws/forwarder.py
+++ b/localstack/aws/forwarder.py
@@ -6,6 +6,7 @@
 from urllib.parse import urlsplit
 
 from botocore.awsrequest import AWSPreparedRequest
+from botocore.config import Config as BotoConfig
 from werkzeug.datastructures import Headers
 
 from localstack import config
@@ -115,6 +116,10 @@ def dispatch_to_backend(
     return parsed_response
 
 
+# boto config deactivating param validation to forward to backends (backends are responsible for validating params)
+_non_validating_boto_config = BotoConfig(parameter_validation=False)
+
+
 def create_aws_request_context(
     service_name: str,
     action: str,
@@ -145,9 +150,14 @@ def create_aws_request_context(
     service = load_service(service_name)
     operation = service.operation_model(action)
 
-    # we re-use botocore internals here to serialize the HTTP request, but don't send it
+    # we re-use botocore internals here to serialize the HTTP request,
+    # but deactivate validation (validation errors should be handled by the backend)
+    # and don't send it yet
     client = aws_stack.connect_to_service(
-        service_name, endpoint_url=endpoint_url, region_name=region
+        service_name,
+        endpoint_url=endpoint_url,
+        region_name=region,
+        config=_non_validating_boto_config,
     )
     request_context = {
         "client_region": region,
diff --git a/localstack/aws/protocol/serializer.py b/localstack/aws/protocol/serializer.py
--- a/localstack/aws/protocol/serializer.py
+++ b/localstack/aws/protocol/serializer.py
@@ -461,10 +461,12 @@ def _get_mime_type(self, headers: Optional[Dict | Headers]) -> str:
         mime_type = mime_accept.best_match(self.SUPPORTED_MIME_TYPES)
         if not mime_type:
             # There is no match between the supported mime types and the requested one(s)
+            mime_type = self.SUPPORTED_MIME_TYPES[0]
             LOG.debug(
-                "Determined accept type (%s) is not supported by this serializer.", accept_header
+                "Determined accept type (%s) is not supported by this serializer. Using default of this serializer: %s",
+                accept_header,
+                mime_type,
             )
-            mime_type = self.SUPPORTED_MIME_TYPES[0]
         return mime_type
 
     # Some extra utility methods subclasses can use.
@@ -481,6 +483,10 @@ def _timestamp_iso8601(value: datetime) -> str:
     def _timestamp_unixtimestamp(value: datetime) -> float:
         return value.timestamp()
 
+    @staticmethod
+    def _timestamp_unixtimestampmillis(value: datetime) -> int:
+        return int(value.timestamp() * 1000)
+
     def _timestamp_rfc822(self, value: datetime) -> str:
         if isinstance(value, datetime):
             value = self._timestamp_unixtimestamp(value)
@@ -1288,10 +1294,16 @@ def _serialize_type_list(
     def _default_serialize(self, body: dict, value: Any, _, key: str, __):
         body[key] = value
 
-    def _serialize_type_timestamp(self, body: dict, value: Any, shape: Shape, key: str, _):
-        body[key] = self._convert_timestamp_to_str(
-            value, shape.serialization.get("timestampFormat")
+    def _serialize_type_timestamp(
+        self, body: dict, value: Any, shape: Shape, key: str, mime_type: str
+    ):
+        timestamp_format = (
+            shape.serialization.get("timestampFormat")
+            # CBOR always uses unix timestamp milliseconds
+            if mime_type not in self.CBOR_TYPES
+            else "unixtimestampmillis"
         )
+        body[key] = self._convert_timestamp_to_str(value, timestamp_format)
 
     def _serialize_type_blob(
         self, body: dict, value: Union[str, bytes], _, key: str, mime_type: str
diff --git a/localstack/logging/setup.py b/localstack/logging/setup.py
--- a/localstack/logging/setup.py
+++ b/localstack/logging/setup.py
@@ -18,12 +18,14 @@
     "s3transfer": logging.INFO,
     "urllib3": logging.WARNING,
     "werkzeug": logging.WARNING,
+    "localstack.aws.protocol.serializer": logging.INFO,
     "localstack.aws.serving.wsgi": logging.WARNING,
     "localstack.request": logging.INFO,
     "localstack.request.internal": logging.WARNING,
 }
 
 trace_log_levels = {
+    "localstack.aws.protocol.serializer": logging.DEBUG,
     "localstack.aws.serving.wsgi": logging.DEBUG,
     "localstack.request": logging.DEBUG,
     "localstack.request.internal": logging.INFO,


Test patch:
diff --git a/tests/integration/test_kinesis.py b/tests/integration/test_kinesis.py
--- a/tests/integration/test_kinesis.py
+++ b/tests/integration/test_kinesis.py
@@ -5,6 +5,8 @@
 import cbor2
 import pytest
 import requests
+from botocore.config import Config as BotoConfig
+from botocore.exceptions import ClientError
 
 from localstack import config, constants
 from localstack.services.kinesis import provider as kinesis_provider
@@ -37,6 +39,13 @@ def kinesis_snapshot_transformer(snapshot):
 
 
 class TestKinesis:
+    def test_create_stream_without_stream_name_raises(self):
+        boto_config = BotoConfig(parameter_validation=False)
+        kinesis_client = aws_stack.create_external_boto_client("kinesis", config=boto_config)
+        with pytest.raises(ClientError) as e:
+            kinesis_client.create_stream()
+        assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+
     @pytest.mark.aws_validated
     def test_create_stream_without_shard_count(
         self, kinesis_client, kinesis_create_stream, wait_for_stream_ready, snapshot
@@ -243,6 +252,11 @@ def test_get_records(self, kinesis_client, kinesis_create_stream, wait_for_strea
         assert select_attributes(json_records[0], attrs) == select_attributes(
             result["Records"][0], attrs
         )
+        # ensure that the CBOR datetime format is unix timestamp millis
+        assert (
+            int(json_records[0]["ApproximateArrivalTimestamp"].timestamp() * 1000)
+            == result["Records"][0]["ApproximateArrivalTimestamp"]
+        )
 
     def test_get_records_empty_stream(
         self, kinesis_client, kinesis_create_stream, wait_for_stream_ready