@@ -456,11 +456,12 @@ cdef class BaseOffset:
456
456
equality between two DateOffset objects.
457
457
"""
458
458
# NB: non-cython subclasses override property with cache_readonly
459
- all_paras = self .__dict__ .copy()
459
+ d = getattr (self , " __dict__" , {})
460
+ all_paras = d.copy()
460
461
all_paras[" n" ] = self .n
461
462
all_paras[" normalize" ] = self .normalize
462
463
for attr in self ._attributes:
463
- if hasattr (self , attr) and attr not in self .__dict__ :
464
+ if hasattr (self , attr) and attr not in d :
464
465
# cython attributes are not in __dict__
465
466
all_paras[attr] = getattr (self , attr)
466
467
@@ -711,6 +712,15 @@ cdef class BaseOffset:
711
712
712
713
def __setstate__ (self , state ):
713
714
""" Reconstruct an instance from a pickled state"""
715
+ if isinstance (self , MonthOffset):
716
+ # We can't just override MonthOffset.__setstate__ because of the
717
+ # combination of MRO resolution and cython not handling
718
+ # multiple inheritance nicely for cdef classes.
719
+ state.pop(" _use_relativedelta" , False )
720
+ state.pop(" offset" , None )
721
+ state.pop(" _offset" , None )
722
+ state.pop(" kwds" , {})
723
+
714
724
if ' offset' in state:
715
725
# Older (<0.22.0) versions have offset attribute instead of _offset
716
726
if ' _offset' in state: # pragma: no cover
@@ -728,6 +738,12 @@ cdef class BaseOffset:
728
738
self .n = state.pop(" n" )
729
739
self .normalize = state.pop(" normalize" )
730
740
self ._cache = state.pop(" _cache" , {})
741
+
742
+ if not len (state):
743
+ # FIXME: kludge because some classes no longer have a __dict__,
744
+ # so we need to short-circuit before raising on the next line
745
+ return
746
+
731
747
self .__dict__ .update(state)
732
748
733
749
if ' weekmask' in state and ' holidays' in state:
@@ -739,7 +755,7 @@ cdef class BaseOffset:
739
755
740
756
def __getstate__ (self ):
741
757
""" Return a pickleable state"""
742
- state = self . __dict__ .copy()
758
+ state = getattr ( self , " __dict__" , {}) .copy()
743
759
state[" n" ] = self .n
744
760
state[" normalize" ] = self .normalize
745
761
@@ -1020,8 +1036,11 @@ cdef class BusinessMixin(SingleConstructorOffset):
1020
1036
1021
1037
cpdef __setstate__(self , state ):
1022
1038
# We need to use a cdef/cpdef method to set the readonly _offset attribute
1039
+ if " _offset" in state:
1040
+ self ._offset = state.pop(" _offset" )
1041
+ elif " offset" in state:
1042
+ self ._offset = state.pop(" offset" )
1023
1043
BaseOffset.__setstate__(self , state)
1024
- self ._offset = state[" _offset" ]
1025
1044
1026
1045
1027
1046
class BusinessHourMixin (BusinessMixin ):
@@ -1180,6 +1199,7 @@ class WeekOfMonthMixin(SingleConstructorOffset):
1180
1199
1181
1200
1182
1201
# ----------------------------------------------------------------------
1202
+ # Year-Based Offset Classes
1183
1203
1184
1204
cdef class YearOffset(SingleConstructorOffset ):
1185
1205
"""
@@ -1201,6 +1221,12 @@ cdef class YearOffset(SingleConstructorOffset):
1201
1221
if month < 1 or month > 12 :
1202
1222
raise ValueError (" Month must go from 1 to 12" )
1203
1223
1224
+ cpdef __setstate__(self , state):
1225
+ self .month = state.pop(" month" )
1226
+ self .n = state.pop(" n" )
1227
+ self .normalize = state.pop(" normalize" )
1228
+ self ._cache = {}
1229
+
1204
1230
def __reduce__ (self ):
1205
1231
return type (self ), (self .n, self .normalize, self .month)
1206
1232
@@ -1242,6 +1268,51 @@ cdef class YearOffset(SingleConstructorOffset):
1242
1268
return type (dtindex)._simple_new(shifted, dtype = dtindex.dtype)
1243
1269
1244
1270
1271
+ cdef class BYearEnd(YearOffset):
1272
+ """
1273
+ DateOffset increments between business EOM dates.
1274
+ """
1275
+
1276
+ _outputName = " BusinessYearEnd"
1277
+ _default_month = 12
1278
+ _prefix = " BA"
1279
+ _day_opt = " business_end"
1280
+
1281
+
1282
+ cdef class BYearBegin(YearOffset):
1283
+ """
1284
+ DateOffset increments between business year begin dates.
1285
+ """
1286
+
1287
+ _outputName = " BusinessYearBegin"
1288
+ _default_month = 1
1289
+ _prefix = " BAS"
1290
+ _day_opt = " business_start"
1291
+
1292
+
1293
+ cdef class YearEnd(YearOffset):
1294
+ """
1295
+ DateOffset increments between calendar year ends.
1296
+ """
1297
+
1298
+ _default_month = 12
1299
+ _prefix = " A"
1300
+ _day_opt = " end"
1301
+
1302
+
1303
+ cdef class YearBegin(YearOffset):
1304
+ """
1305
+ DateOffset increments between calendar year begin dates.
1306
+ """
1307
+
1308
+ _default_month = 1
1309
+ _prefix = " AS"
1310
+ _day_opt = " start"
1311
+
1312
+
1313
+ # ----------------------------------------------------------------------
1314
+ # Quarter-Based Offset Classes
1315
+
1245
1316
cdef class QuarterOffset(SingleConstructorOffset):
1246
1317
_attributes = frozenset ([" n" , " normalize" , " startingMonth" ])
1247
1318
# TODO: Consider combining QuarterOffset and YearOffset __init__ at some
@@ -1262,6 +1333,11 @@ cdef class QuarterOffset(SingleConstructorOffset):
1262
1333
startingMonth = self ._default_startingMonth
1263
1334
self .startingMonth = startingMonth
1264
1335
1336
+ cpdef __setstate__(self , state):
1337
+ self .startingMonth = state.pop(" startingMonth" )
1338
+ self .n = state.pop(" n" )
1339
+ self .normalize = state.pop(" normalize" )
1340
+
1265
1341
def __reduce__ (self ):
1266
1342
return type (self ), (self .n, self .normalize, self .startingMonth)
1267
1343
@@ -1311,6 +1387,57 @@ cdef class QuarterOffset(SingleConstructorOffset):
1311
1387
return type (dtindex)._simple_new(shifted, dtype = dtindex.dtype)
1312
1388
1313
1389
1390
+ cdef class BQuarterEnd(QuarterOffset):
1391
+ """
1392
+ DateOffset increments between business Quarter dates.
1393
+
1394
+ startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
1395
+ startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
1396
+ startingMonth = 3 corresponds to dates like 3/30/2007, 6/29/2007, ...
1397
+ """
1398
+ _outputName = " BusinessQuarterEnd"
1399
+ _default_startingMonth = 3
1400
+ _from_name_startingMonth = 12
1401
+ _prefix = " BQ"
1402
+ _day_opt = " business_end"
1403
+
1404
+
1405
+ # TODO: This is basically the same as BQuarterEnd
1406
+ cdef class BQuarterBegin(QuarterOffset):
1407
+ _outputName = " BusinessQuarterBegin"
1408
+ # I suspect this is wrong for *all* of them.
1409
+ # TODO: What does the above comment refer to?
1410
+ _default_startingMonth = 3
1411
+ _from_name_startingMonth = 1
1412
+ _prefix = " BQS"
1413
+ _day_opt = " business_start"
1414
+
1415
+
1416
+ cdef class QuarterEnd(QuarterOffset):
1417
+ """
1418
+ DateOffset increments between business Quarter dates.
1419
+
1420
+ startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
1421
+ startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
1422
+ startingMonth = 3 corresponds to dates like 3/31/2007, 6/30/2007, ...
1423
+ """
1424
+ _outputName = " QuarterEnd"
1425
+ _default_startingMonth = 3
1426
+ _prefix = " Q"
1427
+ _day_opt = " end"
1428
+
1429
+
1430
+ cdef class QuarterBegin(QuarterOffset):
1431
+ _outputName = " QuarterBegin"
1432
+ _default_startingMonth = 3
1433
+ _from_name_startingMonth = 1
1434
+ _prefix = " QS"
1435
+ _day_opt = " start"
1436
+
1437
+
1438
+ # ----------------------------------------------------------------------
1439
+ # Month-Based Offset Classes
1440
+
1314
1441
cdef class MonthOffset(SingleConstructorOffset):
1315
1442
def is_on_offset (self , dt ) -> bool:
1316
1443
if self.normalize and not is_normalized(dt ):
@@ -1329,6 +1456,38 @@ cdef class MonthOffset(SingleConstructorOffset):
1329
1456
return type (dtindex)._simple_new(shifted, dtype = dtindex.dtype)
1330
1457
1331
1458
1459
+ cdef class MonthEnd(MonthOffset):
1460
+ """
1461
+ DateOffset of one month end.
1462
+ """
1463
+ _prefix = " M"
1464
+ _day_opt = " end"
1465
+
1466
+
1467
+ cdef class MonthBegin(MonthOffset):
1468
+ """
1469
+ DateOffset of one month at beginning.
1470
+ """
1471
+ _prefix = " MS"
1472
+ _day_opt = " start"
1473
+
1474
+
1475
+ cdef class BusinessMonthEnd(MonthOffset):
1476
+ """
1477
+ DateOffset increments between business EOM dates.
1478
+ """
1479
+ _prefix = " BM"
1480
+ _day_opt = " business_end"
1481
+
1482
+
1483
+ cdef class BusinessMonthBegin(MonthOffset):
1484
+ """
1485
+ DateOffset of one business month at beginning.
1486
+ """
1487
+ _prefix = " BMS"
1488
+ _day_opt = " business_start"
1489
+
1490
+
1332
1491
# ---------------------------------------------------------------------
1333
1492
# Special Offset Classes
1334
1493
0 commit comments