Title: datetime and time equality and hashing problematic · Issue #116035 · python/cpython · GitHub
Open Graph Title: datetime and time equality and hashing problematic · Issue #116035 · python/cpython
X Title: datetime and time equality and hashing problematic · Issue #116035 · python/cpython
Description: Bug report Bug description: I'm sure folks will pull out reasoning behind this from the inscrutable section in PEP 495 but this behaviour is honestly extremely surprising at best and just flat out wrong on first glance: >>> from datetime...
Open Graph Description: Bug report Bug description: I'm sure folks will pull out reasoning behind this from the inscrutable section in PEP 495 but this behaviour is honestly extremely surprising at best and just flat out ...
X Description: Bug report Bug description: I'm sure folks will pull out reasoning behind this from the inscrutable section in PEP 495 but this behaviour is honestly extremely surprising at best and just flat ...
Opengraph URL: https://github.com/python/cpython/issues/116035
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"datetime and time equality and hashing problematic","articleBody":"# Bug report\r\n\r\n### Bug description:\r\n\r\nI'm sure folks will pull out reasoning behind this from the [inscrutable section in PEP 495](https://peps.python.org/pep-0495/#aware-datetime-equality-comparison) but this behaviour is honestly extremely surprising at best and just flat out wrong on first glance:\r\n\r\n```pycon\r\n\u003e\u003e\u003e from datetime import time, datetime, date\r\n\u003e\u003e\u003e time(fold=1) == time(fold=0)\r\nTrue\r\n\u003e\u003e\u003e hash(time(fold=1)) == hash(time(fold=0))\r\nTrue\r\n\u003e\u003e\u003e datetime(2024, 10, 27, fold=1) == datetime(2024, 10, 27, fold=0)\r\nTrue\r\n\u003e\u003e\u003e hash(datetime(2024, 10, 27, fold=1)) == hash(datetime(2024, 10, 27, fold=0))\r\nTrue\r\n```\r\n\r\nEven when deliberately specifying as explicitly as possible two points in time which are absolutely not the same (these are two points during the UK DST backwards transition later this year):\r\n\r\n```pycon\r\n\u003e\u003e\u003e from zoneinfo import ZoneInfo\r\n\u003e\u003e\u003e tz = ZoneInfo('Europe/London')\r\n\u003e\u003e\u003e dt1 = datetime(2024, 10, 27, 1, 30, fold=0, tzinfo=tz)\r\n\u003e\u003e\u003e dt2 = datetime(2024, 10, 27, 1, 30, fold=1, tzinfo=tz)\r\n\u003e\u003e\u003e dt1 == dt2 \r\nTrue\r\n\u003e\u003e\u003e hash(dt1) == hash(dt2)\r\nTrue\r\n```\r\n\r\nThis has really unpleasant implications for things like storing points in time in a dictionary:\r\n\r\n```pycon\r\n\u003e\u003e\u003e d = {}\r\n\u003e\u003e\u003e d[dt1] = 1\r\n\u003e\u003e\u003e d[dt2] = 2\r\n\u003e\u003e\u003e d[dt1]\r\n2\r\n\u003e\u003e\u003e d[dt2]\r\n2\r\n```\r\n\r\nThe inverse of this issue is reported in https://github.com/python/cpython/issues/115845.\r\n\r\nYes, I know the timestamp can be used:\r\n\r\n```pycon\r\n\u003e\u003e\u003e dt1.timestamp() == dt2.timestamp()\r\nFalse\r\n```\r\n\r\n...but not on `time` objects, and `timestamp` also brings the local machine's timezone into play:\r\n\r\n```pycon\r\n\u003e\u003e\u003e def fold_equal(*args, **kw):\r\n... return datetime(*args, **kw, fold=0).timestamp() == datetime(*args, **kw, fold=1).timestamp()\r\n... \r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 0, 30)\r\nTrue\r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 1, 30)\r\nFalse\r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 2, 30)\r\nTrue\r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 0, 30, tzinfo=ZoneInfo('America/Chicago'))\r\nTrue\r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 1, 30, tzinfo=ZoneInfo('America/Chicago'))\r\nTrue\r\n\u003e\u003e\u003e fold_equal(2024, 10, 27, 2, 30, tzinfo=ZoneInfo('America/Chicago'))\r\nTrue\r\n```\r\n\r\nConcretely, it would be a lot less confusing if:\r\n\r\n- `time` objects, and `datetime` objects where `tzinfo` is `None` are equal and hashed the same only if all of their attributes _including `fold`_ are the same.\r\n\r\n- `datetime` objects where `tzinfo` is not `None` are equal and hashed the same only if they represent the exact same point in time.\r\n\r\n### CPython versions tested on:\r\n\r\n3.12\r\n\r\n### Operating systems tested on:\r\n\r\n_No response_\n\n\u003c!-- gh-linked-prs --\u003e\n### Linked PRs\n* gh-116187\n* gh-116216\n* gh-116217\n\u003c!-- /gh-linked-prs --\u003e\n","author":{"url":"https://github.com/cjw296","@type":"Person","name":"cjw296"},"datePublished":"2024-02-28T08:47:48.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":23},"url":"https://github.com/116035/cpython/issues/116035"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:701c6e8a-bf9f-f7e0-3e96-3415d4e2df1f |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 93CC:322E35:29D6C41:3A37AB5:6A4F5B8E |
| html-safe-nonce | 9392a4f5ff74ef546074be34f24155cc08dc13d2dda72a5b81ec8f76b513b10f |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5M0NDOjMyMkUzNToyOUQ2QzQxOjNBMzdBQjU6NkE0RjVCOEUiLCJ2aXNpdG9yX2lkIjoiNzQ2NDg5NDQ3NDk2OTExMTQzOCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | c3e6d3d084c65961bd5d68dc0ced233da4baa8a28ae29fdb7cb1d540349dbafe |
| hovercard-subject-tag | issue:2158423906 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/116035/issue_layout |
| twitter:image | https://opengraph.githubassets.com/661239bdfcdaaaad6cf36bae4556aa2350e40095e6c883d5cf63e2570604f720/python/cpython/issues/116035 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/661239bdfcdaaaad6cf36bae4556aa2350e40095e6c883d5cf63e2570604f720/python/cpython/issues/116035 |
| og:image:alt | Bug report Bug description: I'm sure folks will pull out reasoning behind this from the inscrutable section in PEP 495 but this behaviour is honestly extremely surprising at best and just flat out ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | cjw296 |
| hostname | github.com |
| expected-hostname | github.com |
| None | b92d11c0aa4a77d54ef4af1078b6a15fb5a70a215b30c4ecf28889d5a8e656d9 |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 4b249b445842943ed31549e027f57a8ade9881ed |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width