Skip to content
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

datetime needs an "epoch" method #46988

Closed
tebeka mannequin opened this issue May 1, 2008 · 67 comments
Closed

datetime needs an "epoch" method #46988

tebeka mannequin opened this issue May 1, 2008 · 67 comments
Assignees
Labels
docs Documentation in the Doc dir type-feature A feature request or enhancement

Comments

@tebeka
Copy link
Mannequin

tebeka mannequin commented May 1, 2008

BPO 2736
Nosy @malemburg, @tim-one, @jribbens, @amauryfa, @mdickinson, @abalkin, @pitrou, @catlee, @vstinner, @bitdancer
Files
  • add-datetime-totimestamp-method.diff: Implementation of datetime.datetime.timetuple and tests.
  • add-datetime-totimestamp-method-docs.diff
  • datetime_totimestamp-3.patch
  • issue2736-doc.diff
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/abalkin'
    closed_at = <Date 2011-04-25.17:03:30.218>
    created_at = <Date 2008-05-01.21:03:25.299>
    labels = ['type-feature', 'docs']
    title = 'datetime needs an "epoch" method'
    updated_at = <Date 2012-06-08.16:41:09.959>
    user = 'https://github.com/tebeka'

    bugs.python.org fields:

    activity = <Date 2012-06-08.16:41:09.959>
    actor = 'belopolsky'
    assignee = 'belopolsky'
    closed = True
    closed_date = <Date 2011-04-25.17:03:30.218>
    closer = 'belopolsky'
    components = ['Documentation']
    creation = <Date 2008-05-01.21:03:25.299>
    creator = 'tebeka'
    dependencies = []
    files = ['10251', '10256', '12329', '21565']
    hgrepos = []
    issue_num = 2736
    keywords = ['patch']
    message_count = 67.0
    messages = ['66045', '66140', '66532', '66539', '66601', '66610', '75723', '75899', '75900', '75902', '75903', '75904', '75912', '76003', '76324', '76327', '76329', '76331', '76332', '76340', '76344', '76345', '76351', '76352', '77650', '77651', '99545', '103875', '106229', '106230', '106249', '106251', '106252', '106254', '106255', '124197', '124203', '124204', '124225', '124230', '124231', '124237', '124245', '124248', '124252', '124255', '124256', '124257', '124259', '132695', '132697', '132818', '132977', '132994', '133008', '133009', '133011', '133037', '133039', '133053', '133056', '133058', '133072', '133207', '133245', '134395', '162533']
    nosy_count = 23.0
    nosy_names = ['lemburg', 'tim.peters', 'ping', 'jribbens', 'guettli', 'amaury.forgeotdarc', 'mark.dickinson', 'davidfraser', 'belopolsky', 'pitrou', 'andersjm', 'catlee', 'vstinner', 'tomster', 'werneck', 'hodgestar', 'Neil Muller', 'erik.stephens', 'steve.roberts', 'r.david.murray', 'vivanov', 'python-dev', 'Jay.Taylor']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue2736'
    versions = ['Python 3.3']

    @tebeka
    Copy link
    Mannequin Author

    tebeka mannequin commented May 1, 2008

    If you try to convert datetime objects to seconds since epoch and back
    it will not work since the microseconds get lost:

    >>> dt = datetime(2008, 5, 1, 13, 35, 41, 567777)
    >>> seconds = mktime(dt.timetuple())
    >>> datetime.fromtimestamp(seconds) == dt
    False
    
    Current fix is to do
    >>> seconds += (dt.microsecond / 1000000.0)
    >>> datetime.fromtimestamp(seconds) == dt
    True

    @tebeka tebeka mannequin added type-bug An unexpected behavior, bug, or error stdlib Python modules in the Lib dir labels May 1, 2008
    @werneck
    Copy link
    Mannequin

    werneck mannequin commented May 3, 2008

    That's expected as mktime is just a thin wrapper over libc mktime() and
    it does not expect microseconds. Changing time.mktime doesn't seems an
    option, so the best alternative is to implement a method in datetime
    type. Is there a real demand for C code implementing this to justify it?

    @hodgestar
    Copy link
    Mannequin

    hodgestar mannequin commented May 10, 2008

    Attached a patch which adds a .totimetuple(...) method to
    datetime.datetime and tests for it.

    The intention is that the dt.totimetuple(...) method is equivalent to:
    mktime(dt.timetuple()) + (dt.microsecond / 1000000.0)

    @hodgestar
    Copy link
    Mannequin

    hodgestar mannequin commented May 10, 2008

    Patch adding documentation for datetime.totimestamp(...).

    @tebeka
    Copy link
    Mannequin Author

    tebeka mannequin commented May 11, 2008

    I think the name is not good, should be "toepoch" or something like that.

    @NeilMuller
    Copy link
    Mannequin

    NeilMuller mannequin commented May 11, 2008

    datetime has fromtimestamp already, so using totimestamp keeps naming
    consistency (see toordinal and fromordinal).

    @vstinner
    Copy link
    Member

    See also bpo-1673409

    @vstinner
    Copy link
    Member

    I like the method, but I have some comments about the new method:

    • datetime_totimestamp() is not well indented
    • "PyObject *time" should be defined at the before the first
      instruction
    • why not using "if (time == NULL) return NULL;" directly instead of
      using a block in case of time is not NULL?
    • there are reference leaks: timetuple, timestamp and
      PyFloat_FromDouble()

    I wrote a similar patch before reading
    add-datetime-totimestamp-method.diff which does exactly the same... I
    attach my patch but both should be merged.

    @vstinner
    Copy link
    Member

    Here is a merged patch of the three patches. Except the C
    implementation of datetime_totimestamp() (written by me), all code is
    written by hodgestar.

    @abalkin
    Copy link
    Member

    abalkin commented Nov 15, 2008

    I would like to voice my opposition the totimestamp method.

    Representing time as a float is a really bad idea (originated at
    Microsoft as I have heard). In addition to the usual numeric problems
    when dealing with the floating point, the resolution of the floating
    point timestamp varies from year to year making it impossible to
    represent high resolution historical data.

    In my opinion both time.time() returning float and
    datetime.fromtimestamp() taking a float are both design mistakes and
    adding totimestamp that produces a float will further promote a bad
    practice.

    I would not mind integer based to/from timestamp methods taking and
    producing seconds or even (second, microsecond) tuples, but I don't
    think changing fromtimestamp behavior is an option.

    @vstinner
    Copy link
    Member

    Le Saturday 15 November 2008 02:15:30 Alexander Belopolsky, vous avez écrit :

    I don't think changing fromtimestamp behavior is an option.

    It's too late to break the API (Python3 is in RC stage ;-)), but we can create
    new methods like:
    datetime.fromepoch(seconds, microseconds=0) # (int/long, int)
    datetime.toepoch() -> (seconds, microseconds) # (int/long, int)

    @abalkin
    Copy link
    Member

    abalkin commented Nov 15, 2008

    On Fri, Nov 14, 2008 at 8:37 PM, STINNER Victor <report@bugs.python.org> wrote:

    .. but we can create new methods like:
    datetime.fromepoch(seconds, microseconds=0) # (int/long, int)

    While 1970 is the most popular epoch, I've seen 1900, 2000 and even
    2035 (!) being used as well. Similarly, nanoseconds are used in high
    resolution time sources at least as often as microseconds. This makes
    fromepoch() ambiguous and it is really unnecessary because it can be
    written as epoch + timedelta(0, seconds, microseconds).

    datetime.toepoch() -> (seconds, microseconds) # (int/long, int)

    I would much rather have divmod implemented as you suggested in
    bpo-2706 . Then toepoch is simply

    def toepoch(d):
        x, y = divmod(d, timedellta(0, 1))
        return x, y.microseconds

    @vstinner
    Copy link
    Member

    Le Saturday 15 November 2008 04:17:50 Alexander Belopolsky, vous avez écrit :

    it is really unnecessary because it can be
    written as epoch + timedelta(0, seconds, microseconds).

    I tried yesterday and it doesn't work!

    datetime.datetime(1970, 1, 1, 1, 0)
    >>> t1 = epoch + timedelta(seconds=-1660000000)
    >>> t2 = datetime.fromtimestamp(-1660000000)
    >>> t2
    datetime.datetime(1917, 5, 26, 1, 53, 20)
    >>> t1 - t2
    datetime.timedelta(0)
    >>> t2 = datetime.fromtimestamp(-1670000000)
    >>> t2
    datetime.datetime(1917, 1, 30, 7, 6, 40)
    >>> t1 = epoch + timedelta(seconds=-1670000000)
    >>> t1 - t2
    datetime.timedelta(0, 3600)

    We lost an hour durint the 1st World War :-)

    Whereas my implementation using mktime() works:

    -1670000000.0

    @andersjm
    Copy link
    Mannequin

    andersjm mannequin commented Nov 18, 2008

    Any thoughts to time zone/DST handling for naive datetime objects? E.g.
    suppose the datetime object was created by .utcnow or .utcfromtimestamp.

    For aware datetime objects, I think the time.mktime(dt.timetuple())
    approach doesn't work; the tz info is lost in the conversion to time tuple.

    @davidfraser
    Copy link
    Mannequin

    davidfraser mannequin commented Nov 24, 2008

    ----- "Alexander Belopolsky" <report@bugs.python.org> wrote:

    Alexander Belopolsky <belopolsky@users.sourceforge.net> added the
    comment:

    I would like to voice my opposition the totimestamp method.

    Representing time as a float is a really bad idea (originated at
    Microsoft as I have heard). In addition to the usual numeric problems
    when dealing with the floating point, the resolution of the floating
    point timestamp varies from year to year making it impossible to
    represent high resolution historical data.

    In my opinion both time.time() returning float and
    datetime.fromtimestamp() taking a float are both design mistakes and
    adding totimestamp that produces a float will further promote a bad
    practice.

    The point for me is that having to interact with Microsoft systems that require times means that the conversions have to be done. Is it better to have everybody re-implement this, with their own bugs, or to have a standard implementation? I think it's clearly better to have it as a method on the object. Of course, we should put docs in describing the pitfalls of this approach...

    @abalkin
    Copy link
    Member

    abalkin commented Nov 24, 2008

    On Mon, Nov 24, 2008 at 9:04 AM, David Fraser <report@bugs.python.org> wrote:
    ...

    The point for me is that having to interact with Microsoft systems that require times means that the conversions have to be done.

    I did not see the "epoch" proposal as an interoperability with
    Microsoft systems feature. If this is the goal, a deeper analysis of
    the Microsoft standards is in order. For example, what is the valid
    range of the floating point timestamp? What is the range for which
    fromepoch (float to datetime) translation is valid? For example, if
    all floats are valid timestamps, then fromepoch can be limited to +/-
    2**31 or to a smaller range where a float has enough precision to
    roundtrip microseconds.

    Is it better to have everybody re-implement this, with their own bugs, or to have a standard implementation?

    As far as I know, interoperability with Microsoft systems requires
    re-implementation of their bugs many of which are not documented. For
    example, OOXML requires that 1900 be treated as a leap year at least
    in some cases. When you write your own implementation, at least you
    have the source code to your own bugs.

    I think it's clearly better to have it as a method on the object. Of course, we should put docs in describing the pitfalls of this approach...

    Yes, having a well documented high resolution "time since epoch" to
    "local datetime" method in the datetime module is helpful if
    non-trivial timezones (such as the one Victor lives in) are supported.
    However, introducing floating point pitfalls into the already
    overcomplicated realm of calendar calculations would be a mistake.

    I believe the correct approach would be to extend fromtimestamp (and
    utcfromtimestamp) to accept a (seconds, microseconds) tuple as an
    alternative (and in addition) to the float timestamp. Then
    totimestamp can be implemented to return such tuple that
    fromtimestamp(totimestamp(dt) == dt for any datetime dt and
    totimestamp(fromtimestamp((s,us))) == (s, us) for any s and us within
    datetime valid range (note that s will have to be a long integer to
    achieve that).

    In addition exposing the system gettimeofday in the time module to
    produce (s, us) tuples may be helpful to move away from float
    timestamps produced by time.time(), but with totimestamp as proposed
    above that would be equivalent to datetime.now().totimestamp().

    @vstinner
    Copy link
    Member

    About the timestamp, there are many formats:

    (a) UNIX: 32 bits signed integer, number of seconds since the 1st
    january 1970.

    • file format: gzip header, Portable Executable (PE, Windows),
      compiled python script header (.pyc/.pyo)
    • file system: ext2 and ext3

    (b) UNIX64: 64 bits signed integer, number of seconds since the 1st
    january 1970

    • file format: Gnome keyring

    (c) UNIX: 32 bits unsigned integer, number of seconds since the 1st
    january 1904

    • file format: True Type Font (.ttf), iTunes database, AIFF, .MOV

    (d) UUID60: 60 bits unsigned integer, number of 1/10 microseconds
    since the 15st october 1582

    • all formats using UUID version 1 (also known as "GUID" in the
      Microsoft world)

    (e) Win64: 64 bits unsigned integer, number of 1/10 microseconds since
    the 1st january 1601

    • file format: Microsoft Office documents (.doc, .xls, etc), ASF
      video (.asf), Windows link (.lnk)
    • file system: NTFS

    (f) MSDOS DateTime or TimeDate: bitfield with 16 bits for the date and
    16 bits for the time. Time precision is 2 seconds, year is in range
    [1980; 2107]

    • file format: Windows link (.lnk), CAB archive (.cab), Word document
      (.doc), ACE archive (.ace), ZIP archive (.zip), RAR achive (.rar)

    @vstinner
    Copy link
    Member

    Timedelta formats:

    (a) Win64: 64 bits unsigned integer, number of 1/10 microsecond

    • file format: Microsoft Word document (.doc), ASF video (.asf)

    (b) 64 bits float, number of seconds

    • file format: AMF metadata used in Flash video (.flv)

    Other file formats use multiple numbers to store a duration:

    [AVI video]

    • 3 integers (32 bits unsigned): length, rate, scale
    • seconds = length / (rate / scale)
    • (seconds = length * scale / rate)

    [WAV audio]

    • 2 integers (32 bits unsigned): us_per_frame, total_frame
    • seconds = total_frame * (1000000 / us_per_frame)

    [Ogg Vorbis]

    • 2 integers: sample_rate (32 bits unsigned), position (64 bits
      unsigned)
    • seconds = position / sample_rate

    @abalkin
    Copy link
    Member

    abalkin commented Nov 24, 2008

    That's an impressive summary, but what is your conclusion? I don't
    see any format that will benefit from a subsecond
    timedelta.totimestamp(). Your examples have either multisecond or
    submicrosecond resolution.

    On Mon, Nov 24, 2008 at 11:00 AM, STINNER Victor <report@bugs.python.org> wrote:

    STINNER Victor <victor.stinner@haypocalc.com> added the comment:

    Timedelta formats:

    (a) Win64: 64 bits unsigned integer, number of 1/10 microsecond

    • file format: Microsoft Word document (.doc), ASF video (.asf)

    (b) 64 bits float, number of seconds

    • file format: AMF metadata used in Flash video (.flv)

    Other file formats use multiple numbers to store a duration:

    [AVI video]

    • 3 integers (32 bits unsigned): length, rate, scale
    • seconds = length / (rate / scale)
    • (seconds = length * scale / rate)

    [WAV audio]

    • 2 integers (32 bits unsigned): us_per_frame, total_frame
    • seconds = total_frame * (1000000 / us_per_frame)

    [Ogg Vorbis]

    • 2 integers: sample_rate (32 bits unsigned), position (64 bits
      unsigned)
    • seconds = position / sample_rate

    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue2736\>


    @vstinner
    Copy link
    Member

    Ooops, timestamp (c) is the *Mac* timestamp: seconds since the 1st
    january 1904.

    what is your conclusion?

    Hum, it's maybe not possible to choose between integer and float. Why
    not supporting both? Example:

    • totimestamp()->int: truncate microseconds
    • totimestamp(microseconds=True)->float: with microseconds

    Attached file (timestamp.py) is a module to import/export timestamp in
    all listed timestamp formats. It's written in pure Python.
    ----------------

    >>> import timestamp
    >>> from datetime import datetime
    >>> now = datetime.now()
    >>> now
    datetime.datetime(2008, 11, 24, 18, 7, 50, 216762)
    
    >>> timestamp.exportUnix(now)
    1227550070
    >>> timestamp.exportUnix(now, True)
    1227550070.2167621
    >>> timestamp.exportMac(now)
    3310394870L
    >>> timestamp.exportWin64(now)
    128720236702167620L
    >>> timestamp.exportUUID(now)
    134468428702167620L
    
    >>> timestamp.importMac(3310394870)
    datetime.datetime(2008, 11, 24, 18, 7, 50)
    >>> timestamp.importUnix(1227550070)
    datetime.datetime(2008, 11, 24, 18, 7, 50)
    >>> timestamp.importUnix(1227550070.2167621)
    datetime.datetime(2008, 11, 24, 18, 7, 50, 216762)

    It supports int and float types for import and export.

    @abalkin
    Copy link
    Member

    abalkin commented Nov 24, 2008

    On Mon, Nov 24, 2008 at 12:13 PM, STINNER Victor <report@bugs.python.org> wrote:
    ..

    Hum, it's maybe not possible to choose between integer and float. Why
    not supporting both? Example:

    • totimestamp()->int: truncate microseconds
    • totimestamp(microseconds=True)->float: with microseconds

    I would still prefer totimestamp()->(int, int) returning (sec, usec)
    tuple. The important benefit is that such totimestamp() will not
    loose information and will support more formats than either of your
    ->int or ->float variants. The ->int can then be spelt simply as
    totimestamp()[0] and on systems with numpy (which is likely for users
    that deal with floats a lot), totimestamp(microseconds=True) is simply
    dot([1, 1e-6], totimestamp()). (and s,us = totimestamp(); return s +
    us * 1e-6 is not that hard either.)

    @vstinner
    Copy link
    Member

    > Hum, it's maybe not possible to choose between integer and float. Why
    > not supporting both? Example:
    > - totimestamp()->int: truncate microseconds
    > - totimestamp(microseconds=True)->float: with microseconds

    I would still prefer totimestamp()->(int, int) returning (sec, usec)
    tuple. The important benefit is that such totimestamp() will not
    loose information

    Right, I prefer your solution ;-)

    @abalkin
    Copy link
    Member

    abalkin commented Nov 24, 2008

    On Mon, Nov 24, 2008 at 12:34 PM, STINNER Victor <report@bugs.python.org> wrote:
    ..

    > I would still prefer totimestamp()->(int, int) returning (sec, usec)
    > tuple. The important benefit is that such totimestamp() will not
    > loose information

    Right, I prefer your solution ;-)

    Great! What do you think about extending fromtimestamp(timestamp[,
    tz]) and utcfromtimestamp(timestamp) to accept a tuple for the
    timestamp?

    Also, are you motivated enough to bring this up on python-dev to get a
    community and BDFL blessings? I think this has a chance to be
    approved.

    @davidfraser
    Copy link
    Mannequin

    davidfraser mannequin commented Nov 24, 2008

    ----- "STINNER Victor" <report@bugs.python.org> wrote:

    STINNER Victor <victor.stinner@haypocalc.com> added the comment:

    Timedelta formats:

    (a) Win64: 64 bits unsigned integer, number of 1/10 microsecond

    • file format: Microsoft Word document (.doc), ASF video (.asf)

    (b) 64 bits float, number of seconds

    • file format: AMF metadata used in Flash video (.flv)

    There are also the PyWinTime objects returned by PythonWin COM calls which are basically FILETIMEs
    I don't have time to get the details now but I recently submitted a patch to make them work with milliseconds - see http://sourceforge.net/tracker/index.php?func=detail&aid=2209864&group_id=78018&atid=551954 (yes I know this is a bit off-topic here)

    @vstinner
    Copy link
    Member

    belopolsky will be happy to see this new version of my patch:

    • datetime.totimestamp() => (seconds, microseconds): two integers
    • datetime.totimestamp() implement don't use Python time.mktime() but
      directly the C version of mktime() because time.mktime() creates a
      float value
    • fix time.mktime() to support the timestamp -1 (first second before
      the epoch) to make it consistent with datetime.totimestamp() which
      also support this value
    • fix documentation: it's microseconds (10^-6) and not milliseconds
      (10^-3)

    @vstinner
    Copy link
    Member

    About mktime() -> -1: see the bpo-1726687 (I found the fix in this
    issue).

    Next job will be to patch datetime.(utc)fromtimestamp() to support
    (int, int). I tried to write such patch but it's not easy because
    fromtimestamp() will support: int, long, float, (int, int), (int,
    long), (long, int) and (long, long). And I don't know if a "long"
    value can be converted to "time_t".

    @AlexanderBelopolsky
    Copy link
    Mannequin

    AlexanderBelopolsky mannequin commented Feb 18, 2010

    Victor,

    As you explain in your own documentation, the proposed method is equivalent to (time.mktime(self.timetuple()), self.microsecond), so all it does is replacing a less than a one-liner. Moreover, I am not sure time.mktime(self.timetuple()) is something that people would want to do with a TZ-aware datetime. If the tzinfo of the datetime object does not match the system TZ used by mktime, the result will be quite misleading.

    On the patch itself:

    1. See my comment at bpo-1726687 about the tm_wday == 1 typo.

    2. I don't think time_t to long cast is safe on all platforms.

    @AlexanderBelopolsky AlexanderBelopolsky mannequin changed the title datetime needs and "epoch" method datetime needs an "epoch" method Feb 18, 2010
    @AlexanderBelopolsky
    Copy link
    Mannequin

    AlexanderBelopolsky mannequin commented Dec 17, 2010

    On Fri, Dec 17, 2010 at 12:17 PM, Antoine Pitrou <report@bugs.python.org> wrote:
    ..

    > 1. Different application may need different epoch and retained
    > precision depends on the choice of the epoch.

    But then why does fromtimestamp() exist?

    A better question is why datetime.utcfromtimestamp(s) exists given
    that it is actually longer than equivalent EPOCH + timedelta(0, s)? I
    am not responsible for either of these methods, but at least
    datetime.fromtimestamp(s, tz) is well defined for any timezone and
    timestamp unlike its inverse.

    And returning a (seconds, microseconds) tuple does retain the precision.

    It does, but it does not help much those who want a float - they would
    still need another line of code. Note that with divmod(timedelta,
    timedelta), you can now easily extract (seconds, microseconds) or
    any other tuple like (weeks, days, seconds. microseconds) from
    timedelta objects. See msg75904 above.

    > 2. The code above works only on naive datetime objects assumed to be
    > in UTC.

    So, if the "trivial" code doesn't work, you can't bring it up as an
    argument against shipping this functionality, right?

    Well, no one has come up with the code that does work so far. Note
    that timetuple path does not work either because it does not fill
    tm_isdst correctly. The only solution I can think of for having
    proper inverse to fromtimestamp() is to add isdst to datetime objects.
    This would allow correct round-tripping between datetime and
    timetuple and datetime and timestamp.

    > 3. While it is not hard to extend the timestamp(t) code to cover aware
    > datetime objects that use fixed offset tzinfo such as those with
    > tzinfo set to a datetime.timezone instance, it is not well defined for
    > the "smart" tzinfo implementations that do automatic DST adjustment.

    Still, fromtimestamp() exists and apparently fulfills people's
    expectations. So why can't the same strategy be used for totimestamp()
    as well?

    Because in certain timezones fromtimestamp() can return the same
    datetime value for different timestamps and some datetime values do
    not have a corresponding timestamp. I have not seen a working
    proposal on how to handle these issues yet. You are asking to provide
    an inverse to an existing function simply because the function exists.
    But the function in question is not invertible.

    @pitrou
    Copy link
    Member

    pitrou commented Dec 17, 2010

    >> 1. Different application may need different epoch and retained
    >> precision depends on the choice of the epoch.
    >
    > But then why does fromtimestamp() exist?

    A better question is why datetime.utcfromtimestamp(s) exists given
    that it is actually longer than equivalent EPOCH + timedelta(0, s)?

    ??? EPOCH is not even a constant in the datetime module.

    And regardless, the point is *not* the number of characters typed, but
    how easy it is to come up with the solution. Calling the appropriate
    (and appropriately-named) method is much easier than coming up with the
    right datetime arithmetic incantation. It's Python, not Perl. "There
    should be one obvious way to do it".

    > And returning a (seconds, microseconds) tuple does retain the precision.
    >

    It does, but it does not help much those who want a float - they would
    still need another line of code.

    Yes, but a very obvious one at least.

    Note that with divmod(timedelta,
    timedelta), you can now easily extract (seconds, microseconds) or
    any other tuple like (weeks, days, seconds. microseconds) from
    timedelta objects.

    Do you think many users even think of calling divmod() timedelta
    objects? I don't, personally.

    You apparently hold the opinion that the datetime module should be
    reserved for experts in arithmetic over dates, times and timedeltas. But
    it's not. It's the Python stdlib and it should provide reasonably
    high-level tools to do the job.

    @AlexanderBelopolsky
    Copy link
    Mannequin

    AlexanderBelopolsky mannequin commented Dec 17, 2010

    On Fri, Dec 17, 2010 at 1:17 PM, Antoine Pitrou <report@bugs.python.org> wrote:
    ..

    > A better question is why datetime.utcfromtimestamp(s) exists given
    > that it is actually longer than equivalent EPOCH + timedelta(0, s)?

    ??? EPOCH is not even a constant in the datetime module.

    No, and it does not belong there. A higher level library that uses
    seconds since epoch for interchange may define it (and make a decision
    whether it should be a naive datetime(1970, 1, 1) or datetime(1970, 1,
    1, tzinfo=timezone.utc)).

    And regardless, the point is *not* the number of characters typed, but
    how easy it is to come up with the solution. Calling the appropriate
    (and appropriately-named) method is much easier than coming up with the
    right datetime arithmetic incantation. It's Python, not Perl. "There
    should be one obvious way to do it".

    I don't see anything obvious about the choice between
    utcfromtimestamp(s), fromtimestamp(s) and utcfromtimestamp(s,
    timezone.utc).

    datetime(1970, 1, 1) + timedelta(seconds=s)

    is obvious, self-contained, short and does not require any knowledge
    other than elementary school arithmetic to understand. Compared to
    this, "utcfromtimestamp" is a monstrosity that suggests that something
    non-trivial, such as UTC leap seconds is been taken care of.

    > > And returning a (seconds, microseconds) tuple does retain the precision.
    > >
    >
    > It does, but it does not help much those who want a float - they would
    > still need another line of code.

    Yes, but a very obvious one at least.

    Let's see:

    def floattimestamp(t):
          s, us = t.totimestamp()
          return s + us * 1e-6

    and

    def floattimestamp(t):
          s, us = t.totimestamp()
          return s + us / 1000000

    which one is *obviously* correct? Are they *obviously* equivalent?

    Note that when timedelta.total_seconds() was first committed, it
    contained a numerical bug. See bpo-8644.

    > Note that with divmod(timedelta,
    > timedelta), you can now easily extract   (seconds, microseconds)  or
    > any other tuple like (weeks, days, seconds. microseconds) from
    > timedelta objects.

    Do you think many users even think of calling divmod() timedelta
    objects? I don't, personally.

    You apparently hold the opinion that the datetime module should be
    reserved for experts in arithmetic over dates, times and timedeltas. But
    it's not. It's the Python stdlib and it should provide reasonably
    high-level tools to do the job.

    Sure, but if the goal is to implement json serialization of datetime
    objects, maybe stdlib should provide a high-level tool for *that* job?
    Using float representation of datetime is probably the worst option
    for json: it is non-standard, may either loose information or
    introduce spurious differences, and is not human-readable.

    In any case, you ignore the hard question about totimestamp():
    fromtimestamp() is not invertible in most real life timezones. If you
    have a solution that does not restrict totimestamp() to UTC, I would
    like to hear it. Otherwise, I don't see any problem with (t -
    datetime(1970, 1, 1)).total_seconds() expression. Maybe we can add
    this recipe to utcfromtimestamp() documentation.

    @pitrou
    Copy link
    Member

    pitrou commented Dec 17, 2010

    > ??? EPOCH is not even a constant in the datetime module.
    >
    No, and it does not belong there.

    And so what was your point exactly?

    A higher level library that uses
    seconds since epoch for interchange

    I don't think the "time" module can be named "higher level", and it
    still handles such timestamps.

    datetime(1970, 1, 1) + timedelta(seconds=s)

    is obvious, self-contained, short and does not require any knowledge
    other than elementary school arithmetic to understand.

    Sigh.
    Again: it's so obvious that you're the only one who seems to easily come
    up with those solutions. How many times does it have to be repeated?

    Compared to
    this, "utcfromtimestamp" is a monstrosity that suggests that something
    non-trivial, such as UTC leap seconds is been taken care of.

    I don't see anything suggesting it is a monstrosity. The name is
    grammatically bizarre, but that's all.

    Let's see:
    [snip]

    which one is *obviously* correct? Are they *obviously* equivalent?

    Both are obviously correct for whatever the non-perverted user has in
    mind. People in real life don't care whether they will retain
    microsecond precision when carrying a floating point timestamp around.
    For the simple reason that the data source itself will not have such
    precision.

    Note that when timedelta.total_seconds() was first committed, it
    contained a numerical bug. See bpo-8644.

    So? What is your point?

    In any case, you ignore the hard question about totimestamp():
    fromtimestamp() is not invertible in most real life timezones. If you
    have a solution that does not restrict totimestamp() to UTC, I would
    like to hear it.

    IMO, the solution would have the datetime object carry the offset from
    UTC with it, rather than try to be smart and compute it dynamically.

    @AlexanderBelopolsky
    Copy link
    Mannequin

    AlexanderBelopolsky mannequin commented Dec 17, 2010

    On Fri, Dec 17, 2010 at 2:35 PM, Antoine Pitrou <report@bugs.python.org> wrote:
    ..

    I don't think the "time" module can be named "higher level", and it
    still handles such timestamps.

    > datetime(1970, 1, 1) + timedelta(seconds=s)
    >
    > is obvious, self-contained,  short and does not require any knowledge
    > other than elementary school arithmetic to understand.

    Sigh.
    Again: it's so obvious that you're the only one who seems to easily come
    up with those solutions. How many times does it have to be repeated?

    Remember, most of the code is written once, but read and edited many
    times. Show me one person who will have trouble understanding what
    datetime(1970, 1, 1) + timedelta(seconds=s) means and show me another
    who can understand datetime.utcfromtimestamp(s) without reading the
    manual.

    > Compared to
    > this, "utcfromtimestamp" is a monstrosity that suggests that something
    > non-trivial, such as UTC leap seconds is been taken care of.

    I don't see anything suggesting it is a monstrosity. The name is
    grammatically bizarre, but that's all.

    Yes, UTC not being a proper acronym in any human language is one
    problem, Python datetime not being able to represent some valid UTC
    times is another.

    That's correct, but most users expect their timestamps to be the same
    when saved on one system and read on another. Granted, most users
    expect the same from their floats as well, but this can only be solved
    by education. Calendaric calculations are complex enough that we
    don't want to expose users to floating point gotchas at the same time.

    > Note that when timedelta.total_seconds() was first committed, it
    > contained a numerical bug.  See bpo-8644.

    So? What is your point?

    I thought the point was obvious: conversion between time values and
    float is non-trivial and error prone. Users should not be encouraged
    to casually convert (seconds, microseconds) tuples to floats. If they
    do, chances are they will do it differently in different parts of the
    program.

    > In any case, you ignore the hard question about totimestamp():
    > fromtimestamp() is not invertible in most real life timezones.  If you
    > have a solution that does not restrict totimestamp() to UTC, I would
    > like to hear it.

    IMO, the solution would have the datetime object carry the offset from
    UTC with it, rather than try to be smart and compute it dynamically.

    Ditto. This is exactly what bpo-9527 is attempting to achieve.

    @pitrou
    Copy link
    Member

    pitrou commented Dec 17, 2010

    Yes, UTC not being a proper acronym in any human language is one
    problem,

    Ok. Too bad you don't live on the same planet than most of us. I bail
    out.

    @abalkin
    Copy link
    Member

    abalkin commented Dec 17, 2010

    On Fri, Dec 17, 2010 at 3:26 PM, Antoine Pitrou <report@bugs.python.org> wrote:
    ..

    > Yes, UTC not being a proper acronym in any human language is one
    > problem,

    Ok. Too bad you don't live on the same planet than most of us. I bail
    out.

    Sorry that my attempt at humor has proven to be too subtle. I was
    referring to the following fact:

    """
    The International Telecommunication Union wanted Coordinated Universal
    Time to have the same symbol in all languages. English and French
    speakers wanted the initials of both their respective language's terms
    to be used internationally: "CUT" for "coordinated universal time" and
    "TUC" for "temps universel coordonné". This resulted in the final
    compromise of "UTC".
    """

    http://en.wikipedia.org/wiki/Coordinated_Universal_Time

    @vstinner
    Copy link
    Member

    It looks like it's not possible to choose between float and (int, int) output type for datetime.totimestamp(). One is more practical (and enough for people who doesn't need an exact result), and one is needed to keep the same resolution than the datetime object. I think that we can add two methods:

    • datetime.totimestamp()->float
    • datetime.totimestamptuple()->(int,int)

    I choosed the shortest name for float because I suppose that most users prefer float than a tuple, and so the API is symmetrical:

    • datetime.fromtimestamp(float)->datetime
    • datetime.totimestamp()->float

    My patch have to be updated to use the timezone (and the DST thing?) and also to update the Python implementation.

    @ping
    Copy link
    Mannequin

    ping mannequin commented Mar 31, 2011

    I am extremely disappointed by what has happened here.

    We are talking about a very simple method that everybody needs, and that has been reimplemented over and over again. I have been frustrated countless times by the lack of a utctotimestamp() method. I have watched beginners and experienced programmers alike suffer over and over again for the lack of this method, and spend hours trying to figure out why Python doesn't have it and how it should be spelled in Python.

    The discussion here has been stuck on assumptions that the method must meet all of the following ideals:

    1. It must produce a value that is easy to compute with
    2. It must have perfect precision in representing microseconds, forever
    3. It must make an exact round-trip for any possible input
    4. It must let users use whatever epoch they want

    These ideals cannot all be met simultaneously and perfectly. The correct thing to do as an engineer is to choose a practical compromise and document the decision.

    The compromise that almost everyone chooses (because it is useful, convenient, has microsecond precision at least until the year 2100, and millisecond precision is frequently sufficient) is to use a floating-point number with an epoch of 1970-01-01. Floating-point seconds can be easily subtracted, added, serialized, and deserialized, and are a primitive data type in nearly every language and database. They are unmatched in ease of use. So everyone wastes time searching for the answer and figuring out how to write:

        import calendar
        calendar.timegm(dt.utctimetuple()) + dt.microsecond * 1e-6

    We should use this as the definition of datetime.utctotimestamp(), document its limitations, and be done with it.

    Instead, this essential and useful method has now been held up for almost three YEARS by an inability to accept a simple engineering decision. Unbelievable.

    @abalkin
    Copy link
    Member

    abalkin commented Mar 31, 2011

    On Thu, Mar 31, 2011 at 2:52 PM, Ka-Ping Yee <report@bugs.python.org> wrote:
    ..

    I am extremely disappointed by what has happened here.

    What exactly are you disappointed about? As far as I can tell, the
    feature request has not been rejected, just no one has come up with a
    satisfactory solution. The issue is open and patches are welcome.

    We are talking about a very simple method that everybody needs, and that has been
    reimplemented over and over again.  I have been frustrated countless times by the lack of
    a utctotimestamp() method.

    This is not what this issue has been about so far. It was about local
    time to timestamp. In py3k, utctotimestamp() is easy:

    EPOCH = datetime(1970, 1, 1)
    def utctotimestamp(dt) :
          return (dt - EPOCH).total_seconds()

     I have watched beginners and experienced programmers alike suffer over and over
    again for the lack of this method, and spend hours trying to figure out why Python
    doesn't have it and how it should be spelled in Python.

    These "beginners and experienced programmers" may want to reconsider
    using floating point numbers to store high precision timestamps. I
    know that some OSes made the same unfortunate choice in system APIs,
    but it does not make this choice any better. I can make a long list
    of why this is a bad choice, but I'll just mention that the precision
    of your timestamp varies from year to year and the program that works
    fine today may mysteriously fail in 5 years when nobody is around who
    can fix it anymore.

    The discussion here has been stuck on assumptions that the method must meet
    all of the following ideals:

     1. It must produce a value that is easy to compute with
     2. It must have perfect precision in representing microseconds, forever
     3. It must make an exact round-trip for any possible input
     4. It must let users use whatever epoch they want

    No it was actually stuck because of the inability to reliably obtain
    the system UTC offset for historical times. This is a solvable
    problem, but the patches proposed so far did not solve it correctly.
    On top of this, there is an issue of datetime.fromtimestamp() not
    being invertible in the presence of DST shifts, so
    datetime.totimestamp() is ambiguous for some datetime values.

    These ideals cannot all be met simultaneously and perfectly.  The correct thing to
    do as an engineer is to choose a practical compromise and document the decision.

    The compromise that almost everyone chooses (because it is useful, convenient, has
    microsecond precision at least until the year 2100, and millisecond precision is frequently
    sufficient) is to use a floating-point number with an epoch of 1970-01-01.  Floating-point
    seconds can be easily subtracted, added, serialized, and deserialized, and are a primitive
    data type in nearly every language and database.

    Those who need to do arithmetics on time values more often deal with
    durations rather than points in time. An arbitrary epoch around
    current time is often more appropriate for timeseries analytics than
    Unix epoch.

     They are unmatched in ease of use.

    Compared to what? I find integers much more suitable for representing
    points in time than floats. Yes, in some languages you have to deal
    with 32-bit int overflow issues if you want to be able to deal with
    durations of over 100 years expressed in microseconds, but these days
    64-bit integers are almost universally available.

     So everyone wastes time searching for the answer and figuring out how to write:

       import calendar
       calendar.timegm(dt.utctimetuple()) + dt.microsecond * 1e-6

    And this is the wrong answer. Someone else using (dt -
    EPOCH).total_seconds() may get a slightly different result. Some may
    argue that given that it is not obvious what expression to use, we
    need to provide a function. However, we already provided
    timedelta.total_seconds() that hides the floating point details. In
    my opinion, even adding total_seconds() was a mistake and x /
    timedelta(seconds=1) is just as short and more explicit than
    x.total_seconds().

    I think the best we can do is to expand datetime.utcfromtimestamp()
    documentation to explain that it is equivalent to

    def utcfromtimestamp(s):
         return EPOCH + timedelta(seconds=s)

    and either leave it as an exercise to the reader to solve
    utcfromtimestamp(s) = dt for s or spell out

    def utctotimestamp(dt) :
          return (dt - EPOCH) / timedelta(seconds=1)

    @ping
    Copy link
    Mannequin

    ping mannequin commented Apr 2, 2011

    no one has come up with a satisfactory solution

    Plenty have proposed a satisfactory solution. No one has come up with a solution that is satisfactory to *you*, because you have overconstrained the problem. The reason we still have no utctotimestamp() after all these years is that you, and you alone as far as I know, refuse to accept a method that inverts utcfromtimestamp() with microsecond precision over its working range. Such a method is a perfectly reasonable and acceptable solution and would add a lot of value to Python as a language.

    I suspect you don't realize just how much pain you have unintentionally caused the world of Python users by singlehandedly blocking progress on this issue. I've seen them: students, friends, coworkers -- even very smart and capable people are stymied by it. No one thinks of looking in the calendar module. Maybe if you watched some of them struggle with this, you would understand.

    leave it as an exercise to the reader to solve

    To take this perspective is to miss the point of Python.

    @JayTaylor
    Copy link
    Mannequin

    JayTaylor mannequin commented Apr 4, 2011

    I couldn't agree more with ping's position on this. It is against the spirit of what Python has set out to be, and the blocking needs to stop.

    Any chance we could get a .epoch() function into python 2.7 as well?

    @abalkin
    Copy link
    Member

    abalkin commented Apr 5, 2011

    On Mon, Apr 4, 2011 at 5:42 PM, Jay Taylor <report@bugs.python.org> wrote:
    ..

    I couldn't agree more with ping's position on this.

    Adding votes to a tracker issue without a working patch will not move
    it any further. There are several committers besides me in the nosy
    list including the original author of the datetime module. If it was
    such a universally desired feature as Ka-Ping makes it sound, it would
    be committed long before I became the maintainer of the datetime
    module.

     It is against the spirit of what Python has set out to be, and the blocking needs to stop.

    I don't think any committer has a power to *block* a patch. I
    certainly don't. If Ka-Ping wants to add a feature over my
    objections, it is well within his power to do so. (Note that I
    objected to timedelta.total_seconds(), but it was added nevertheless.)
    It would be best, however to bring this to python-dev or python-ideas
    first.

    Any chance we could get a .epoch() function into python 2.7 as well?

    No.

    @malemburg
    Copy link
    Member

    Just to add another data point to this discussion:

    mxDateTime, which in large parts inspired the Python datetime module,
    has had a .ticks() method (for local time) and a .gmticks() method
    (for UTC) for more than a decade now and so far, I haven't seen a
    single complaint about any of the issues raised in this discussion.

    The methods naturally return the Unix ticks value as float,
    since that's what the time module uses as basis and the whole purpose
    of those methods is to make interaction with the time module easy
    and straight-forward. Likewise, the epoch is also the same as the time
    module's one.

    Victor's patch could easily be updated to return floats as well,
    to make it compatible with the time module.

    There's only one catch that Victor's patch doesn't include: mktime()
    doesn't always work with DST set to anything but -1. mxDateTime
    checks the API at module load time and then determines whether
    it can be used with a DST setting or not (see the mxDateTime code
    for details). Not sure whether today's mktime() implementations
    still have any issues with this, but it's better to double-check
    than to get wrong results.

    http://www.egenix.com/products/python/mxBase/mxDateTime/
    

    @vstinner
    Copy link
    Member

    vstinner commented Apr 5, 2011

    Marc, could you maybe write a new patching taking care of the DST and
    maybe also the timezone? It looks like you have a long experience in
    timestamps :-)

    @malemburg
    Copy link
    Member

    STINNER Victor wrote:

    STINNER Victor <victor.stinner@haypocalc.com> added the comment:

    Marc, could you maybe write a new patching taking care of the DST and
    maybe also the timezone? It looks like you have a long experience in
    timestamps :-)

    Sorry, but no. I'm not really a fan of the datetime module and
    try to stay away from it whenever I can :-)

    Note that dealing with DST in timezones other than the local time
    zone, is bound to go wrong without direct access to the tz library.
    The C lib doesn't provide any good way to access timezone
    information other than the local timezone or UTC.

    When dealing with date/time values, it is usually best to stay with
    UTC and only transform those values into local times in user
    interfaces on the front-end client.

    Consequently, I'd suggest to only allow UTC and local timezone
    conversions for the method in the datetime module.

    @abalkin
    Copy link
    Member

    abalkin commented Apr 5, 2011

    On Tue, Apr 5, 2011 at 4:33 AM, Marc-Andre Lemburg
    <report@bugs.python.org> wrote:
    ..

    mxDateTime, which in large parts inspired the Python datetime module,
    has had a .ticks() method (for local time) and a .gmticks() method
    (for UTC) for more than a decade now

    Yes, mxDateTime's gmticks()/ticks() pair of functions present a much
    more mature design than anything that has been proposed here. It is
    telling, however, that no one has mentioned mxDateTime's gmticks() on
    this issue in four years. On a duplicate bpo-1673409, Marc did
    bring it up, but as far as I can tell, no one responded. See
    msg75411.

    Google code search,

    http://www.google.com/codesearch?hl=en&sa=N&q=gmticks+lang:python

    returns only 13 hits for "gmticks". In several instances, the
    resulting float is immediately converted to int, in other instances
    "gmticks" is mentioned in comments and the code works around its bugs.

    I would not use Google Code search as an ultimate arbiter on the
    popularity of a feature, so I would really like to hear from the
    proponents about real life uses of gmticks() or any other examples
    where a similar method "has been reimplemented over and over again."

    so far, I haven't seen a single complaint about any of the issues raised in this discussion.

    Well, search for gmticks does not return too many hits outside of
    mxDateTime code and manuals, but I had no trouble finding this:

    """
    okay, all the MySQLdb dataobject tick-based time handling methods are
    broken in various ways -- reconstruct GMT ticks from time module's
    mktime...
    """
    http://viewvc.tigris.org/ds/viewMessage.do?dsForumId=4251&dsMessageId=656863

    Follow the link for some more colorful language describing developer's
    experience with the feature.

    Note that it is likely that the bug MySQLdb developer complained about
    was fixed in mxDateTime at some point,
    <http://www.egenix.com/www2002/python/mxDateTime-History.html\>, but
    this shows that implementing gmticks() correctly is not as trivial as
    those who never tried might think.

    The methods naturally return the Unix ticks value as float,
    since that's what the time module uses as basis

    Which in turn is a mistake IMO. Note that POSIX does not use float
    timestamps for a reason.

    and the whole purpose
    of those methods is to make interaction with the time module easy
    and straight-forward.

    This is not the goal that I would support. I would rather see code
    that uses datetime module not require time module methods at all.

    Victor's patch could easily be updated to return floats as well,
    to make it compatible with the time module.

    Victor reported implementing two methods, one to return a float and
    another to return a tuple. See msg124259. I am not sure I've seen
    that code.

    There's only one catch that Victor's patch doesn't include ...

    No, it is not about "only one catch". Victor's patch is simply wrong.
    For an aware datetime instance it extracts DST flag from tzinfo, but
    ignores the offset.

    @abalkin
    Copy link
    Member

    abalkin commented Apr 5, 2011

    MAL> Since most of the datetime module was inspired by mxDateTime,
    MAL> I wonder why [ticks()/gmticks()] were left out. (msg75411)

    """
    The datetime module intended to be an island of relative sanity.
    Because the range of dates "timestamps" can represent varies across
    platforms (and even "the epoch" varies), datetime doesn't even try to
    produce timestamps directly -- datetime is more of an alternative to
    "seconds from the epoch" schemes. Because datetime objects have
    greater range and precision than timestamps, conversion is
    problem-free in only one direction. It's not a coincidence that
    that's the only direction datetime supplies ;-)
    """ - Tim Peters

    http://bytes.com/topic/python/answers/522572-datetime-timestamp

    I will also add that fromtimestamp() is not invertible in the presence of DST. That's why mxDatetime.ticks() takes a DST flag making it effectively a multi-valued function. Note that naive users, who just want to pass datetime values to an OS function expecting a float, most likely will not have means of properly obtaining DST flag.

    @malemburg
    Copy link
    Member

    Alexander Belopolsky wrote:

    Alexander Belopolsky <belopolsky@users.sourceforge.net> added the comment:

    On Tue, Apr 5, 2011 at 4:33 AM, Marc-Andre Lemburg
    <report@bugs.python.org> wrote:
    ..
    > mxDateTime, which in large parts inspired the Python datetime module,
    > has had a .ticks() method (for local time) and a .gmticks() method
    > (for UTC) for more than a decade now

    Yes, mxDateTime's gmticks()/ticks() pair of functions present a much
    more mature design than anything that has been proposed here. It is
    telling, however, that no one has mentioned mxDateTime's gmticks() on
    this issue in four years. On a duplicate bpo-1673409, Marc did
    bring it up, but as far as I can tell, no one responded. See
    msg75411.

    Google code search,

    http://www.google.com/codesearch?hl=en&sa=N&q=gmticks+lang:python

    returns only 13 hits for "gmticks". In several instances, the
    resulting float is immediately converted to int, in other instances
    "gmticks" is mentioned in comments and the code works around its bugs.

    I would not use Google Code search as an ultimate arbiter on the
    popularity of a feature, so I would really like to hear from the
    proponents about real life uses of gmticks() or any other examples
    where a similar method "has been reimplemented over and over again."

    mxDateTime needs those two methods, since it doesn't natively
    use timezones. The .ticks() method is used for local time values,
    .gmticks() for UTC ones; that's why there are two methods.

    The .gmticks() method is always used when storing UTC values
    in mxDateTime instances, which actually is the preferred way
    of storing data in databases. Google Code doesn't really count
    much, since it only scans a limited number of OSS code bases.
    Most of our users are commercial users who use the tools
    in-house.

    Note that implementing .gmticks() is fairly easy on POSIX
    conform systems. On most others, timegm() can be used. If
    that doesn't exist, things get tricky, but that case should
    be rare nowadays.

    > so far, I haven't seen a single complaint about any of the issues raised in this discussion.

    Well, search for gmticks does not return too many hits outside of
    mxDateTime code and manuals, but I had no trouble finding this:

    """
    okay, all the MySQLdb dataobject tick-based time handling methods are
    broken in various ways -- reconstruct GMT ticks from time module's
    mktime...
    """
    http://viewvc.tigris.org/ds/viewMessage.do?dsForumId=4251&dsMessageId=656863

    Follow the link for some more colorful language describing developer's
    experience with the feature.

    Note that it is likely that the bug MySQLdb developer complained about
    was fixed in mxDateTime at some point,
    <http://www.egenix.com/www2002/python/mxDateTime-History.html\>, but
    this shows that implementing gmticks() correctly is not as trivial as
    those who never tried might think.

    Note that he was referring to the .ticks() method, not the .gmticks()
    method. The patch doesn't say which version of mxDateTime he was
    using. The bug mentioned in the changelog was fixed in 1998. It is
    possible, however, that the mktime() on his system was broken - which
    is why I added a test for it in mxDateTime.

    >
    > The methods naturally return the Unix ticks value as float,
    > since that's what the time module uses as basis

    Which in turn is a mistake IMO. Note that POSIX does not use float
    timestamps for a reason.

    The time module is our reference in this case and this tries hard
    to add fractions of a second to the value :-)

    Note that sub-second accuracy relies on a number of factors,
    the storage format most certainly is the least important
    aspect ;-)

    On many systems, you only get 1/100s accuracy, on others, the timer
    ticks in fixed increments, giving you even weirder sub-second values
    (e.g. time appears to stay constant between time.time() calls).

    OTOH, there's a new set of APIs for nano-second accuracy available
    now, which the datetime module objects cannot represent at all due
    to the integer-based storage format.

    BTW: The integer format was chose in order to keep the memory
    footprint of the objects low.

    > and the whole purpose
    > of those methods is to make interaction with the time module easy
    > and straight-forward.

    This is not the goal that I would support. I would rather see code
    that uses datetime module not require time module methods at all.

    No chance :-) In practice, the time module gets used a lot
    for date/time storage or to quickly measure time deltas.
    Some people also prefer time module ticks due to their lower
    memory footprint, esp. when it comes to storing thousands of
    time values in time series.

    > Victor's patch could easily be updated to return floats as well,
    > to make it compatible with the time module.
    >

    Victor reported implementing two methods, one to return a float and
    another to return a tuple. See msg124259. I am not sure I've seen
    that code.

    I had a look at the last patch on this ticket.

    > There's only one catch that Victor's patch doesn't include ...

    No, it is not about "only one catch". Victor's patch is simply wrong.
    For an aware datetime instance it extracts DST flag from tzinfo, but
    ignores the offset.

    True, so make that two issues ;-)

    @malemburg
    Copy link
    Member

    Alexander Belopolsky wrote:

    Alexander Belopolsky <belopolsky@users.sourceforge.net> added the comment:

    MAL> Since most of the datetime module was inspired by mxDateTime,
    MAL> I wonder why [ticks()/gmticks()] were left out. (msg75411)

    """
    The datetime module intended to be an island of relative sanity.
    Because the range of dates "timestamps" can represent varies across
    platforms (and even "the epoch" varies), datetime doesn't even try to
    produce timestamps directly -- datetime is more of an alternative to
    "seconds from the epoch" schemes. Because datetime objects have
    greater range and precision than timestamps, conversion is
    problem-free in only one direction. It's not a coincidence that
    that's the only direction datetime supplies ;-)
    """ - Tim Peters

    http://bytes.com/topic/python/answers/522572-datetime-timestamp

    I will also add that fromtimestamp() is not invertible in the presence of DST. That's why mxDatetime.ticks() takes a DST flag making it effectively a multi-valued function. Note that naive users, who just want to pass datetime values to an OS function expecting a float, most likely will not have means of properly obtaining DST flag.

    IMHO, the whole concept of DST is broken, but that's not our fault :-)

    Ditching the concept just because it is known to fail for
    one hour out of 8760 you have in a typical year doesn't really
    warrant breaking the "practicality beats purity" guideline.

    Otherwise, we'd have to ditch the date support in the datetime module
    too: after all, Feb 29 only exists every 4 years (well, most of the
    time) - and that's one day out of 1461 in those 4 years, so an
    even worse ratio :-)

    And I'm not even starting to talk about ditching the concept
    of Unix ticks to begin with, as a result of having leap seconds
    causing POSIX ticks values not matching (real) UTC ticks.

    In reality, all these things hardly ever matter and if they do,
    users will either know that they have to make conscious decision,
    simply don't care or decide not to care.

    BTW: A "timestamp" usually refers to the combination of date and
    time. The time.time() return value is "seconds since the Epoch".
    I usually call those values "ticks" (not sure whether it's standard
    term of not, but always writing "seconds since Epoch" wasn't an
    option either ;-)).

    Date/time is fun, isn't it ?

    @abalkin
    Copy link
    Member

    abalkin commented Apr 5, 2011

    Let me state my position on this issue once again. Converting datetime values to float is easy. If your dt is a naive instance representing UTC time:

       timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

    If your dt is an aware instance:

       timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)

    These recipes are easy to adjust for your application needs. One application may want millisecond or microsecond ticks, another might want to carry subsecond presision in a separate integer, third may want to avoid timestamps before 1970 or after 2038 or ignore microseconds altogether. No matter what a hypothetical datetime.epoch() will provide, most of applications will need to add a line or two to its code to serve their needs. Applications that will use dt.epoch() naively without thinking what dt represents (say local or UTC) will be buggy.

    The only related feature that I think is missing from datetime module is the ability to obtain local time as an aware datetime instance and to convert a naive datetime instance assumed to represent local time to an aware one.

    This is the subject of bpo-9527, but there is a resistance to adding that feature.

    @abalkin
    Copy link
    Member

    abalkin commented Apr 5, 2011

    On Tue, Apr 5, 2011 at 1:45 PM, Marc-Andre Lemburg
    <report@bugs.python.org> wrote:
    ..

    BTW: A "timestamp" usually refers to the combination of date and
    time. The time.time() return value is "seconds since the Epoch".
    I usually call those values "ticks" (not sure whether it's standard
    term of not, but always writing "seconds since Epoch" wasn't an
    option either ;-)).

    In Unix context, the term "timestamp" is usually associated with the
    various time values that OS stores with the files. I think this use
    is due to the analogy with physical "received on" timestamps used on
    paper documents. Since it is well-known that Unix filesystems store
    time values as seconds since Epoch, it is common to refer to these
    values as "Unix timestamps".

    See, for example:

    http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html

    @vivanov
    Copy link
    Mannequin

    vivanov mannequin commented Apr 7, 2011

    On 04/05/2011 18:22, Alexander Belopolsky wrote:

    """
    The datetime module intended to be an island of relative sanity.
    ....... """ - Tim Peters

    Refusing to cooperate with the rest of the world is not sane by my books.

    On 04/05/2011 21:06, Alexander Belopolsky wrote:

    Converting datetime values to float is easy. If your dt is a naive instance representing UTC time:

    timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)
    

    If your dt is an aware instance:

    timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)
    

    Please add these lines to the datetime module's documentation. In some
    central, well lit place. I believe that if nothing else, the whole
    discussion should have proved to you that there are many people looking
    for them.

    OTOH a sinceepoch(epoch=datetime(1970,1,1)) method of the datetime class
    should be equally easy. Would be especially useful if few of the more
    frequently used EPOCHs are provided as constants.

    @abalkin
    Copy link
    Member

    abalkin commented Apr 7, 2011

    On Thu, Apr 7, 2011 at 6:20 AM, Velko Ivanov <report@bugs.python.org> wrote:
    ..

    > Converting datetime values to float is easy.   If your dt is a naive instance representing UTC time:
    >
    >     timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)
    >
    > If your dt is an aware instance:
    >
    >     timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)

    Please add these lines to the datetime module's documentation. In some
    central, well lit place. I believe that if nothing else, the whole
    discussion should have proved to you that there are many people looking
    for them.

    This is precisely what I suggested at the end of msg132697 above. See
    attached patch (bpo-2736-doc.diff) for a proposed documentation
    enhancement.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Apr 25, 2011

    New changeset b55eac85e39c by Alexander Belopolsky in branch 'default':
    Issue bpo-2736: Documented how to compute seconds since epoch.
    http://hg.python.org/cpython/rev/b55eac85e39c

    @abalkin abalkin added docs Documentation in the Doc dir and removed stdlib Python modules in the Lib dir labels Apr 25, 2011
    @abalkin abalkin closed this as completed Apr 25, 2011
    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jun 8, 2012

    New changeset 6671c5039e15 by Alexander Belopolsky in branch 'default':
    Issue bpo-2736: Added datetime.timestamp() method.
    http://hg.python.org/cpython/rev/6671c5039e15

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    docs Documentation in the Doc dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants