Skip to content

Commit 2c657dd

Browse files
jbrockmendelharisbal
authored and
harisbal
committed
Continue porting period_helper; fix leftover asfreq bug (pandas-dev#19834)
1 parent 8466004 commit 2c657dd

File tree

6 files changed

+128
-187
lines changed

6 files changed

+128
-187
lines changed

doc/source/whatsnew/v0.23.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ Timedelta
765765
- Bug in :class:`TimedeltaIndex` where division by a ``Series`` would return a ``TimedeltaIndex`` instead of a ``Series`` (:issue:`19042`)
766766
- Bug in :func:`Timedelta.__add__`, :func:`Timedelta.__sub__` where adding or subtracting a ``np.timedelta64`` object would return another ``np.timedelta64`` instead of a ``Timedelta`` (:issue:`19738`)
767767
- Bug in :func:`Timedelta.__floordiv__`, :func:`Timedelta.__rfloordiv__` where operating with a ``Tick`` object would raise a ``TypeError`` instead of returning a numeric value (:issue:`19738`)
768-
- Bug in :func:`Period.asfreq` where periods near ``datetime(1, 1, 1)`` could be converted incorrectly (:issue:`19643`)
768+
- Bug in :func:`Period.asfreq` where periods near ``datetime(1, 1, 1)`` could be converted incorrectly (:issue:`19643`, :issue:`19834`)
769769
- Bug in :func:`Timedelta.total_seconds()` causing precision errors i.e. ``Timedelta('30S').total_seconds()==30.000000000000004`` (:issue:`19458`)
770770
- Bug in :func: `Timedelta.__rmod__` where operating with a ``numpy.timedelta64`` returned a ``timedelta64`` object instead of a ``Timedelta`` (:issue:`19820`)
771771
- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue`19333`)

pandas/_libs/src/period_helper.c

+56-64
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ static int floordiv(int x, int divisor) {
4242
static int monthToQuarter(int month) { return ((month - 1) / 3) + 1; }
4343

4444

45-
/* Find the absdate (days elapsed since datetime(1, 1, 1)
45+
/* Find the unix_date (days elapsed since datetime(1970, 1, 1)
4646
* for the given year/month/day.
4747
* Assumes GREGORIAN_CALENDAR */
48-
npy_int64 absdate_from_ymd(int year, int month, int day) {
48+
npy_int64 unix_date_from_ymd(int year, int month, int day) {
4949
/* Calculate the absolute date */
5050
pandas_datetimestruct dts;
5151
npy_int64 unix_date;
@@ -55,16 +55,16 @@ npy_int64 absdate_from_ymd(int year, int month, int day) {
5555
dts.month = month;
5656
dts.day = day;
5757
unix_date = pandas_datetimestruct_to_datetime(PANDAS_FR_D, &dts);
58-
return ORD_OFFSET + unix_date;
58+
return unix_date;
5959
}
6060

6161
/* Sets the date part of the date_info struct
6262
Assumes GREGORIAN_CALENDAR */
6363
static int dInfoCalc_SetFromAbsDate(register struct date_info *dinfo,
64-
npy_int64 absdate) {
64+
npy_int64 unix_date) {
6565
pandas_datetimestruct dts;
6666

67-
pandas_datetime_to_datetimestruct(absdate - ORD_OFFSET, PANDAS_FR_D, &dts);
67+
pandas_datetime_to_datetimestruct(unix_date, PANDAS_FR_D, &dts);
6868
dinfo->year = dts.year;
6969
dinfo->month = dts.month;
7070
dinfo->day = dts.day;
@@ -137,26 +137,26 @@ PANDAS_INLINE npy_int64 transform_via_day(npy_int64 ordinal,
137137
return result;
138138
}
139139

140-
static npy_int64 DtoB_weekday(npy_int64 absdate) {
141-
return floordiv(absdate, 7) * 5 + mod_compat(absdate, 7) - BDAY_OFFSET;
140+
static npy_int64 DtoB_weekday(npy_int64 unix_date) {
141+
return floordiv(unix_date + 4, 7) * 5 + mod_compat(unix_date + 4, 7) - 4;
142142
}
143143

144144
static npy_int64 DtoB(struct date_info *dinfo,
145-
int roll_back, npy_int64 absdate) {
145+
int roll_back, npy_int64 unix_date) {
146146
int day_of_week = dayofweek(dinfo->year, dinfo->month, dinfo->day);
147147

148148
if (roll_back == 1) {
149149
if (day_of_week > 4) {
150150
// change to friday before weekend
151-
absdate -= (day_of_week - 4);
151+
unix_date -= (day_of_week - 4);
152152
}
153153
} else {
154154
if (day_of_week > 4) {
155155
// change to Monday after weekend
156-
absdate += (7 - day_of_week);
156+
unix_date += (7 - day_of_week);
157157
}
158158
}
159-
return DtoB_weekday(absdate);
159+
return DtoB_weekday(unix_date);
160160
}
161161

162162

@@ -165,18 +165,19 @@ static npy_int64 DtoB(struct date_info *dinfo,
165165
static npy_int64 asfreq_DTtoA(npy_int64 ordinal, asfreq_info *af_info) {
166166
struct date_info dinfo;
167167
ordinal = downsample_daytime(ordinal, af_info);
168-
dInfoCalc_SetFromAbsDate(&dinfo, ordinal + ORD_OFFSET);
168+
dInfoCalc_SetFromAbsDate(&dinfo, ordinal);
169169
if (dinfo.month > af_info->to_a_year_end) {
170-
return (npy_int64)(dinfo.year + 1 - BASE_YEAR);
170+
return (npy_int64)(dinfo.year + 1 - 1970);
171171
} else {
172-
return (npy_int64)(dinfo.year - BASE_YEAR);
172+
return (npy_int64)(dinfo.year - 1970);
173173
}
174174
}
175175

176-
static npy_int64 DtoQ_yq(npy_int64 ordinal, asfreq_info *af_info, int *year,
177-
int *quarter) {
176+
static int DtoQ_yq(npy_int64 ordinal, asfreq_info *af_info, int *year) {
178177
struct date_info dinfo;
179-
dInfoCalc_SetFromAbsDate(&dinfo, ordinal + ORD_OFFSET);
178+
int quarter;
179+
180+
dInfoCalc_SetFromAbsDate(&dinfo, ordinal);
180181
if (af_info->to_q_year_end != 12) {
181182
dinfo.month -= af_info->to_q_year_end;
182183
if (dinfo.month <= 0) {
@@ -187,47 +188,43 @@ static npy_int64 DtoQ_yq(npy_int64 ordinal, asfreq_info *af_info, int *year,
187188
}
188189

189190
*year = dinfo.year;
190-
*quarter = monthToQuarter(dinfo.month);
191-
192-
return 0;
191+
quarter = monthToQuarter(dinfo.month);
192+
return quarter;
193193
}
194194

195195
static npy_int64 asfreq_DTtoQ(npy_int64 ordinal, asfreq_info *af_info) {
196196
int year, quarter;
197197

198198
ordinal = downsample_daytime(ordinal, af_info);
199199

200-
DtoQ_yq(ordinal, af_info, &year, &quarter);
201-
return (npy_int64)((year - BASE_YEAR) * 4 + quarter - 1);
200+
quarter = DtoQ_yq(ordinal, af_info, &year);
201+
return (npy_int64)((year - 1970) * 4 + quarter - 1);
202202
}
203203

204204
static npy_int64 asfreq_DTtoM(npy_int64 ordinal, asfreq_info *af_info) {
205205
struct date_info dinfo;
206206

207207
ordinal = downsample_daytime(ordinal, af_info);
208208

209-
dInfoCalc_SetFromAbsDate(&dinfo, ordinal + ORD_OFFSET);
210-
return (npy_int64)((dinfo.year - BASE_YEAR) * 12 + dinfo.month - 1);
209+
dInfoCalc_SetFromAbsDate(&dinfo, ordinal);
210+
return (npy_int64)((dinfo.year - 1970) * 12 + dinfo.month - 1);
211211
}
212212

213213
static npy_int64 asfreq_DTtoW(npy_int64 ordinal, asfreq_info *af_info) {
214214
ordinal = downsample_daytime(ordinal, af_info);
215-
return (ordinal + ORD_OFFSET - (1 + af_info->to_week_end)) / 7 + 1 -
216-
WEEK_OFFSET;
215+
return floordiv(ordinal + 3 - af_info->to_week_end, 7) + 1;
217216
}
218217

219218
static npy_int64 asfreq_DTtoB(npy_int64 ordinal, asfreq_info *af_info) {
220219
struct date_info dinfo;
221-
npy_int64 absdate;
222220
int roll_back;
223221

224222
ordinal = downsample_daytime(ordinal, af_info);
225-
absdate = ordinal + ORD_OFFSET;
226-
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
223+
dInfoCalc_SetFromAbsDate(&dinfo, ordinal);
227224

228225
// This usage defines roll_back the opposite way from the others
229226
roll_back = 1 - af_info->is_end;
230-
return DtoB(&dinfo, roll_back, absdate);
227+
return DtoB(&dinfo, roll_back, ordinal);
231228
}
232229

233230
// all intra day calculations are now done within one function
@@ -243,10 +240,7 @@ static npy_int64 asfreq_UpsampleWithinDay(npy_int64 ordinal,
243240
//************ FROM BUSINESS ***************
244241

245242
static npy_int64 asfreq_BtoDT(npy_int64 ordinal, asfreq_info *af_info) {
246-
ordinal += BDAY_OFFSET;
247-
ordinal =
248-
(floordiv(ordinal - 1, 5) * 7 + mod_compat(ordinal - 1, 5) + 1 -
249-
ORD_OFFSET);
243+
ordinal = floordiv(ordinal + 3, 5) * 7 + mod_compat(ordinal + 3, 5) - 3;
250244

251245
return upsample_daytime(ordinal, af_info);
252246
}
@@ -270,8 +264,7 @@ static npy_int64 asfreq_BtoW(npy_int64 ordinal, asfreq_info *af_info) {
270264
//************ FROM WEEKLY ***************
271265

272266
static npy_int64 asfreq_WtoDT(npy_int64 ordinal, asfreq_info *af_info) {
273-
ordinal = (ordinal + WEEK_OFFSET) * 7 +
274-
af_info->from_week_end - ORD_OFFSET +
267+
ordinal = ordinal * 7 + af_info->from_week_end - 4 +
275268
(7 - 1) * (af_info->is_end - 1);
276269
return upsample_daytime(ordinal, af_info);
277270
}
@@ -294,30 +287,29 @@ static npy_int64 asfreq_WtoW(npy_int64 ordinal, asfreq_info *af_info) {
294287

295288
static npy_int64 asfreq_WtoB(npy_int64 ordinal, asfreq_info *af_info) {
296289
struct date_info dinfo;
297-
npy_int64 absdate = asfreq_WtoDT(ordinal, af_info) + ORD_OFFSET;
290+
npy_int64 unix_date = asfreq_WtoDT(ordinal, af_info);
298291
int roll_back = af_info->is_end;
299-
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
292+
dInfoCalc_SetFromAbsDate(&dinfo, unix_date);
300293

301-
return DtoB(&dinfo, roll_back, absdate);
294+
return DtoB(&dinfo, roll_back, unix_date);
302295
}
303296

304297
//************ FROM MONTHLY ***************
305298
static void MtoD_ym(npy_int64 ordinal, int *y, int *m) {
306-
*y = floordiv(ordinal, 12) + BASE_YEAR;
299+
*y = floordiv(ordinal, 12) + 1970;
307300
*m = mod_compat(ordinal, 12) + 1;
308301
}
309302

310303
static npy_int64 asfreq_MtoDT(npy_int64 ordinal, asfreq_info *af_info) {
311-
npy_int64 absdate;
304+
npy_int64 unix_date;
312305
int y, m;
313306

314307
ordinal += af_info->is_end;
315308
MtoD_ym(ordinal, &y, &m);
316-
absdate = absdate_from_ymd(y, m, 1);
317-
ordinal = absdate - ORD_OFFSET;
309+
unix_date = unix_date_from_ymd(y, m, 1);
318310

319-
ordinal -= af_info->is_end;
320-
return upsample_daytime(ordinal, af_info);
311+
unix_date -= af_info->is_end;
312+
return upsample_daytime(unix_date, af_info);
321313
}
322314

323315
static npy_int64 asfreq_MtoA(npy_int64 ordinal, asfreq_info *af_info) {
@@ -334,18 +326,18 @@ static npy_int64 asfreq_MtoW(npy_int64 ordinal, asfreq_info *af_info) {
334326

335327
static npy_int64 asfreq_MtoB(npy_int64 ordinal, asfreq_info *af_info) {
336328
struct date_info dinfo;
337-
npy_int64 absdate = asfreq_MtoDT(ordinal, af_info) + ORD_OFFSET;
329+
npy_int64 unix_date = asfreq_MtoDT(ordinal, af_info);
338330
int roll_back = af_info->is_end;
339331

340-
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
332+
dInfoCalc_SetFromAbsDate(&dinfo, unix_date);
341333

342-
return DtoB(&dinfo, roll_back, absdate);
334+
return DtoB(&dinfo, roll_back, unix_date);
343335
}
344336

345337
//************ FROM QUARTERLY ***************
346338

347339
static void QtoD_ym(npy_int64 ordinal, int *y, int *m, asfreq_info *af_info) {
348-
*y = floordiv(ordinal, 4) + BASE_YEAR;
340+
*y = floordiv(ordinal, 4) + 1970;
349341
*m = mod_compat(ordinal, 4) * 3 + 1;
350342

351343
if (af_info->from_q_year_end != 12) {
@@ -359,16 +351,16 @@ static void QtoD_ym(npy_int64 ordinal, int *y, int *m, asfreq_info *af_info) {
359351
}
360352

361353
static npy_int64 asfreq_QtoDT(npy_int64 ordinal, asfreq_info *af_info) {
362-
npy_int64 absdate;
354+
npy_int64 unix_date;
363355
int y, m;
364356

365357
ordinal += af_info->is_end;
366358
QtoD_ym(ordinal, &y, &m, af_info);
367359

368-
absdate = absdate_from_ymd(y, m, 1);
360+
unix_date = unix_date_from_ymd(y, m, 1);
369361

370-
absdate -= af_info->is_end;
371-
return upsample_daytime(absdate - ORD_OFFSET, af_info);
362+
unix_date -= af_info->is_end;
363+
return upsample_daytime(unix_date, af_info);
372364
}
373365

374366
static npy_int64 asfreq_QtoQ(npy_int64 ordinal, asfreq_info *af_info) {
@@ -389,32 +381,32 @@ static npy_int64 asfreq_QtoW(npy_int64 ordinal, asfreq_info *af_info) {
389381

390382
static npy_int64 asfreq_QtoB(npy_int64 ordinal, asfreq_info *af_info) {
391383
struct date_info dinfo;
392-
npy_int64 absdate = asfreq_QtoDT(ordinal, af_info) + ORD_OFFSET;
384+
npy_int64 unix_date = asfreq_QtoDT(ordinal, af_info);
393385
int roll_back = af_info->is_end;
394386

395-
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
387+
dInfoCalc_SetFromAbsDate(&dinfo, unix_date);
396388

397-
return DtoB(&dinfo, roll_back, absdate);
389+
return DtoB(&dinfo, roll_back, unix_date);
398390
}
399391

400392
//************ FROM ANNUAL ***************
401393

402394
static npy_int64 asfreq_AtoDT(npy_int64 ordinal, asfreq_info *af_info) {
403-
npy_int64 absdate;
395+
npy_int64 unix_date;
404396

405397
// start from 1970
406-
npy_int64 year = ordinal + BASE_YEAR;
398+
npy_int64 year = ordinal + 1970;
407399

408400
int month = (af_info->from_a_year_end % 12) + 1;
409401
if (af_info->from_a_year_end != 12) {
410402
year -= 1;
411403
}
412404

413405
year += af_info->is_end;
414-
absdate = absdate_from_ymd(year, month, 1);
406+
unix_date = unix_date_from_ymd(year, month, 1);
415407

416-
absdate -= af_info->is_end;
417-
return upsample_daytime(absdate - ORD_OFFSET, af_info);
408+
unix_date -= af_info->is_end;
409+
return upsample_daytime(unix_date, af_info);
418410
}
419411

420412
static npy_int64 asfreq_AtoA(npy_int64 ordinal, asfreq_info *af_info) {
@@ -435,11 +427,11 @@ static npy_int64 asfreq_AtoW(npy_int64 ordinal, asfreq_info *af_info) {
435427

436428
static npy_int64 asfreq_AtoB(npy_int64 ordinal, asfreq_info *af_info) {
437429
struct date_info dinfo;
438-
npy_int64 absdate = asfreq_AtoDT(ordinal, af_info) + ORD_OFFSET;
430+
npy_int64 unix_date = asfreq_AtoDT(ordinal, af_info);
439431
int roll_back = af_info->is_end;
440-
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
432+
dInfoCalc_SetFromAbsDate(&dinfo, unix_date);
441433

442-
return DtoB(&dinfo, roll_back, absdate);
434+
return DtoB(&dinfo, roll_back, unix_date);
443435
}
444436

445437
static npy_int64 nofunc(npy_int64 ordinal, asfreq_info *af_info) {

pandas/_libs/src/period_helper.h

-24
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,8 @@ frequency conversion routines.
2020
#include "limits.h"
2121
#include "numpy/ndarraytypes.h"
2222

23-
/*
24-
* declarations from period here
25-
*/
26-
27-
#define Py_Error(errortype, errorstr) \
28-
{ \
29-
PyErr_SetString(errortype, errorstr); \
30-
goto onError; \
31-
}
32-
3323
/*** FREQUENCY CONSTANTS ***/
3424

35-
// HIGHFREQ_ORIG is the datetime ordinal from which to begin the second
36-
// frequency ordinal sequence
37-
38-
// #define HIGHFREQ_ORIG 62135683200LL
39-
#define BASE_YEAR 1970
40-
#define ORD_OFFSET 719163LL // days until 1970-01-01
41-
#define BDAY_OFFSET 513689LL // days until 1970-01-01
42-
#define WEEK_OFFSET 102737LL
43-
#define BASE_WEEK_TO_DAY_OFFSET \
44-
1 // difference between day 0 and end of week in days
45-
#define DAYS_PER_WEEK 7
46-
#define BUSINESS_DAYS_PER_WEEK 5
47-
#define HIGHFREQ_ORIG 0 // ORD_OFFSET * 86400LL // days until 1970-01-01
48-
4925
#define FR_ANN 1000 /* Annual */
5026
#define FR_ANNDEC FR_ANN /* Annual - December year end*/
5127
#define FR_ANNJAN 1001 /* Annual - January year end*/

0 commit comments

Comments
 (0)