Skip to content

gh-97517: Improve docstrings for strptime and strftime methods in datetime module #31761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 163 additions & 5 deletions Lib/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,15 +1025,78 @@ def __repr__(self):


def ctime(self):
"Return ctime() style string."
"""Return ctime() style string, such as 'Fri Oct 5 00:00:00 2001'"""
weekday = self.toordinal() % 7 or 7
return "%s %s %2d 00:00:00 %04d" % (
_DAYNAMES[weekday],
_MONTHNAMES[self._month],
self._day, self._year)

def strftime(self, fmt):
"Format using strftime()."
"""Format date or datetime given the format string `fmt`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these changes are overkill for docstrings, and they impose a maintenance burden to keep them up to date. How about just containing a reference to the actual documentation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a good compromise could be including the most common ones, and then having a link to the remaining ones?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ericvsmith bump?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any examples of functions (preferably in datetime) that contain pointers to their documentation? Or is it just assumed that people know how to look it up?

I'm not crazy about trying to decide what common codes would be.

You should probably reach out to the module maintainer for more input on this. I'm just an interested bystander. @pganssle @abalkin: Any thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the full documentation be available inline would be the optimal solution, but I'd settle for a quick link - as long as there's something to help avoid the frustrating web search and scroll through the massive amount of online documentation. It could be interesting to look into generating documentation from docstrings and the like (using a tool like pdoc) so that there would be as much difficulty in terms of keeping documentation up-to-date in multiple places, but I'd say that's well beyond the scope of this particular PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Links are not done anywhere in the docs from what I can tell. Below are all instances of a link to the docs in cpython.

iOS/testbed/iOSTestbedTests/iOSTestbedTests.m:    // See https://docs.python.org/3/library/os.html#python-utf-8-mode.
InternalDocs/garbage_collector.md:[`gc.get_threshold()`](https://docs.python.org/3/library/gc.html#gc.get_threshold)
InternalDocs/parser.md:  [`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError) is".
InternalDocs/parser.md:[`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError)
InternalDocs/parser.md:>   [`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError)
InternalDocs/changing_grammar.md:  [`pyclbr`](https://docs.python.org/3/library/pyclbr.html#module-pyclbr).
Tools/c-analyzer/c_parser/preprocessor/common.py:    # See: https://docs.python.org/3/library/sys.html#sys.platform
Lib/asyncio/coroutines.py:    # See: https://docs.python.org/3/library/asyncio-dev.html#asyncio-debug-mode.
Lib/multiprocessing/spawn.py:        section in https://docs.python.org/3/library/multiprocessing.html
grep: Lib/multiprocessing/__pycache__/spawn.cpython-314.pyc: binary file matches
Lib/importlib/_bootstrap.py:                # See # https://docs.python.org/3/library/importlib.html#importlib.abc.Loader.load_module
Lib/test/test_unittest/test_discovery.py:        # https://docs.python.org/3/library/unittest.html#load-tests-protocol
Lib/idlelib/run.py:            "https://docs.python.org/3/library/idle.html#startup-failure",
Lib/idlelib/pyshell.py:            "https://docs.python.org/3/library/idle.html#startup-failure",
Lib/venv/__init__.py:                       'see https://docs.python.org/3/library/venv.html\n')

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we're generally not including URLs to the specific version of the docs that's hosted on docs.python.org.

I think this should use a sentence like “See the documentation for a list of format codes”.
We can't include a full list, since that's platform-specific and there's no portable API to get such a list.
We could include the portable codes, but as said already, repeating them 3 times isn't very maintainable.


The following format codes can be used within the format string. Entries
marked with * are localised.

* %f microseconds as a zero-padded 6 digit decimal

* %S seconds (zero-padded)

* %M minutes (zero-padded)

* %H hour (24-hour clock)

* %I hour (12-hour clock)

* %p AM-PM*

* %z UTC offset in the form (+-)HHMM[SS[.ffffff]]

* %Z time zone name

* %a abbreviated weekday*

* %A full weekday*

* %w weekday as decimal (Sunday = 0 through to Saturday = 6)

* %u ISO 8601 weekday where 1 is Monday (1 - 7)

* %d day of month as zero-padded decimal (01 - 31)

* %m month as a zero-padded decimal (01 - 12)

* %b abbreviated month*

* %B full month*

* %j day of the year as a zero-padded decimal (001 - 3666)

* %U week number of the year with Sunday as the first day (00 - 53)

* %W week number of the year with Monday as the first day (00 - 53)

* %V ISO 8601 week number (01 - 53)

* %y year without century (00, 01, ...)

* %Y year with century (0001, 0002, ..., 2000, ...)

* %G ISO 8601 year with century

* %X locale's appropriate time representation

* %x locale's appropriate date representation

* %c date and time in locale's appropriate format

* %% the literal '%' character

>>> datetime(2010, 1, 1).strftime('%d %b %Y')
'01 Jan 2010'
"""
# Documentation above should keep dot points updated with
# datetime.strptime, as the same escape sequences are used.
return _wrap_strftime(self, fmt, self.timetuple())

