[Python-ideas] datetime: Support infinity

Yawar Amin yawar.amin at gmail.com
Tue Jan 27 03:59:56 CET 2015


Hi Thomas,

On 2015-01-26 03:24, Thomas Güttler wrote:
> [...]
> I don't know the internals of the datetime module. I guess it is not
> possible to support infinity.
>
> What do you think?

I think it's a great idea to support infinity dates in Python (in fact,
in any modern date handling library). If I read you right, the problem
with mapping back to Postgres is that infinity dates are being
represented in-band in Python, so they get mapped to non-infinite dates
in Postgres.

The solution is to represent infinity dates as out-of-band values. My
take on this is to represent them as subtypes of datetime and handle
those subtypes appropriately. A rough (but working) sketch of the idea:

    import datetime as dt
   
    class datetime_ext(dt.datetime):
      def __new__(cls, *args, **kwargs):
        return dt.datetime.__new__(cls, *args, **kwargs)
   
      # Getting the infinity dates.
      @staticmethod
      def pos_inf(): return datetime_inf(1)
      @staticmethod
      def neg_inf(): return datetime_inf(-1)
   
      # More fun names.
      @staticmethod
      def big_bang(): return datetime_ext.neg_inf()
      @staticmethod
      def big_crunch(): return datetime_ext.pos_inf()
   
      # Now the fun part.
      def _are_we_inf(self, other):
        return (
          isinstance(self, datetime_inf),
          isinstance(other, datetime_inf)
        )
   
      def __lt__(self, other):
        awi = self._are_we_inf(other)
   
        if awi == (True, True): return self.sign < other.sign
        elif awi == (False, True): return other.sign > 0
        elif awi == (True, False): return self.sign < 0
        else: return self < other
   
      def __gt__(self, other):
        awi = self._are_we_inf(other)
   
        if awi == (True, True): return self.sign > other.sign
        elif awi == (False, True): return other.sign < 0
        elif awi == (True, False): return self.sign > 0
        else: return self > other
   
      def __eq__(self, other):
        awi = self._are_we_inf(other)
   
        if awi == (True, True): return self.sign == other.sign
        elif awi == (False, False): return self == other
        else: return False
   
      def __ne__(self, other): return not (self == other)
      def __le__(self, other): return (self < other) or (self == other)
      def __ge__(self, other): return (self > other) or (self == other)
   
    class datetime_inf(datetime_ext):
      def __new__(cls, sign):
        # The numbers passed in to the constructor are meaningless; they
        # should never be used.
        retval = datetime_ext.__new__(cls, 1, 1, 1)
        retval.sign = sign
   
        return retval
   
      def _raise_err(self): raise TypeError("Infinity date")
      def date(self): return datetime_inf(self.sign)
      def time(self): _raise_err()
      def timetz(self): _raise_err()
      def replace(self): _raise_err()
      def astimezone(self): _raise_err()
      # ... all the others ...
   
    # All True:
    print(datetime_ext.big_bang() == datetime_ext.big_bang())
    print(datetime_ext.big_crunch() == datetime_ext.big_crunch())
    print(datetime_ext.big_bang() < datetime_ext.big_crunch())
    print(datetime_ext.big_crunch() > datetime_ext.big_bang())
    print(datetime_ext.big_bang() < dt.datetime.today())
    print(datetime_ext.big_crunch() > dt.datetime.today())
   
    # Added bonuses (boni?):
    print(dt.datetime.max < datetime_ext.big_crunch())
    print(dt.datetime.min > datetime_ext.big_bang())

So the basic idea here is to override the comparison methods to treat
the datetime_inf type specially. The implementation of datetime_inf
isn't particularly elegant, but I think it works for its intended
purpose.

Now with these, I'm pretty sure you can write an InfDateWrapper class
that checks for datetime_ext.big_bang() and datetime_ext.big_crunch()
and returns the corresponding Postgres '-infinity::date' and
'infinity::date'; and that the mapping will be fully bidirectional.

And as I said, I believe something like this should be added to the
Python stdlib. If no one has a better implementation I'll start working
on a proposal.

Regards,

Yawar


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 834 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150126/b11b5ea7/attachment.sig>


More information about the Python-ideas mailing list