view mercurial/dirstateutils/timestamp.py @ 48263:83d0bd45b662

dirstate-v2: actually use sub-second mtime precision Instead of zero, set the nanoseconds field to its correct value whenever possible and preserve it across serialization+parsing. Differential Revision: https://phab.mercurial-scm.org/D11702
author Simon Sapin <simon.sapin@octobus.net>
date Wed, 13 Oct 2021 15:58:14 +0200
parents 68bb472aee9c
children 08b060abd658
line wrap: on
line source

# Copyright Mercurial Contributors
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

import functools
import stat


rangemask = 0x7FFFFFFF


@functools.total_ordering
class timestamp(tuple):
    """
    A Unix timestamp with optional nanoseconds precision,
    modulo 2**31 seconds.

    A 2-tuple containing:

    `truncated_seconds`: seconds since the Unix epoch,
    truncated to its lower 31 bits

    `subsecond_nanoseconds`: number of nanoseconds since `truncated_seconds`.
    When this is zero, the sub-second precision is considered unknown.
    """

    def __new__(cls, value):
        truncated_seconds, subsec_nanos = value
        value = (truncated_seconds & rangemask, subsec_nanos)
        return super(timestamp, cls).__new__(cls, value)

    def __eq__(self, other):
        self_secs, self_subsec_nanos = self
        other_secs, other_subsec_nanos = other
        return self_secs == other_secs and (
            self_subsec_nanos == other_subsec_nanos
            or self_subsec_nanos == 0
            or other_subsec_nanos == 0
        )

    def __gt__(self, other):
        self_secs, self_subsec_nanos = self
        other_secs, other_subsec_nanos = other
        if self_secs > other_secs:
            return True
        if self_secs < other_secs:
            return False
        if self_subsec_nanos == 0 or other_subsec_nanos == 0:
            # they are considered equal, so not "greater than"
            return False
        return self_subsec_nanos > other_subsec_nanos


def zero():
    """
    Returns the `timestamp` at the Unix epoch.
    """
    return tuple.__new__(timestamp, (0, 0))


def mtime_of(stat_result):
    """
    Takes an `os.stat_result`-like object and returns a `timestamp` object
    for its modification time.
    """
    try:
        # TODO: add this attribute to `osutil.stat` objects,
        # see `mercurial/cext/osutil.c`.
        #
        # This attribute is also not available on Python 2.
        nanos = stat_result.st_mtime_ns
    except AttributeError:
        # https://docs.python.org/2/library/os.html#os.stat_float_times
        # "For compatibility with older Python versions,
        #  accessing stat_result as a tuple always returns integers."
        secs = stat_result[stat.ST_MTIME]

        subsec_nanos = 0
    else:
        billion = int(1e9)
        secs = nanos // billion
        subsec_nanos = nanos % billion

    return timestamp((secs, subsec_nanos))