def __format__(self, fmt):
Expand Down Expand Up @@ -1551,9 +1614,40 @@ def fromisoformat(cls, time_string):


def strftime(self, fmt):
"""Format using strftime(). The date part of the timestamp passed
to underlying strftime should not be used.
"""Format time given the format string `fmt`

The following format codes can be used within the format string. Entries
marked with * are localised.

* %f microseconds as a zero-padded 6 digit decimal

* %S seconds (zero-padded)

* %M minutes (zero-padded)

* %H hour (24-hour clock)

* %I hour (12-hour clock)

* %p AM-PM*

* %z UTC offset in the form (+-)HHMM[SS[.ffffff]]

* %Z time zone name

* %X locale's appropriate time representation

* %% the literal '%' character

The date part of the timestamp passed to underlying strftime should not
be used.

>>> time(10, 42).strftime('%M:%S')
'10:42'
"""
# Documentation above should keep dot points updated with
# datetime.strftime, as a subset of the same escape sequences are used.

# The year must be >= 1000 else Python's strftime implementation
# can raise a bogus exception.
timetuple = (1900, 1, 1,
Expand Down Expand Up @@ -2057,7 +2151,71 @@ def __str__(self):

@classmethod
def strptime(cls, date_string, format):
'string, format -> new datetime parsed from a string (like time.strptime()).'
"""Parses a datetime from a string given a format specification, much
like strptime() in the time module.

The following format codes can be used within the format string. Entries
marked with * are localised.

* %f microseconds as a zero-padded 6 digit decimal

* %S seconds (zero-padded)

* %M minutes (zero-padded)

* %H hour (24-hour clock)

* %I hour (12-hour clock)

* %p AM-PM*

* %z UTC offset in the form (+-)HHMM[SS[.ffffff]]

* %Z time zone name

* %a abbreviated weekday*

* %A full weekday*

* %w weekday as decimal (Sunday = 0 through to Saturday = 6)

* %u ISO 8601 weekday where 1 is Monday (1 - 7)

* %d day of month as zero-padded decimal (01 - 31)

* %m month as a zero-padded decimal (01 - 12)

* %b abbreviated month*

* %B full month*

* %j day of the year as a zero-padded decimal (001 - 3666)

* %U week number of the year with Sunday as the first day (00 - 53)

* %W week number of the year with Monday as the first day (00 - 53)

* %V ISO 8601 week number (01 - 53)

* %y year without century (00, 01, ...)

* %Y year with century (0001, 0002, ..., 2000, ...)

* %G ISO 8601 year with century

* %X locale's appropriate time representation

* %x locale's appropriate date representation

* %c date and time in locale's appropriate format

* %% the literal '%' character

>>> datetime.strptime('01 Jan 2010', '%d %b %Y')
datetime.datetime(2010, 1, 1, 0, 0)
"""
# Documentation above should keep dot points updated with
# datetime.strftime, as the same escape sequences are used.
import _strptime
return _strptime._strptime_datetime(cls, date_string, format)

Expand Down