Skip to content

Commit 507a2a2

Browse files
jbrockmendeljreback
authored andcommitted
Continue porting period_helper to cython (#19608)
1 parent 13bd008 commit 507a2a2

File tree

3 files changed

+349
-305
lines changed

3 files changed

+349
-305
lines changed

pandas/_libs/src/period_helper.c

+20-266
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ static int monthToQuarter(int month) { return ((month - 1) / 3) + 1; }
4545
/* Find the absdate (days elapsed since datetime(1, 1, 1)
4646
* for the given year/month/day.
4747
* Assumes GREGORIAN_CALENDAR */
48-
static npy_int64 dInfoCalc_SetFromDateAndTime(int year, int month, int day) {
48+
npy_int64 absdate_from_ymd(int year, int month, int day) {
4949
/* Calculate the absolute date */
5050
pandas_datetimestruct dts;
5151
npy_int64 unix_date;
@@ -68,8 +68,6 @@ static int dInfoCalc_SetFromAbsDate(register struct date_info *dinfo,
6868
dinfo->year = dts.year;
6969
dinfo->month = dts.month;
7070
dinfo->day = dts.day;
71-
72-
dinfo->absdate = absdate;
7371
return 0;
7472
}
7573

@@ -100,8 +98,7 @@ PANDAS_INLINE int get_freq_group(int freq) { return (freq / 1000) * 1000; }
10098
PANDAS_INLINE int get_freq_group_index(int freq) { return freq / 1000; }
10199

102100

103-
PANDAS_INLINE npy_int64 get_daytime_conversion_factor(int from_index,
104-
int to_index) {
101+
npy_int64 get_daytime_conversion_factor(int from_index, int to_index) {
105102
int row = min_value(from_index, to_index);
106103
int col = max_value(from_index, to_index);
107104
// row or col < 6 means frequency strictly lower than Daily, which
@@ -144,9 +141,9 @@ static npy_int64 DtoB_weekday(npy_int64 absdate) {
144141
return (((absdate) / 7) * 5) + (absdate) % 7 - BDAY_OFFSET;
145142
}
146143

147-
static npy_int64 DtoB(struct date_info *dinfo, int roll_back) {
144+
static npy_int64 DtoB(struct date_info *dinfo,
145+
int roll_back, npy_int64 absdate) {
148146
int day_of_week = dayofweek(dinfo->year, dinfo->month, dinfo->day);
149-
npy_int64 absdate = dinfo->absdate;
150147

151148
if (roll_back == 1) {
152149
if (day_of_week > 4) {
@@ -162,9 +159,6 @@ static npy_int64 DtoB(struct date_info *dinfo, int roll_back) {
162159
return DtoB_weekday(absdate);
163160
}
164161

165-
static npy_int64 absdate_from_ymd(int y, int m, int d) {
166-
return dInfoCalc_SetFromDateAndTime(y, m, d);
167-
}
168162

169163
//************ FROM DAILY ***************
170164

@@ -224,15 +218,16 @@ static npy_int64 asfreq_DTtoW(npy_int64 ordinal, asfreq_info *af_info) {
224218

225219
static npy_int64 asfreq_DTtoB(npy_int64 ordinal, asfreq_info *af_info) {
226220
struct date_info dinfo;
221+
npy_int64 absdate;
227222
int roll_back;
228223

229224
ordinal = downsample_daytime(ordinal, af_info);
230-
231-
dInfoCalc_SetFromAbsDate(&dinfo, ordinal + ORD_OFFSET);
225+
absdate = ordinal + ORD_OFFSET;
226+
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
232227

233228
// This usage defines roll_back the opposite way from the others
234229
roll_back = 1 - af_info->is_end;
235-
return DtoB(&dinfo, roll_back);
230+
return DtoB(&dinfo, roll_back, absdate);
236231
}
237232

238233
// all intra day calculations are now done within one function
@@ -298,11 +293,11 @@ static npy_int64 asfreq_WtoW(npy_int64 ordinal, asfreq_info *af_info) {
298293

299294
static npy_int64 asfreq_WtoB(npy_int64 ordinal, asfreq_info *af_info) {
300295
struct date_info dinfo;
296+
npy_int64 absdate = asfreq_WtoDT(ordinal, af_info) + ORD_OFFSET;
301297
int roll_back = af_info->is_end;
302-
dInfoCalc_SetFromAbsDate(
303-
&dinfo, asfreq_WtoDT(ordinal, af_info) + ORD_OFFSET);
298+
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
304299

305-
return DtoB(&dinfo, roll_back);
300+
return DtoB(&dinfo, roll_back, absdate);
306301
}
307302

308303
//************ FROM MONTHLY ***************
@@ -338,12 +333,12 @@ static npy_int64 asfreq_MtoW(npy_int64 ordinal, asfreq_info *af_info) {
338333

339334
static npy_int64 asfreq_MtoB(npy_int64 ordinal, asfreq_info *af_info) {
340335
struct date_info dinfo;
336+
npy_int64 absdate = asfreq_MtoDT(ordinal, af_info) + ORD_OFFSET;
341337
int roll_back = af_info->is_end;
342338

343-
dInfoCalc_SetFromAbsDate(
344-
&dinfo, asfreq_MtoDT(ordinal, af_info) + ORD_OFFSET);
339+
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
345340

346-
return DtoB(&dinfo, roll_back);
341+
return DtoB(&dinfo, roll_back, absdate);
347342
}
348343

349344
//************ FROM QUARTERLY ***************
@@ -393,12 +388,12 @@ static npy_int64 asfreq_QtoW(npy_int64 ordinal, asfreq_info *af_info) {
393388

394389
static npy_int64 asfreq_QtoB(npy_int64 ordinal, asfreq_info *af_info) {
395390
struct date_info dinfo;
391+
npy_int64 absdate = asfreq_QtoDT(ordinal, af_info) + ORD_OFFSET;
396392
int roll_back = af_info->is_end;
397393

398-
dInfoCalc_SetFromAbsDate(
399-
&dinfo, asfreq_QtoDT(ordinal, af_info) + ORD_OFFSET);
394+
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
400395

401-
return DtoB(&dinfo, roll_back);
396+
return DtoB(&dinfo, roll_back, absdate);
402397
}
403398

404399
//************ FROM ANNUAL ***************
@@ -439,11 +434,11 @@ static npy_int64 asfreq_AtoW(npy_int64 ordinal, asfreq_info *af_info) {
439434

440435
static npy_int64 asfreq_AtoB(npy_int64 ordinal, asfreq_info *af_info) {
441436
struct date_info dinfo;
437+
npy_int64 absdate = asfreq_AtoDT(ordinal, af_info) + ORD_OFFSET;
442438
int roll_back = af_info->is_end;
443-
dInfoCalc_SetFromAbsDate(
444-
&dinfo, asfreq_AtoDT(ordinal, af_info) + ORD_OFFSET);
439+
dInfoCalc_SetFromAbsDate(&dinfo, absdate);
445440

446-
return DtoB(&dinfo, roll_back);
441+
return DtoB(&dinfo, roll_back, absdate);
447442
}
448443

449444
static npy_int64 nofunc(npy_int64 ordinal, asfreq_info *af_info) {
@@ -675,65 +670,6 @@ freq_conv_func get_asfreq_func(int fromFreq, int toFreq) {
675670
}
676671
}
677672

678-
double get_abs_time(int freq, npy_int64 date_ordinal, npy_int64 ordinal) {
679-
int freq_index, day_index, base_index;
680-
npy_int64 per_day, start_ord;
681-
double unit, result;
682-
683-
if (freq <= FR_DAY) {
684-
return 0;
685-
}
686-
687-
freq_index = get_freq_group_index(freq);
688-
day_index = get_freq_group_index(FR_DAY);
689-
base_index = get_freq_group_index(FR_SEC);
690-
691-
per_day = get_daytime_conversion_factor(day_index, freq_index);
692-
unit = get_daytime_conversion_factor(freq_index, base_index);
693-
694-
if (base_index < freq_index) {
695-
unit = 1 / unit;
696-
}
697-
698-
start_ord = date_ordinal * per_day;
699-
result = (double)(unit * (ordinal - start_ord));
700-
return result;
701-
}
702-
703-
/* Sets the time part of the DateTime object. */
704-
static int dInfoCalc_SetFromAbsTime(struct date_info *dinfo, double abstime) {
705-
int inttime;
706-
int hour, minute;
707-
double second;
708-
709-
inttime = (int)abstime;
710-
hour = inttime / 3600;
711-
minute = (inttime % 3600) / 60;
712-
second = abstime - (double)(hour * 3600 + minute * 60);
713-
714-
dinfo->hour = hour;
715-
dinfo->minute = minute;
716-
dinfo->second = second;
717-
return 0;
718-
}
719-
720-
/* Set the instance's value using the given date and time.
721-
Assumes GREGORIAN_CALENDAR. */
722-
static int dInfoCalc_SetFromAbsDateTime(struct date_info *dinfo,
723-
npy_int64 absdate, double abstime) {
724-
/* Bounds check */
725-
// The calling function is responsible for ensuring that
726-
// abstime >= 0.0 && abstime <= 86400
727-
728-
/* Calculate the date */
729-
dInfoCalc_SetFromAbsDate(dinfo, absdate);
730-
731-
/* Calculate the time */
732-
dInfoCalc_SetFromAbsTime(dinfo, abstime);
733-
734-
return 0;
735-
}
736-
737673
/* ------------------------------------------------------------------
738674
* New pandas API-helper code, to expose to cython
739675
* ------------------------------------------------------------------*/
@@ -750,185 +686,3 @@ npy_int64 asfreq(npy_int64 period_ordinal, int freq1, int freq2,
750686
val = (*func)(period_ordinal, &finfo);
751687
return val;
752688
}
753-
754-
/* generate an ordinal in period space */
755-
npy_int64 get_period_ordinal(int year, int month, int day, int hour, int minute,
756-
int second, int microseconds, int picoseconds,
757-
int freq) {
758-
npy_int64 absdays, delta, seconds;
759-
npy_int64 weeks, days;
760-
npy_int64 ordinal, day_adj;
761-
int freq_group, fmonth, mdiff;
762-
freq_group = get_freq_group(freq);
763-
764-
if (freq == FR_SEC || freq == FR_MS || freq == FR_US || freq == FR_NS) {
765-
absdays = absdate_from_ymd(year, month, day);
766-
delta = (absdays - ORD_OFFSET);
767-
seconds =
768-
(npy_int64)(delta * 86400 + hour * 3600 + minute * 60 + second);
769-
770-
switch (freq) {
771-
case FR_MS:
772-
return seconds * 1000 + microseconds / 1000;
773-
774-
case FR_US:
775-
return seconds * 1000000 + microseconds;
776-
777-
case FR_NS:
778-
return seconds * 1000000000 + microseconds * 1000 +
779-
picoseconds / 1000;
780-
}
781-
782-
return seconds;
783-
}
784-
785-
if (freq == FR_MIN) {
786-
absdays = absdate_from_ymd(year, month, day);
787-
delta = (absdays - ORD_OFFSET);
788-
return (npy_int64)(delta * 1440 + hour * 60 + minute);
789-
}
790-
791-
if (freq == FR_HR) {
792-
absdays = absdate_from_ymd(year, month, day);
793-
delta = (absdays - ORD_OFFSET);
794-
return (npy_int64)(delta * 24 + hour);
795-
}
796-
797-
if (freq == FR_DAY) {
798-
return (npy_int64)(absdate_from_ymd(year, month, day) - ORD_OFFSET);
799-
}
800-
801-
if (freq == FR_UND) {
802-
return (npy_int64)(absdate_from_ymd(year, month, day) - ORD_OFFSET);
803-
}
804-
805-
if (freq == FR_BUS) {
806-
days = absdate_from_ymd(year, month, day);
807-
// calculate the current week assuming sunday as last day of a week
808-
weeks = (days - BASE_WEEK_TO_DAY_OFFSET) / DAYS_PER_WEEK;
809-
// calculate the current weekday (in range 1 .. 7)
810-
delta = (days - BASE_WEEK_TO_DAY_OFFSET) % DAYS_PER_WEEK + 1;
811-
// return the number of business days in full weeks plus the business
812-
// days in the last - possible partial - week
813-
return (npy_int64)(weeks * BUSINESS_DAYS_PER_WEEK) +
814-
(delta <= BUSINESS_DAYS_PER_WEEK ? delta
815-
: BUSINESS_DAYS_PER_WEEK + 1) -
816-
BDAY_OFFSET;
817-
}
818-
819-
if (freq_group == FR_WK) {
820-
ordinal = (npy_int64)absdate_from_ymd(year, month, day);
821-
day_adj = freq - FR_WK;
822-
return (ordinal - (1 + day_adj)) / 7 + 1 - WEEK_OFFSET;
823-
}
824-
825-
if (freq == FR_MTH) {
826-
return (year - BASE_YEAR) * 12 + month - 1;
827-
}
828-
829-
if (freq_group == FR_QTR) {
830-
fmonth = freq - FR_QTR;
831-
if (fmonth == 0) fmonth = 12;
832-
833-
mdiff = month - fmonth;
834-
if (mdiff < 0) mdiff += 12;
835-
if (month >= fmonth) mdiff += 12;
836-
837-
return (year - BASE_YEAR) * 4 + (mdiff - 1) / 3;
838-
}
839-
840-
if (freq_group == FR_ANN) {
841-
fmonth = freq - FR_ANN;
842-
if (fmonth == 0) fmonth = 12;
843-
if (month <= fmonth) {
844-
return year - BASE_YEAR;
845-
} else {
846-
return year - BASE_YEAR + 1;
847-
}
848-
}
849-
850-
Py_Error(PyExc_RuntimeError, "Unable to generate frequency ordinal");
851-
852-
onError:
853-
return INT_ERR_CODE;
854-
}
855-
856-
/*
857-
Returns the proleptic Gregorian ordinal of the date, as an integer.
858-
This corresponds to the number of days since Jan., 1st, 1AD.
859-
When the instance has a frequency less than daily, the proleptic date
860-
is calculated for the last day of the period.
861-
*/
862-
863-
npy_int64 get_python_ordinal(npy_int64 period_ordinal, int freq) {
864-
asfreq_info af_info;
865-
freq_conv_func toDaily = NULL;
866-
867-
if (freq == FR_DAY) return period_ordinal + ORD_OFFSET;
868-
869-
toDaily = get_asfreq_func(freq, FR_DAY);
870-
get_asfreq_info(freq, FR_DAY, 'E', &af_info);
871-
872-
return toDaily(period_ordinal, &af_info) + ORD_OFFSET;
873-
}
874-
875-
876-
int get_yq(npy_int64 ordinal, int freq, int *quarter, int *year) {
877-
asfreq_info af_info;
878-
int qtr_freq;
879-
npy_int64 daily_ord;
880-
freq_conv_func toDaily = NULL;
881-
882-
toDaily = get_asfreq_func(freq, FR_DAY);
883-
get_asfreq_info(freq, FR_DAY, 'E', &af_info);
884-
885-
daily_ord = toDaily(ordinal, &af_info);
886-
887-
if (get_freq_group(freq) == FR_QTR) {
888-
qtr_freq = freq;
889-
} else {
890-
qtr_freq = FR_QTR;
891-
}
892-
get_asfreq_info(FR_DAY, qtr_freq, 'E', &af_info);
893-
894-
DtoQ_yq(daily_ord, &af_info, year, quarter);
895-
return 0;
896-
}
897-
898-
int _quarter_year(npy_int64 ordinal, int freq, int *year, int *quarter) {
899-
asfreq_info af_info;
900-
int qtr_freq;
901-
902-
ordinal = get_python_ordinal(ordinal, freq) - ORD_OFFSET;
903-
904-
if (get_freq_group(freq) == FR_QTR)
905-
qtr_freq = freq;
906-
else
907-
qtr_freq = FR_QTR;
908-
909-
get_asfreq_info(FR_DAY, qtr_freq, 'E', &af_info);
910-
911-
DtoQ_yq(ordinal, &af_info, year, quarter);
912-
913-
if ((qtr_freq % 1000) > 12) *year -= 1;
914-
915-
return 0;
916-
}
917-
918-
919-
int get_date_info(npy_int64 ordinal, int freq, struct date_info *dinfo) {
920-
npy_int64 absdate = get_python_ordinal(ordinal, freq);
921-
double abstime = get_abs_time(freq, absdate - ORD_OFFSET, ordinal);
922-
923-
while (abstime < 0) {
924-
abstime += 86400;
925-
absdate -= 1;
926-
}
927-
while (abstime >= 86400) {
928-
abstime -= 86400;
929-
absdate += 1;
930-
}
931-
932-
dInfoCalc_SetFromAbsDateTime(dinfo, absdate, abstime);
933-
return 0;
934-
}

0 commit comments

Comments
 (0)