8000 MAINT: Use `extract_unit` throughout datetime · numpy/numpy@b9fa88e · GitHub
[go: up one dir, main page]

Skip to content

Commit b9fa88e

Browse files
committed
MAINT: Use extract_unit throughout datetime
This eliminates a lot of duplicate work to handle the difference between C and python modulo. `extract_unit` has been moved to the top of the file, and has been split into identical 32- and 64-bit variants.
1 parent b844558 commit b9fa88e

File tree

1 file changed

+101
-138
lines changed

1 file changed

+101
-138
lines changed

numpy/core/src/multiarray/datetime.c

Lines changed: 101 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,40 @@
2626
#include "_datetime.h"
2727
#include "datetime_strings.h"
2828

29+
/*
30+
* Computes the python `ret, d = divmod(d, unit)`.
31+
*
32+
* Note that GCC is smart enough at -O2 to eliminate the `if(*d < 0)` branch
33+
* for subsequent calls to this command - it is able to deduce that `*d >= 0`.
34+
*/
35+
static inline
36+
npy_int64 extract_unit_64(npy_int64 *d, npy_int64 unit) {
37+
assert(unit > 0);
38+
npy_int64 div = *d / unit;
39+
npy_int64 mod = *d % unit;
40+
if (mod < 0) {
41+
mod += unit;
42+
div -= 1;
43+
}
44+
assert(mod >= 0);
45+
*d = mod;
46+
return div;
47+
}
48+
49+
static inline
50+
npy_int32 extract_unit_32(npy_int32 *d, npy_int32 unit) {
51+
assert(unit > 0);
52+
npy_int32 div = *d / unit;
53+
npy_int32 mod = *d % unit;
54+
if (mod < 0) {
55+
mod += unit;
56+
div -= 1;
57+
}
58+
assert(mod >= 0);
59+
*d = mod;
60+
return div;
61+
}
62+
2963
/*
3064
* Imports the PyDateTime functions so we can create these objects.
3165
* This is called during module initialization
@@ -160,17 +194,7 @@ days_to_yearsdays(npy_int64 *days_)
160194
npy_int64 year;
161195

162196
/* Break down the 400 year cycle to get the year and day within the year */
163-
if (days >= 0) {
164-
year = 400 * (days / days_per_400years);
165-
days = days % days_per_400years;
166-
}
167-
else {
168-
year = 400 * ((days - (days_per_400years - 1)) / days_per_400years);
169-
days = days % days_per_400years;
170-
if (days < 0) {
171-
days += days_per_400years;
172-
}
173-
}
197+
year = 400 * extract_unit_64(&days, days_per_400years);
174198

