Repository: aws-powertools/powertools-lambda-python

Issue:
bug: aws scalar type for AWSDateTime should include milliseconds
**What were you trying to accomplish?**

## Expected Behavior

AWSDateTime - An extended ISO 8601 date and time string in the format YYYY-MM-DDThh:mm:ss.sssZ.

## Current Behavior

AWSDateTime is not including the millisecond part `sss`.

## Possible Solution

Generate timestamps to include the milliseconds

## Steps to Reproduce (for bugs)

```python3
> print(aws_datetime())
2021-07-02T17:09:47Z
````

## Environment

* **Powertools version used**: 1.17.0
* **Packaging format (Layers, PyPi)**: PyPi
* **AWS Lambda function runtime:** Python 3.8



Discussion:

Resolving patch:
diff --git a/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py b/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py
--- a/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py
+++ b/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py
@@ -19,15 +19,21 @@ def _formatted_time(now: datetime.date, fmt: str, timezone_offset: int) -> str:
     str
         Returns string formatted time with optional timezone offset
     """
-    if timezone_offset == 0:
-        return now.strftime(fmt + "Z")
+    if timezone_offset != 0:
+        now = now + datetime.timedelta(hours=timezone_offset)
+
+    datetime_str = now.strftime(fmt)
+    if fmt.endswith(".%f"):
+        datetime_str = datetime_str[:-3]
 
-    now = now + datetime.timedelta(hours=timezone_offset)
-    fmt += "+" if timezone_offset > 0 else "-"
-    fmt += str(abs(timezone_offset)).zfill(2)
-    fmt += ":00:00"
+    if timezone_offset == 0:
+        postfix = "Z"
+    else:
+        postfix = "+" if timezone_offset > 0 else "-"
+        postfix += str(abs(timezone_offset)).zfill(2)
+        postfix += ":00:00"
 
-    return now.strftime(fmt)
+    return datetime_str + postfix
 
 
 def make_id() -> str:
@@ -65,7 +71,7 @@ def aws_time(timezone_offset: int = 0) -> str:
     str
         Returns current time as AWSTime scalar string with optional timezone offset
     """
-    return _formatted_time(datetime.datetime.utcnow(), "%H:%M:%S", timezone_offset)
+    return _formatted_time(datetime.datetime.utcnow(), "%H:%M:%S.%f", timezone_offset)
 
 
 def aws_datetime(timezone_offset: int = 0) -> str:
@@ -81,7 +87,7 @@ def aws_datetime(timezone_offset: int = 0) -> str:
     str
         Returns current time as AWSDateTime scalar string with optional timezone offset
     """
-    return _formatted_time(datetime.datetime.utcnow(), "%Y-%m-%dT%H:%M:%S", timezone_offset)
+    return _formatted_time(datetime.datetime.utcnow(), "%Y-%m-%dT%H:%M:%S.%f", timezone_offset)
 
 
 def aws_timestamp() -> int:


Test patch:
diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py
--- a/tests/functional/test_data_classes.py
+++ b/tests/functional/test_data_classes.py
@@ -1210,13 +1210,18 @@ def test_aws_date_utc():
 def test_aws_time_utc():
     time_str = aws_time()
     assert isinstance(time_str, str)
-    assert datetime.datetime.strptime(time_str, "%H:%M:%SZ")
+    assert datetime.datetime.strptime(time_str, "%H:%M:%S.%fZ")
 
 
 def test_aws_datetime_utc():
     datetime_str = aws_datetime()
-    assert isinstance(datetime_str, str)
-    assert datetime.datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%SZ")
+    assert datetime.datetime.strptime(datetime_str[:-1] + "000Z", "%Y-%m-%dT%H:%M:%S.%fZ")
+
+
+def test_format_time_to_milli():
+    now = datetime.datetime(2024, 4, 23, 16, 26, 34, 123021)
+    datetime_str = _formatted_time(now, "%H:%M:%S.%f", -12)
+    assert datetime_str == "04:26:34.123-12:00:00"
 
 
 def test_aws_timestamp():
@@ -1227,14 +1232,12 @@ def test_aws_timestamp():
 def test_format_time_positive():
     now = datetime.datetime(2022, 1, 22)
     datetime_str = _formatted_time(now, "%Y-%m-%d", 8)
-    assert isinstance(datetime_str, str)
     assert datetime_str == "2022-01-22+08:00:00"
 
 
 def test_format_time_negative():
     now = datetime.datetime(2022, 1, 22, 14, 22, 33)
     datetime_str = _formatted_time(now, "%H:%M:%S", -12)
-    assert isinstance(datetime_str, str)
     assert datetime_str == "02:22:33-12:00:00"