@@ -9,12 +9,15 @@ from cpython cimport (
9
9
PyTypeObject,
10
10
PyFloat_Check,
11
11
PyObject_RichCompareBool,
12
- PyString_Check
12
+ PyObject_RichCompare,
13
+ PyString_Check,
14
+ Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE
13
15
)
14
16
15
17
# Cython < 0.17 doesn't have this in cpython
16
18
cdef extern from " Python.h" :
17
19
cdef PyTypeObject * Py_TYPE(object )
20
+ int PySlice_Check(object )
18
21
19
22
20
23
from libc.stdlib cimport free
@@ -30,9 +33,6 @@ from datetime import timedelta, datetime
30
33
from datetime import time as datetime_time
31
34
from pandas.compat import parse_date
32
35
33
- cdef extern from " Python.h" :
34
- int PySlice_Check(object )
35
-
36
36
# initialize numpy
37
37
import_array()
38
38
# import_ufunc()
@@ -350,6 +350,11 @@ NaT = NaTType()
350
350
351
351
iNaT = util.get_nat()
352
352
353
+
354
+ cdef inline bint _cmp_nat_dt(_NaT lhs, _Timestamp rhs, int op) except - 1 :
355
+ return _nat_scalar_rules[op]
356
+
357
+
353
358
cdef _tz_format(object obj, object zone):
354
359
try :
355
360
return obj.strftime(' %% Z, tz=%s ' % zone)
@@ -437,9 +442,35 @@ def apply_offset(ndarray[object] values, object offset):
437
442
438
443
result = np.empty(n, dtype = ' M8[ns]' )
439
444
new_values = result.view(' i8' )
440
- pass
441
445
442
446
447
+ cdef inline bint _cmp_scalar(int64_t lhs, int64_t rhs, int op) except - 1 :
448
+ if op == Py_EQ:
449
+ return lhs == rhs
450
+ elif op == Py_NE:
451
+ return lhs != rhs
452
+ elif op == Py_LT:
453
+ return lhs < rhs
454
+ elif op == Py_LE:
455
+ return lhs <= rhs
456
+ elif op == Py_GT:
457
+ return lhs > rhs
458
+ elif op == Py_GE:
459
+ return lhs >= rhs
460
+
461
+
462
+ cdef int _reverse_ops[6 ]
463
+
464
+ _reverse_ops[Py_LT] = Py_GT
465
+ _reverse_ops[Py_LE] = Py_GE
466
+ _reverse_ops[Py_EQ] = Py_EQ
467
+ _reverse_ops[Py_NE] = Py_NE
468
+ _reverse_ops[Py_GT] = Py_LT
469
+ _reverse_ops[Py_GE] = Py_LE
470
+
471
+
472
+ cdef str _NDIM_STRING = " ndim"
473
+
443
474
# This is PITA. Because we inherit from datetime, which has very specific
444
475
# construction requirements, we need to do object instantiation in python
445
476
# (see Timestamp class above). This will serve as a C extension type that
@@ -449,18 +480,21 @@ cdef class _Timestamp(datetime):
449
480
int64_t value, nanosecond
450
481
object offset # frequency reference
451
482
452
- def __hash__ (self ):
483
+ def __hash__ (_Timestamp self ):
453
484
if self .nanosecond:
454
485
return hash (self .value)
455
- else :
456
- return datetime.__hash__ (self )
486
+ return datetime.__hash__ (self )
457
487
458
488
def __richcmp__ (_Timestamp self , object other , int op ):
459
- cdef _Timestamp ots
489
+ cdef:
490
+ _Timestamp ots
491
+ int ndim
460
492
461
493
if isinstance (other, _Timestamp):
494
+ if isinstance (other, _NaT):
495
+ return _cmp_nat_dt(other, self , _reverse_ops[op])
462
496
ots = other
463
- elif type (other) is datetime:
497
+ elif isinstance (other, datetime) :
464
498
if self .nanosecond == 0 :
465
499
val = self .to_datetime()
466
500
return PyObject_RichCompareBool(val, other, op)
@@ -470,70 +504,60 @@ cdef class _Timestamp(datetime):
470
504
except ValueError :
471
505
return self ._compare_outside_nanorange(other, op)
472
506
else :
473
- if op == 2 :
474
- return False
475
- elif op == 3 :
476
- return True
507
+ ndim = getattr (other, _NDIM_STRING, - 1 )
508
+
509
+ if ndim != - 1 :
510
+ if ndim == 0 :
511
+ if isinstance (other, np.datetime64):
512
+ other = Timestamp(other)
513
+ else :
514
+ raise TypeError (' Cannot compare type %r with type %r ' %
515
+ (type (self ).__name__,
516
+ type (other).__name__))
517
+ return PyObject_RichCompare(other, self , _reverse_ops[op])
477
518
else :
478
- raise TypeError (' Cannot compare Timestamp with '
479
- ' {0!r}' .format(other.__class__ .__name__ ))
519
+ if op == Py_EQ:
520
+ return False
521
+ elif op == Py_NE:
522
+ return True
523
+ raise TypeError (' Cannot compare type %r with type %r ' %
524
+ (type (self ).__name__, type (other).__name__))
480
525
481
526
self ._assert_tzawareness_compat(other)
527
+ return _cmp_scalar(self .value, ots.value, op)
482
528
483
- if op == 2 : # ==
484
- return self .value == ots.value
485
- elif op == 3 : # !=
486
- return self .value != ots.value
487
- elif op == 0 : # <
488
- return self .value < ots.value
489
- elif op == 1 : # <=
490
- return self .value <= ots.value
491
- elif op == 4 : # >
492
- return self .value > ots.value
493
- elif op == 5 : # >=
494
- return self .value >= ots.value
495
-
496
- cdef _compare_outside_nanorange(self , object other, int op):
497
- dtval = self .to_datetime()
529
+ cdef bint _compare_outside_nanorange(_Timestamp self , datetime other,
530
+ int op) except - 1 :
531
+ cdef datetime dtval = self .to_datetime()
498
532
499
533
self ._assert_tzawareness_compat(other)
500
534
501
535
if self .nanosecond == 0 :
502
- if op == 2 : # ==
503
- return dtval == other
504
- elif op == 3 : # !=
505
- return dtval != other
506
- elif op == 0 : # <
507
- return dtval < other
508
- elif op == 1 : # <=
509
- return dtval <= other
510
- elif op == 4 : # >
511
- return dtval > other
512
- elif op == 5 : # >=
513
- return dtval >= other
536
+ return PyObject_RichCompareBool(dtval, other, op)
514
537
else :
515
- if op == 2 : # ==
538
+ if op == Py_EQ:
516
539
return False
517
- elif op == 3 : # !=
540
+ elif op == Py_NE:
518
541
return True
519
- elif op == 0 : # <
542
+ elif op == Py_LT:
520
543
return dtval < other
521
- elif op == 1 : # <=
544
+ elif op == Py_LE:
522
545
return dtval < other
523
- elif op == 4 : # >
546
+ elif op == Py_GT:
524
547
return dtval >= other
525
- elif op == 5 : # >=
548
+ elif op == Py_GE:
526
549
return dtval >= other
527
550
528
- cdef _assert_tzawareness_compat(self , object other):
551
+ cdef int _assert_tzawareness_compat(_Timestamp self ,
552
+ object other) except - 1 :
529
553
if self .tzinfo is None :
530
554
if other.tzinfo is not None :
531
- raise Exception (' Cannot compare tz-naive and '
532
- ' tz-aware timestamps' )
555
+ raise ValueError (' Cannot compare tz-naive and tz-aware '
556
+ ' timestamps' )
533
557
elif other.tzinfo is None :
534
- raise Exception (' Cannot compare tz-naive and tz-aware timestamps' )
558
+ raise ValueError (' Cannot compare tz-naive and tz-aware timestamps' )
535
559
536
- cpdef to_datetime(self ):
560
+ cpdef datetime to_datetime(_Timestamp self ):
537
561
cdef:
538
562
pandas_datetimestruct dts
539
563
_TSObject ts
@@ -580,30 +604,35 @@ cdef inline bint is_timestamp(object o):
580
604
return Py_TYPE(o) == ts_type # isinstance(o, Timestamp)
581
605
582
606
607
+ cdef bint _nat_scalar_rules[6 ]
608
+
609
+ _nat_scalar_rules[Py_EQ] = False
610
+ _nat_scalar_rules[Py_NE] = True
611
+ _nat_scalar_rules[Py_LT] = False
612
+ _nat_scalar_rules[Py_LE] = False
613
+ _nat_scalar_rules[Py_GT] = False
614
+ _nat_scalar_rules[Py_GE] = False
615
+
616
+
583
617
cdef class _NaT(_Timestamp):
584
618
585
619
def __hash__ (_NaT self ):
586
620
# py3k needs this defined here
587
621
return hash (self .value)
588
622
589
623
def __richcmp__ (_NaT self , object other , int op ):
590
- # if not isinstance(other, (_NaT, _Timestamp)):
591
- # raise TypeError('Cannot compare %s with NaT' % type(other))
592
-
593
- if op == 2 : # ==
594
- return False
595
- elif op == 3 : # !=
596
- return True
597
- elif op == 0 : # <
598
- return False
599
- elif op == 1 : # <=
600
- return False
601
- elif op == 4 : # >
602
- return False
603
- elif op == 5 : # >=
604
- return False
624
+ cdef int ndim = getattr (other, ' ndim' , - 1 )
605
625
626
+ if ndim == - 1 :
627
+ return _nat_scalar_rules[op]
606
628
629
+ if ndim == 0 :
630
+ if isinstance (other, np.datetime64):
631
+ other = Timestamp(other)
632
+ else :
633
+ raise TypeError (' Cannot compare type %r with type %r ' %
634
+ (type (self ).__name__, type (other).__name__))
635
+ return PyObject_RichCompare(other, self , _reverse_ops[op])
607
636
608
637
609
638
def _delta_to_nanoseconds (delta ):
0 commit comments