175199
/* Work out the year/day within the 400 year cycle */
176200
if (days >= 366) {
@@ -410,26 +434,6 @@ PyArray_TimedeltaStructToTimedelta(
410434
return -1;
411435
}
412436

413-
/*
414-
* Computes the python `ret, d = divmod(d, unit)`.
415-
*
416-
* Note that GCC is smart enough at -O2 to eliminate the `if(*d < 0)` branch
417-
* for subsequent calls to this command - it is able to deduce that `*d >= 0`.
418-
*/
419-
static inline
420-
npy_int64 extract_unit(npy_datetime *d, npy_datetime unit) {
421-
assert(unit > 0);
422-
npy_int64 div = *d / unit;
423-
npy_int64 mod = *d % unit;
424-
if (mod < 0) {
425-
mod += unit;
426-
div -= 1;
427-
}
428-
assert(mod >= 0);
429-
*d = mod;
430-
return div;
431-
}
432-
433437
/*
434438
* Converts a datetime based on the given metadata into a datetimestruct
435439
*/
@@ -438,7 +442,7 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
438442
npy_datetime dt,
439443
npy_datetimestruct *out)
440444
{
441-
npy_int64 perday;
445+
npy_int64 days;
442446

443447
/* Initialize the output to all zeros */
444448
memset(out, 0, sizeof(npy_datetimestruct));
@@ -473,7 +477,7 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
473477
break;
474478

475479
case NPY_FR_M:
476-
out->year = 1970 + extract_unit(&dt, 12);
480+
out->year = 1970 + extract_unit_64(&dt, 12);
477481
out->month = dt + 1;
478482
break;
479483

@@ -487,90 +491,84 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
487491
break;
488492

489493
case NPY_FR_h:
490-
perday = 24LL;
491-
492-
set_datetimestruct_days(extract_unit(&dt, perday), out);
494+
days = extract_unit_64(&dt, 24LL);
495+
set_datetimestruct_days(days, out);
493496
out->hour = (int)dt;
494497
break;
495498

496499
case NPY_FR_m:
497-
perday = 24LL * 60;
498-
499-
set_datetimestruct_days(extract_unit(&dt, perday), out);
500-
out->hour = (int)extract_unit(&dt, 60);
501-
out->min = (int)dt;
500+
days = extract_unit_64(&dt, 60LL*24);
501+
set_datetimestruct_days(days, out);
502+
out->hour = (int)extract_unit_64(&dt, 60LL);
503+
out->min = (int)dt;
502504
break;
503505

504506
case NPY_FR_s:
505-
perday = 24LL * 60 * 60;
506-
507-
set_datetimestruct_days(extract_unit(&dt, perday), out);
508-
out->hour = (int)extract_unit(&dt, 60*60);
509-
out->min = (int)extract_unit(&dt, 60);
507+
days = extract_unit_64(&dt, 60LL*60*24);
508+
set_datetimestruct_days(days, out);
509+
out->hour = (int)extract_unit_64(&dt, 60LL*60);
510+
out->min = (int)extract_unit_64(&dt, 60LL);
510511
out->sec = (int)dt;
511512
break;
512513

513514
case NPY_FR_ms:
514-
perday = 24LL * 60 * 60 * 1000;
515-
516-
set_datetimestruct_days(extract_unit(&dt, perday), out);
517-
out->hour = (int)extract_unit(&dt, 1000LL*60*60);
518-
out->min = (int)extract_unit(&dt, 1000LL*60);
519-
out->sec = (int)extract_unit(&dt, 1000LL);
515+
days = extract_unit_64(&dt, 1000LL*60*60*24);
516+
set_datetimestruct_days(days, out);
517+
out->hour = (int)extract_unit_64(&dt, 1000LL*60*60);
518+
out->min = (int)extract_unit_64(&dt, 1000LL*60);
519+
out->sec = (int)extract_unit_64(&dt, 1000LL);
520520
out->us = (int)(dt * 1000);
521521
break;
522522

523523
case NPY_FR_us:
524-
perday = 24LL * 60LL * 60LL * 1000LL * 1000LL;
525-
set_datetimestruct_days(extract_unit(&dt, perday), out);
526-
out->hour = (int)extract_unit(&dt, 1000LL*1000*60*60);
527-
out->min = (int)extract_unit(&dt, 1000LL*1000*60);
528-
out->sec = (int)extract_unit(&dt, 1000LL*1000);
524+
days = extract_unit_64(&dt, 1000LL*1000*60*60*24);
525+
set_datetimestruct_days(days, out);
526+
out->hour = (int)extract_unit_64(&dt, 1000LL*1000*60*60);
527+
out->min = (int)extract_unit_64(&dt, 1000LL*1000*60);
528+
out->sec = (int)extract_unit_64(&dt, 1000LL*1000);
529529
out->us = (int)dt;
530530
break;
531531

532532
case NPY_FR_ns:
533-
perday = 24LL * 60LL * 60LL * 1000LL * 1000LL * 1000LL;
534-
535-
set_datetimestruct_days(extract_unit(&dt, perday), out);
536-
out->hour = (int)extract_unit(&dt, 1000LL*1000*1000*60*60);
537-
out->min = (int)extract_unit(&dt, 1000LL*1000*1000*60);
538-
out->sec = (int)extract_unit(&dt, 1000LL*1000*1000);
539-
out->us = (int)extract_unit(&dt, 1000LL);
533+
days = extract_unit_64(&dt, 1000LL*1000*1000*60*60*24);
534+
set_datetimestruct_days(days, out);
535+
out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*60*60);
536+
out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*60);
537+
out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000);
538+
out->us = (int)extract_unit_64(&dt, 1000LL);
540539
out->ps = (int)(dt * 1000);
541540
break;
542541

