datetime: strftime/strptime sub-second precision specifier (%Nf)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 35.9k
- PR merge metrics
- PR metrics pending
Description
Feature or enhancement
Note: in this issue datetime refers to datetime.datetime, while time refers to datetime.time. This issue doesn't talk about the time module.
Background
Currently, in datetime.strftime/time.strftime, the only sub-second formatting code available is %f, which produces a fixed-width string of microsecond digits, padded from the left with zeros, like "004312".
Despite %f coding for an integer, its zero-padded nature makes it usable in producing the "fraction of the second" part of a datetime string. For example "%T.%f" results in a string like 22:11:55.012345.
Problem
There is still no way for datetime.strftime/time.strftime, alone to produce milliseconds, like 22:11:55.012, or any other fraction length between 1 and 5.
Non-solutions
If you control the code, you can call d.isoformat(timespec="milliseconds"). If all you have is configuration where you specify the datetime.strftime format string, you're in no luck.
Proposal
I'm proposing an expanded variant of the %f datetime.strftime format code in the form of %Nf where N∈{1,2,3,4,5,6}. It would produce datetime microseconds, zero-padded to 6 decimal positions and truncated to N most significant positions.
Examples:
- If
%f→"012345", then:
%6f→"012345"%5f→"01234"%4f→"0123"%3f→"012"%2f→"01"%1f→"0"
- An ISO 8601 format specifying millisecond precision:
%Y-%m-%dT%H:%M:%S.%3f%:z - A human-friendly format with 2 sub-second decimal places:
%Y-%m-%d %H:%M:%S.%2f %z
Issues and choices
Truncation or rounding
For microseconds equal to 012345, should %5f produce:
"01234"(truncation)"01235"(arithmetic rounding)- some other rounding mode?
My proposed change truncates.
Format code syntax
N in %Nf theoretically conflicts with "optional minimum field width" in POSIX libc. The post by @jb2170 highlights that the conflict is very theoretical because %f is a Python extension (and libc doesn't include sub-seconds in struct tm).
This theoretical conflict could still be avoided by following alternative codes:
%.Nfinstead of%Nf. This however was deemed confusing, since having a dot in the format code suggests that the code would also produce a dot in its output, and generally, round a float rather than truncating an integer.%:Nf(where:Nis analogous to the slicing operation) was floated, with no traction.
Overall, the 2 recently active discussion members are in favour of %Nf. I believe that either %Nf or %:Nf could be valid, which makes 3/3 recent posters in favour of %Nf.
Format code corner cases
In my initial implementation, if N falls outside of the supported [1, 6] range, a ValueError is raised. Since format code scanning operates on characters, such detection will only occur for 0, 7, 8 and 9. A code like "%10f" will not be intercepted and will hit the libc - handling always-incorrect multi-digit codes would complicate parsing in the C variant of wrap_strftime so I'm not considering that.
As an alternative to the ValueError, N=0 could produce nothing while values of 7, 8, 9 could act like N=6.
My preference: No change to initial code.
strptime leniency
In my initial implementation a %Nf code in datetime.strptime/time.strptime means that exactly N digits are expected. "%3f" will accept "111" but not "11" or "1111". This is OK since the existing %f code still accepts any number of digits in the [1, 6] range, as before.
Alternatively, a relaxation could be made instead: for a %Nf code, any number of digits from the range [1, N] could be accepted.
My preference: No relaxation.
Prior art
Rust's chrono lib permits %3f, %6f, and %9f (datetime cannot support %9f, since it doesn't go beyond microseconds).
Has this already been discussed elsewhere?
I have already discussed this feature proposal on Discourse
Links to previous discussion of this feature:
https://discuss.python.org/t/add-millisecond-formatting-support-to-datetime-strftime
Implementation
My initial implementation is in this PR:
Linked PRs
- gh-157291
- gh-157704
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked PRs gh-157291 and gh-157704, then read the linked Discourse discussion and the unresolved choices in this issue. Completion requires an agreed format-code syntax and behavior for formatting, invalid widths, truncation, and strptime parsing, with the implementation reflected in the linked work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100