Repository: celery/celery

Issue:
Deprecate iso8601 module
<!--
Please fill this template entirely and do not erase parts of it.
We reserve the right to close without a response
enhancement requests which are incomplete.
-->
# Checklist
<!--
To check an item on the list replace [ ] with [x].
-->

- [x] I have checked the [issues list](https://github.com/celery/celery/issues?q=is%3Aissue+label%3A%22Issue+Type%3A+Enhancement%22+-label%3A%22Category%3A+Documentation%22)
  for similar or identical enhancement to an existing feature.
- [x] I have checked the [pull requests list](https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22Issue+Type%3A+Enhancement%22+-label%3A%22Category%3A+Documentation%22)
  for existing proposed enhancements.
- [x] I have checked the [commit log](https://github.com/celery/celery/commits/main)
  to find out if the if the same enhancement was already implemented in the
  main branch.
- [x] I have included all related issues and possible duplicate issues in this issue
      (If there are none, check this box anyway).

## Related Issues and Possible Duplicates
<!--
Please make sure to search and mention any related issues
or possible duplicates to this issue as requested by the checklist above.

This may or may not include issues in other repositories that the Celery project
maintains or other repositories that are dependencies of Celery.

If you don't know how to mention issues, please refer to Github's documentation
on the subject: https://help.github.com/en/articles/autolinked-references-and-urls#issues-and-pull-requests
-->

#### Related Issues

- None

#### Possible Duplicates

- None

# Brief Summary
<!--
Please include a brief summary of what the enhancement is
and why it is needed.
-->

`celery.utils.iso8601` is historic module which `.parse_iso8601()` can be replaced by `datetime.datetime.fromisoformat()` as Python 3.7 or above is now supported.

Suggest deprecate this module and use `datetime.datetime.fromisoformat()` internally.

# Design

## Architectural Considerations
<!--
If more components other than Celery are involved,
describe them here and the effect it would have on Celery.
-->
None

## Proposed Behavior
<!--
Please describe in detail how this enhancement is going to change the behavior
of an existing feature.
Describe what happens in case of failures as well if applicable.
-->

- `iso8601` module is still usable before v6.0.0, but with addition of deprecation warning
- remove `iso8601` module in v6.0.0
- replace all usage of `parse_iso8601()` in the library by `datetime.datetime.fromisoformat`, with compatibility of Python 3.7 to 3.11

## Proposed UI/UX
<!--
Please provide your ideas for the API, CLI options,
configuration key names etc. that will be adjusted for this enhancement.
-->

N/A

## Diagrams
<!--
Please include any diagrams that might be relevant
to the implementation of this enhancement such as:
* Class Diagrams
* Sequence Diagrams
* Activity Diagrams
You can drag and drop images into the text box to attach them to this issue.
-->
N/A

## Alternatives
<!--
If you have considered any alternative implementations
describe them in detail below.
-->
None



Discussion:
Hey @wongcht :wave:,
Thank you for opening an issue. We will get back to you as soon as we can.
Also, check out our [Open Collective](https://opencollective.com/celery) and consider backing us - every little helps!

We also offer priority support for our sponsors.
If you require immediate assistance please consider sponsoring us.

if that is the case lets do it.

Resolving patch:
diff --git a/celery/result.py b/celery/result.py
--- a/celery/result.py
+++ b/celery/result.py
@@ -14,7 +14,6 @@
 from .app import app_or_default
 from .exceptions import ImproperlyConfigured, IncompleteStream, TimeoutError
 from .utils.graph import DependencyGraph, GraphFormatter
-from .utils.iso8601 import parse_iso8601
 
 try:
     import tblib
@@ -530,7 +529,7 @@ def date_done(self):
         """UTC date and time."""
         date_done = self._get_task_meta().get('date_done')
         if date_done and not isinstance(date_done, datetime.datetime):
-            return parse_iso8601(date_done)
+            return datetime.datetime.fromisoformat(date_done)
         return date_done
 
     @property
diff --git a/celery/utils/iso8601.py b/celery/utils/iso8601.py
--- a/celery/utils/iso8601.py
+++ b/celery/utils/iso8601.py
@@ -37,6 +37,8 @@
 
 from pytz import FixedOffset
 
+from celery.utils.deprecated import warn
+
 __all__ = ('parse_iso8601',)
 
 # Adapted from http://delete.me.uk/2005/03/iso8601.html
@@ -53,6 +55,7 @@
 
 def parse_iso8601(datestring):
     """Parse and convert ISO-8601 string to datetime."""
+    warn("parse_iso8601", "v5.3", "v6", "datetime.datetime.fromisoformat")
     m = ISO8601_REGEX.match(datestring)
     if not m:
         raise ValueError('unable to parse date string %r' % datestring)
diff --git a/celery/utils/time.py b/celery/utils/time.py
--- a/celery/utils/time.py
+++ b/celery/utils/time.py
@@ -13,7 +13,6 @@
 from pytz import utc
 
 from .functional import dictfilter
-from .iso8601 import parse_iso8601
 from .text import pluralize
 
 __all__ = (
@@ -257,7 +256,7 @@ def maybe_iso8601(dt):
         return
     if isinstance(dt, datetime):
         return dt
-    return parse_iso8601(dt)
+    return datetime.fromisoformat(dt)
 
 
 def is_naive(dt):


Test patch:
diff --git a/t/unit/tasks/test_tasks.py b/t/unit/tasks/test_tasks.py
--- a/t/unit/tasks/test_tasks.py
+++ b/t/unit/tasks/test_tasks.py
@@ -13,7 +13,6 @@
 from celery.contrib.testing.mocks import ContextMock
 from celery.exceptions import Ignore, ImproperlyConfigured, Retry
 from celery.result import AsyncResult, EagerResult
-from celery.utils.time import parse_iso8601
 
 try:
     from urllib.error import HTTPError
@@ -889,11 +888,11 @@ def assert_next_task_data_equal(self, consumer, presult, task_name,
         assert task_headers['task'] == task_name
         if test_eta:
             assert isinstance(task_headers.get('eta'), str)
-            to_datetime = parse_iso8601(task_headers.get('eta'))
+            to_datetime = datetime.fromisoformat(task_headers.get('eta'))
             assert isinstance(to_datetime, datetime)
         if test_expires:
             assert isinstance(task_headers.get('expires'), str)
-            to_datetime = parse_iso8601(task_headers.get('expires'))
+            to_datetime = datetime.fromisoformat(task_headers.get('expires'))
             assert isinstance(to_datetime, datetime)
         properties = properties or {}
         for arg_name, arg_value in properties.items():