543542
case NPY_FR_ps:
544-
perday = 24LL * 60 * 60 * 1000 * 1000 * 1000 * 1000;
545-
546-
set_datetimestruct_days(extract_unit(&dt, perday), out);
547-
out->hour = (int)extract_unit(&dt, 1000LL*1000*1000*1000*60*60);
548-
out->min = (int)extract_unit(&dt, 1000LL*1000*1000*1000*60);
549-
out->sec = (int)extract_unit(&dt, 1000LL*1000*1000*1000);
550-
out->us = (int)extract_unit(&dt, 1000LL*1000);
543+
days = extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60*24);
544+
set_datetimestruct_days(days, out);
545+
out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60);
546+
out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60);
547+
out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
548+
out->us = (int)extract_unit_64(&dt, 1000LL*1000);
551549
out->ps = (int)(dt);
552550
break;
553551

554552
case NPY_FR_fs:
555553
/* entire range is only +- 2.6 hours */
556-
out->hour = (int)extract_unit(&dt, 1000LL*1000*1000*1000*1000*60*60);
554+
out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60*60);
557555
if (out->hour < 0) {
558556
out->year = 1969;
559557
out->month = 12;
560558
out->day = 31;
561559
out->hour += 24;
562560
assert(out->hour >= 0);
563561
}
564-
out->min = (int)extract_unit(&dt, 1000LL*1000*1000*1000*1000*60);
565-
out->sec = (int)extract_unit(&dt, 1000LL*1000*1000*1000*1000);
566-
out->us = (int)extract_unit(&dt, 1000LL*1000*1000);
567-
out->ps = (int)extract_unit(&dt, 1000LL);
562+
out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60);
563+
out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000);
564+
out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000);
565+
out->ps = (int)extract_unit_64(&dt, 1000LL);
568566
out->as = (int)(dt * 1000);
569567
break;
570568

571569
case NPY_FR_as:
572570
/* entire range is only +- 9.2 seconds */
573-
out->sec = (int)extract_unit(&dt, 1000LL*1000*1000*1000*1000*1000);
571+
out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*1000);
574572
if (out->sec < 0) {
575573
out->year = 1969;
576574
out->month = 12;
@@ -580,8 +578,8 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
580578
out->sec += 60;
581579
assert(out->sec >= 0);
582580
}
583-
out->us = (int)extract_unit(&dt, 1000LL*1000*1000*1000);
584-
out->ps = (int)extract_unit(&dt, 1000LL*1000);
581+
out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
582+
out->ps = (int)extract_unit_64(&dt, 1000LL*1000);
585583
out->as = (int)dt;
586584
break;
587585

@@ -2017,20 +2015,8 @@ add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds)
20172015
int minutes;
20182016

20192017
dts->sec += seconds;
2020-
if (dts->sec < 0) {
2021-
minutes = dts->sec / 60;
2022-
dts->sec = dts->sec % 60;
2023-
if (dts->sec < 0) {
2024-
--minutes;
2025-
dts->sec += 60;
2026-
}
2027-
add_minutes_to_datetimestruct(dts, minutes);
2028-
}
2029-
else if (dts->sec >= 60) {
2030-
minutes = dts->sec / 60;
2031-
dts->sec = dts->sec % 60;
2032-
add_minutes_to_datetimestruct(dts, minutes);
2033-
}
2018+
minutes = extract_unit_32(&dts->sec, 60);
2019+
add_minutes_to_datetimestruct(dts, minutes);
20342020
}
20352021

20362022
/*
@@ -2042,28 +2028,13 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
20422028
{
20432029
int isleap;
20442030

2045-
/* MINUTES */
20462031
dts->min += minutes;
2047-
while (dts->min < 0) {
2048-
dts->min += 60;
2049-
dts->hour--;
2050-
}
2051-
while (dts->min >= 60) {
2052-
dts->min -= 60;
2053-
dts->hour++;
2054-
}
20552032

