A "timestamp" method for date objects

datetime.date objects have a fromtimestamp method, but they do not have a .timestamp() method, as datetime.datetime objects have. That seems inconsistent to me.

Just as the .fromtimestamp() method truncates the time information given by the timestamp (Unix time), a .timestamp() method would have to implicitly use a time - e.g. 0 hours in the local time zone (as date objects are naive). As a work-around, you can create a datetime object from it by using combine:

from datetime import date, datetime

d = date(2020, 1, 1) # on UTC+1 
dt = datetime(2020, 1, 1) # for comparison

print(dt.timestamp())
# 1577833200.0

print(datetime.combine(d, datetime.min.time()).timestamp())
# 1577833200.0

But why not add this method to the datetime.date class?

The short answer is that dates do not have second precision - they’re
entirely to do with whole days. As you demonstrate you can make a
datetime with just a year/month/day supplied, so if you want seconds
precision, just do that.

The other answer is that the datetime module has a lot of rough edges.
There are various third party modules which build on it to provide
better or more complete interfaces. I recommend you try one of these.

Also, all your things above assume a timezone (which is not simply a
GMT/UTC offset). Always tricky. This ambiguity is one of the reasons
people might be loathe to pretend seconds precision when all you have is
a day.

I’ve been dipping my toe in the arrow module:

https://pypi.org/project/arrow/

See if that makes you happier.

Cheers,
Cameron Simpson cs@cskk.id.au

I know about the “rough edges” and that there are alternatives. But I was surprised by the inconsistency - for example there’s also a timetuple method, which returns an object that represents date/time in a higher resolution than ‘day’! - hence the idea. Not sure if this is really useful for everybody in practice - hence the question here :wink: Thanks for your comment!

The timetuple() method returns a time.struct_time, which directly
mirrors the C “struct tm” returned by the POSIX gmtime() and localtime()
functions. This is why it exists.

Cheers,
Cameron Simpson cs@cskk.id.au

Why we are on this topic, why doesn’t python have date.strptime(), and if one wish to convert string to date, one have to use datetime.data.strptime().date().

@2pi360 since 3.7, you have at least a method for ISO format input, date.fromisoformat.

My point in general is: if the Python standard library provides a date class, it should be full-featured. Other libraries (e.g. numpy, and built on that: pandas) only have a datetime type; if you want only the date, you’ll have to floor/normalize the hour, minute, etc.

1 Like

@2pi360 since 3.7, you have at least a method for ISO format input,
date.fromisoformat.

My point in general is: if the Python standard library provides a date class, it should be full-featured.

For the record, I don’t actually disagree with this as a design
principle or objective. But it can be a fair bit of work, and some
things such as date.timestamp() => seconds require a bunch of clarifying
assumptions like “floor/normalize_
the hour, minute, etc” mentioned below and also the timezone. These
complicate the expected semantics.

Other libraries (e.g. numpy, and built on that: pandas) only have a
datetime type; if you want only the date, you’ll have to
floor/normalize the hour, minute, etc.

This I think is the saner way to go. Just work with datetime throughout
when possible. At least it halves the API space and also you’re now
working with a thing which inherently has seconds precision.

Personally, I work with unixtimes throughout (seconds since the UNIX
epoch) if I can, and ony use datetimes etc for presentation. Not always
feasible, but more reliable because the times are at least not ambiguous
(no timezone weirdness, not repeated or missing times due to
summer/winter timezone offset shifts, no utterly weird arithmetic behind
the scenes because of the object’s value is effectively expressed as a
sum of rational numbers whose demoninators vary (looking at you,
days-in-a-month and days-in-a-year)).

Cheers,
Cameron Simpson cs@cskk.id.au

There is a lot of domains where date is the only thing that needed. So carrying seconds with it is not only unnecessary, but potentially problematic (e.g. I write my code with datetime assuming all time is ‘12:00:00’, so I can write date1 == date2 for comparing dates, but then one second is added somewhere and my code stop working).

And datetime.date is already fully functional, with just a few things missing (absence of strptime is what bothers me the most)

Having said that, the closes analogy I see is unicode and non-unicode strings, where multiple domains use strings but just dont care about unicode characters at all. And Python handles this issue by dropping the non-unicode string…

If you feel its important to add, you could open an issue on BPO to discuss it, and if there’s general agreement that it would be a net improvement, you could submit a PR. It should be quite doable if you have a decent level of Python experience (unless it also needs to be implemented as a C extension, in which case you would need to have some basic C experience too—can’t remember if it is).

There is a lot of domains where date is the only thing that needed. So carrying seconds with it is not only unnecessary, but potentially problematic (e.g. I write my code with datetime assuming all time is ‘12:00:00’, so I can write date1 == date2 for comparing dates, but then one second is added somewhere and my code stop working).

Doesn’t this situation inherently display the trickiness of assuming you
can get a seconds timestamp from a date? The relationship seems very
fragile.

That said, I agree that if you’re working only with dates, use “date”.
Then there are no additional ill specified hours etc fields to break
your comparisons. But going with that, I would not expect to extract a
seconds precision value.

And datetime.date is already fully functional, with just a few things missing (absence of strptime is what bothers me the most)

Yes, strptime would be good. It might merely be missing (nobody
bothered) or the datetime.strptime might call the C/POSIX strptime
directly using a struct tm or similar, and to do that you need to invent
the missing fields (even if simply all zeroes).

If that is the case I would be inclined to argue that date should npt
have a strptime method, and that the required conversion to datetime
should be overt to make it clear what assumptions about hours, timezones
etc were involved in that conversion.

Having said that, the closes analogy I see is unicode and non-unicode strings, where multiple domains use strings but just dont care about unicode characters at all. And Python handles this issue by dropping the non-unicode string…

That’s an unfair comparison.

There aren’t “Unicode characters” as such - it is meant to cover all
existing human character sets. (I guess some new things like emojis
might be defined only in Unicode, so they probably count as “Unicode
characters”.)

What Python did was drop support for the old 8-bit str type, which was
(a) too small for anything but parochial text and (b) had no
association with a character set - it was just bytes underneath and if
they didn’t match up with your external character mapping they were just
nonsense.

Storing all text as Unicode means you can store any text, and the
transcription into files or onto displays like terminals or web pages
involves specifying the encoding of the characters, making overt what is
going on. Likewise the reverse: you can’t get a Unicode string from some
bytes without specifying what encoding the bytes use.

This is analogous to making a datetime from a date - you need to define
the conversion (eg how the new hh/mm/ss and timezone are generated). You
can punt on the timezone with datetime, getting a “naive” datetime. But
the object inherently has a timezone field, even when the field says
“unspecified”.

Cheers,
Cameron Simpson cs@cskk.id.au

3 Likes

Just to clarify one point, it didn’t drop it at all; it was just renamed bytes and the semantics were changed to no longer implicitly and silently treat it like a string of of ASCII/Latin-1 characters when it was really a sequence of arbitrary 8-bit binary bytes, which ends up being a major footgun for those who assumed it was the former.