2056-
/* HOURS */
2057-
while (dts->hour < 0) {
2058-
dts->hour += 24;
2059-
dts->day--;
2060-
}
2061-
while (dts->hour >= 24) {
2062-
dts->hour -= 24;
2063-
dts->day++;
2064-
}
2033+
/* propagate invalid minutes into hour and day changes */
2034+
dts->hour += extract_unit_32(&dts->min, 60);
2035+
dts->day += extract_unit_32(&dts->hour, 24);
20652036

2066-
/* DAYS */
2037+
/* propagate invalid days into month and year changes */
20672038
if (dts->day < 1) {
20682039
dts->month--;
20692040
if (dts->month < 1) {
@@ -2890,7 +2861,6 @@ convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta)
28902861
NPY_NO_EXPORT PyObject *
28912862
convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
28922863
{
2893-
PyObject *ret = NULL;
28942864
npy_timedelta value;
28952865
int days = 0, seconds = 0, useconds = 0;
28962866

@@ -2920,54 +2890,47 @@ convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
29202890
/* Convert to days/seconds/useconds */
29212891
switch (meta->base) {
29222892
case NPY_FR_W:
2923-
value *= 7;
2893+
days = value * 7;
29242894
break;
29252895
case NPY_FR_D:
2896+
days = value;
29262897
break;
29272898
case NPY_FR_h:
2928-
seconds = (int)((value % 24) * (60*60));
2929-
value = value / 24;
2899+
days = extract_unit_64(&value, 24ULL);
2900+
seconds = value*60*60;
29302901
break;
29312902
case NPY_FR_m:
2932-
seconds = (int)(value % (24*60)) * 60;
2933-
value = value / (24*60);
2903+
days = extract_unit_64(&value, 60ULL*24);
2904+
seconds = value*60;
29342905
break;
29352906
case NPY_FR_s:
2936-
seconds = (int)(value % (24*60*60));
2937-
value = value / (24*60*60);
2907+
days = extract_unit_64(&value, 60ULL*60*24);
2908+
seconds = value;
29382909
break;
29392910
case NPY_FR_ms:
2940-
useconds = (int)(value % 1000) * 1000;
2941-
value = value / 1000;
2942-
seconds = (int)(value % (24*60*60));
2943-
value = value / (24*60*60);
2911+
days = extract_unit_64(&value, 1000ULL*60*60*24);
2912+
seconds = extract_unit_64(&value, 1000ULL);
2913+
useconds = value*1000;
29442914
break;
29452915
case NPY_FR_us:
2946-
useconds = (int)(value % (1000*1000));
2947-
value = value / (1000*1000);
2948-
seconds = (int)(value % (24*60*60));
2949-
value = value / (24*60*60);
2916+
days = extract_unit_64(&value, 1000ULL*1000*60*60*24);
2917+
seconds = extract_unit_64(&value, 1000ULL*1000);
2918+
useconds = value;
29502919
break;
29512920
default:
2921+
// unreachable, handled by the `if` above
2922+
assert(NPY_FALSE);
29522923
break;
29532924
}
29542925
/*
2955-
* 'value' represents days, and seconds/useconds are filled.
2956-
*
29572926
* If it would overflow the datetime.timedelta days, return a raw int
29582927
*/
2959-
if (value < -999999999 || value > 999999999) {
2928+
if (days < -999999999 || days > 999999999) {
29602929
return PyLong_FromLongLong(td);
29612930
}
29622931
else {
2963-
days = (int)value;
2964-
ret = PyDelta_FromDSU(days, seconds, useconds);
2965-
if (ret == NULL) {
2966-
return NULL;
2967-
}
2932+
return PyDelta_FromDSU(days, seconds, useconds);
29682933
}
2969-
2970-
return ret;
29712934
}
29722935

29732936
/*

0 commit comments

Comments
 (0)
0