1 /* ------------------------------------------------------------------------- */
2 
3 #include <Python.h>
4 #include <datetime.h>
5 #include <structmember.h>
6 #include <math.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 
12 /* ------------------------------------------------------------------------- */
13 
14 #define EPOCH_YEAR 1970
15 
16 #define DAYS_PER_N_YEAR 365
17 #define DAYS_PER_L_YEAR 366
18 
19 #define USECS_PER_SEC 1000000
20 
21 #define SECS_PER_MIN 60
22 #define SECS_PER_HOUR (60 * SECS_PER_MIN)
23 #define SECS_PER_DAY (SECS_PER_HOUR * 24)
24 
25 // 400-year chunks always have 146097 days (20871 weeks).
26 #define DAYS_PER_400_YEARS 146097L
27 #define SECS_PER_400_YEARS ((int64_t)DAYS_PER_400_YEARS * (int64_t)SECS_PER_DAY)
28 
29 // The number of seconds in an aligned 100-year chunk, for those that
30 // do not begin with a leap year and those that do respectively.
31 const int64_t SECS_PER_100_YEARS[2] = {
32     (uint64_t)(76L * DAYS_PER_N_YEAR + 24L * DAYS_PER_L_YEAR) * SECS_PER_DAY,
33     (uint64_t)(75L * DAYS_PER_N_YEAR + 25L * DAYS_PER_L_YEAR) * SECS_PER_DAY
34 };
35 
36 // The number of seconds in an aligned 4-year chunk, for those that
37 // do not begin with a leap year and those that do respectively.
38 const int32_t SECS_PER_4_YEARS[2] = {
39     (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
40     (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY
41 };
42 
43 // The number of seconds in non-leap and leap years respectively.
44 const int32_t SECS_PER_YEAR[2] = {
45     DAYS_PER_N_YEAR * SECS_PER_DAY,
46     DAYS_PER_L_YEAR * SECS_PER_DAY
47 };
48 
49 #define MONTHS_PER_YEAR 12
50 
51 // The month lengths in non-leap and leap years respectively.
52 const int32_t DAYS_PER_MONTHS[2][13] = {
53     {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
54     {-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
55 };
56 
57 // The day offsets of the beginning of each (1-based) month in non-leap
58 // and leap years respectively.
59 // For example, in a leap year there are 335 days before December.
60 const int32_t MONTHS_OFFSETS[2][14] = {
61     {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
62     {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
63 };
64 
65 const int DAY_OF_WEEK_TABLE[12] = {
66     0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
67 };
68 
69 #define TM_SUNDAY 0
70 #define TM_MONDAY 1
71 #define TM_TUESDAY 2
72 #define TM_WEDNESDAY 3
73 #define TM_THURSDAY 4
74 #define TM_FRIDAY 5
75 #define TM_SATURDAY 6
76 
77 #define TM_JANUARY 0
78 #define TM_FEBRUARY 1
79 #define TM_MARCH 2
80 #define TM_APRIL 3
81 #define TM_MAY 4
82 #define TM_JUNE 5
83 #define TM_JULY 6
84 #define TM_AUGUST 7
85 #define TM_SEPTEMBER 8
86 #define TM_OCTOBER 9
87 #define TM_NOVEMBER 10
88 #define TM_DECEMBER 11
89 
90 /* ------------------------------------------------------------------------- */
91 
_p(int y)92 int _p(int y) {
93     return y + y/4 - y/100 + y /400;
94 }
95 
_is_leap(int year)96 int _is_leap(int year) {
97     return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
98 }
99 
_is_long_year(int year)100 int _is_long_year(int year) {
101     return (_p(year) % 7 == 4) || (_p(year - 1) % 7 == 3);
102 }
103 
_week_day(int year,int month,int day)104 int _week_day(int year, int month, int day) {
105     int y;
106     int w;
107 
108     y = year - (month < 3);
109 
110     w = (_p(y) + DAY_OF_WEEK_TABLE[month - 1] + day) % 7;
111 
112     if (!w) {
113         w = 7;
114     }
115 
116     return w;
117 }
118 
_days_in_year(int year)119 int _days_in_year(int year) {
120     if (_is_leap(year)) {
121         return DAYS_PER_L_YEAR;
122     }
123 
124     return DAYS_PER_N_YEAR;
125 }
126 
_day_number(int year,int month,int day)127 int _day_number(int year, int month, int day) {
128     month = (month + 9) % 12;
129     year = year - month / 10;
130 
131     return (
132         365 * year
133         + year / 4 - year / 100 + year / 400
134         + (month * 306 + 5) / 10
135         + (day - 1)
136     );
137 }
138 
_get_offset(PyObject * dt)139 int _get_offset(PyObject *dt) {
140     PyObject *tzinfo;
141     PyObject *offset;
142 
143     tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo;
144 
145     if (tzinfo != Py_None) {
146         offset = PyObject_CallMethod(tzinfo, "utcoffset", "O", dt);
147 
148         return
149             PyDateTime_DELTA_GET_DAYS(offset) * SECS_PER_DAY
150             + PyDateTime_DELTA_GET_SECONDS(offset);
151     }
152 
153     return 0;
154 }
155 
_has_tzinfo(PyObject * dt)156 int _has_tzinfo(PyObject *dt) {
157     return ((_PyDateTime_BaseTZInfo *)(dt))->hastzinfo;
158 }
159 
_get_tz_name(PyObject * dt)160 char* _get_tz_name(PyObject *dt) {
161     PyObject *tzinfo;
162     char *tz = "";
163 
164     tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo;
165 
166     if (tzinfo != Py_None) {
167         if (PyObject_HasAttrString(tzinfo, "name")) {
168             // Pendulum timezone
169             tz = PyUnicode_AsUTF8(
170                 PyObject_GetAttrString(tzinfo, "name")
171             );
172         } else if (PyObject_HasAttrString(tzinfo, "zone")) {
173             // pytz timezone
174             tz = PyUnicode_AsUTF8(
175                 PyObject_GetAttrString(tzinfo, "zone")
176             );
177         }
178     }
179 
180     return tz;
181 }
182 
183 /* ------------------------ Custom Types ------------------------------- */
184 
185 /*
186  * class Diff():
187  */
188 typedef struct {
189     PyObject_HEAD
190     int years;
191     int months;
192     int days;
193     int hours;
194     int minutes;
195     int seconds;
196     int microseconds;
197     int total_days;
198 } Diff;
199 
200 /*
201  * def __init__(self, years, months, days, hours, minutes, seconds, microseconds, total_days):
202  *     self.years = years
203  *     # ...
204 */
Diff_init(Diff * self,PyObject * args,PyObject * kwargs)205 static int Diff_init(Diff *self, PyObject *args, PyObject *kwargs) {
206     int years;
207     int months;
208     int days;
209     int hours;
210     int minutes;
211     int seconds;
212     int microseconds;
213     int total_days;
214 
215     if (!PyArg_ParseTuple(args, "iiiiiii", &years, &months, &days, &hours, &minutes, &seconds, &microseconds, &total_days))
216         return -1;
217 
218     self->years = years;
219     self->months = months;
220     self->days = days;
221     self->hours = hours;
222     self->minutes = minutes;
223     self->seconds = seconds;
224     self->microseconds = microseconds;
225     self->total_days = total_days;
226 
227     return 0;
228 }
229 
230 /*
231  * def __repr__(self):
232  *     return '{} years {} months {} days {} hours {} minutes {} seconds {} microseconds'.format(
233  *         self.years, self.months, self.days, self.minutes, self.hours, self.seconds, self.microseconds
234  *     )
235  */
Diff_repr(Diff * self)236 static PyObject *Diff_repr(Diff *self) {
237     char repr[82] = {0};
238 
239     sprintf(
240         repr,
241         "%d years %d months %d days %d hours %d minutes %d seconds %d microseconds",
242         self->years,
243         self->months,
244         self->days,
245         self->hours,
246         self->minutes,
247         self->seconds,
248         self->microseconds
249     );
250 
251     return PyUnicode_FromString(repr);
252 }
253 
254 /*
255  * Instantiate new Diff_type object
256  * Skip overhead of calling PyObject_New and PyObject_Init.
257  * Directly allocate object.
258  */
new_diff_ex(int years,int months,int days,int hours,int minutes,int seconds,int microseconds,int total_days,PyTypeObject * type)259 static PyObject *new_diff_ex(int years, int months, int days, int hours, int minutes, int seconds, int microseconds, int total_days, PyTypeObject *type) {
260     Diff *self = (Diff *) (type->tp_alloc(type, 0));
261 
262     if (self != NULL) {
263         self->years = years;
264         self->months = months;
265         self->days = days;
266         self->hours = hours;
267         self->minutes = minutes;
268         self->seconds = seconds;
269         self->microseconds = microseconds;
270         self->total_days = total_days;
271     }
272 
273     return (PyObject *) self;
274 }
275 
276 /*
277  * Class member / class attributes
278  */
279 static PyMemberDef Diff_members[] = {
280     {"years", T_INT, offsetof(Diff, years), 0, "years in diff"},
281     {"months", T_INT, offsetof(Diff, months), 0, "months in diff"},
282     {"days", T_INT, offsetof(Diff, days), 0, "days in diff"},
283     {"hours", T_INT, offsetof(Diff, hours), 0, "hours in diff"},
284     {"minutes", T_INT, offsetof(Diff, minutes), 0, "minutes in diff"},
285     {"seconds", T_INT, offsetof(Diff, seconds), 0, "seconds in diff"},
286     {"microseconds", T_INT, offsetof(Diff, microseconds), 0, "microseconds in diff"},
287     {"total_days", T_INT, offsetof(Diff, total_days), 0, "total days in diff"},
288     {NULL}
289 };
290 
291 static PyTypeObject Diff_type = {
292     PyVarObject_HEAD_INIT(NULL, 0)
293     "PreciseDiff",                  /* tp_name */
294     sizeof(Diff),                           /* tp_basicsize */
295     0,                                      /* tp_itemsize */
296     0,                                      /* tp_dealloc */
297     0,                                      /* tp_print */
298     0,                                      /* tp_getattr */
299     0,                                      /* tp_setattr */
300     0,                                      /* tp_as_async */
301     (reprfunc)Diff_repr,                    /* tp_repr */
302     0,                                      /* tp_as_number */
303     0,                                      /* tp_as_sequence */
304     0,                                      /* tp_as_mapping */
305     0,                                      /* tp_hash  */
306     0,                                      /* tp_call */
307     (reprfunc)Diff_repr,                    /* tp_str */
308     0,                                      /* tp_getattro */
309     0,                                      /* tp_setattro */
310     0,                                      /* tp_as_buffer */
311     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
312     "Precise difference between two datetime objects",             /* tp_doc */
313 };
314 
315 #define new_diff(years, months, days, hours, minutes, seconds, microseconds, total_days) new_diff_ex(years, months, days, hours, minutes, seconds, microseconds, total_days, &Diff_type)
316 
317 
318 /* -------------------------- Functions --------------------------*/
319 
is_leap(PyObject * self,PyObject * args)320 PyObject* is_leap(PyObject *self, PyObject *args) {
321     PyObject *leap;
322     int year;
323 
324     if (!PyArg_ParseTuple(args, "i", &year)) {
325         PyErr_SetString(
326             PyExc_ValueError, "Invalid parameters"
327         );
328         return NULL;
329     }
330 
331     leap = PyBool_FromLong(_is_leap(year));
332 
333     return leap;
334 }
335 
is_long_year(PyObject * self,PyObject * args)336 PyObject* is_long_year(PyObject *self, PyObject *args) {
337     PyObject *is_long;
338     int year;
339 
340     if (!PyArg_ParseTuple(args, "i", &year)) {
341         PyErr_SetString(
342             PyExc_ValueError, "Invalid parameters"
343         );
344         return NULL;
345     }
346 
347     is_long = PyBool_FromLong(_is_long_year(year));
348 
349     return is_long;
350 }
351 
week_day(PyObject * self,PyObject * args)352 PyObject* week_day(PyObject *self, PyObject *args) {
353     PyObject *wd;
354     int year;
355     int month;
356     int day;
357 
358     if (!PyArg_ParseTuple(args, "iii", &year, &month, &day)) {
359         PyErr_SetString(
360             PyExc_ValueError, "Invalid parameters"
361         );
362         return NULL;
363     }
364 
365     wd = PyLong_FromLong(_week_day(year, month, day));
366 
367     return wd;
368 }
369 
days_in_year(PyObject * self,PyObject * args)370 PyObject* days_in_year(PyObject *self, PyObject *args) {
371     PyObject *ndays;
372     int year;
373 
374     if (!PyArg_ParseTuple(args, "i", &year)) {
375         PyErr_SetString(
376             PyExc_ValueError, "Invalid parameters"
377         );
378         return NULL;
379     }
380 
381     ndays = PyLong_FromLong(_days_in_year(year));
382 
383     return ndays;
384 }
385 
timestamp(PyObject * self,PyObject * args)386 PyObject* timestamp(PyObject *self, PyObject *args) {
387     int64_t result;
388     PyObject* dt;
389 
390     if (!PyArg_ParseTuple(args, "O", &dt)) {
391         PyErr_SetString(
392             PyExc_ValueError, "Invalid parameters"
393         );
394         return NULL;
395     }
396 
397     int year = (double) PyDateTime_GET_YEAR(dt);
398     int month = PyDateTime_GET_MONTH(dt);
399     int day = PyDateTime_GET_DAY(dt);
400     int hour = PyDateTime_DATE_GET_HOUR(dt);
401     int minute = PyDateTime_DATE_GET_MINUTE(dt);
402     int second = PyDateTime_DATE_GET_SECOND(dt);
403 
404     result = (year - 1970) * 365 + MONTHS_OFFSETS[0][month];
405     result += (int) floor((double) (year - 1968) / 4);
406     result -= (year - 1900) / 100;
407     result += (year - 1600) / 400;
408 
409     if (_is_leap(year) && month < 3) {
410         result -= 1;
411     }
412 
413     result += day - 1;
414     result *= 24;
415     result += hour;
416     result *= 60;
417     result += minute;
418     result *= 60;
419     result += second;
420 
421     return PyLong_FromSsize_t(result);
422 }
423 
local_time(PyObject * self,PyObject * args)424 PyObject* local_time(PyObject *self, PyObject *args) {
425     double unix_time;
426     int32_t utc_offset;
427     int32_t year;
428     int32_t microsecond;
429     int64_t seconds;
430     int32_t leap_year;
431     int64_t sec_per_100years;
432     int64_t sec_per_4years;
433     int32_t sec_per_year;
434     int32_t month;
435     int32_t day;
436     int32_t month_offset;
437     int32_t hour;
438     int32_t minute;
439     int32_t second;
440 
441     if (!PyArg_ParseTuple(args, "dii", &unix_time, &utc_offset, &microsecond)) {
442         PyErr_SetString(
443             PyExc_ValueError, "Invalid parameters"
444         );
445         return NULL;
446     }
447 
448     year = EPOCH_YEAR;
449     seconds = (int64_t) floor(unix_time);
450 
451     // Shift to a base year that is 400-year aligned.
452     if (seconds >= 0) {
453         seconds -= 10957L * SECS_PER_DAY;
454         year += 30;  // == 2000;
455     } else {
456         seconds += (int64_t)(146097L - 10957L) * SECS_PER_DAY;
457         year -= 370;  // == 1600;
458     }
459 
460     seconds += utc_offset;
461 
462     // Handle years in chunks of 400/100/4/1
463     year += 400 * (seconds / SECS_PER_400_YEARS);
464     seconds %= SECS_PER_400_YEARS;
465     if (seconds < 0) {
466         seconds += SECS_PER_400_YEARS;
467         year -= 400;
468     }
469 
470     leap_year = 1;  // 4-century aligned
471 
472     sec_per_100years = SECS_PER_100_YEARS[leap_year];
473 
474     while (seconds >= sec_per_100years) {
475         seconds -= sec_per_100years;
476         year += 100;
477         leap_year = 0;  // 1-century, non 4-century aligned
478         sec_per_100years = SECS_PER_100_YEARS[leap_year];
479     }
480 
481     sec_per_4years = SECS_PER_4_YEARS[leap_year];
482     while (seconds >= sec_per_4years) {
483         seconds -= sec_per_4years;
484         year += 4;
485         leap_year = 1;  // 4-year, non century aligned
486         sec_per_4years = SECS_PER_4_YEARS[leap_year];
487     }
488 
489     sec_per_year = SECS_PER_YEAR[leap_year];
490     while (seconds >= sec_per_year) {
491         seconds -= sec_per_year;
492         year += 1;
493         leap_year = 0;  // non 4-year aligned
494         sec_per_year = SECS_PER_YEAR[leap_year];
495     }
496 
497     // Handle months and days
498     month = TM_DECEMBER + 1;
499     day = seconds / SECS_PER_DAY + 1;
500     seconds %= SECS_PER_DAY;
501     while (month != TM_JANUARY + 1) {
502         month_offset = MONTHS_OFFSETS[leap_year][month];
503         if (day > month_offset) {
504             day -= month_offset;
505             break;
506         }
507 
508         month -= 1;
509     }
510 
511     // Handle hours, minutes and seconds
512     hour = seconds / SECS_PER_HOUR;
513     seconds %= SECS_PER_HOUR;
514     minute = seconds / SECS_PER_MIN;
515     second = seconds % SECS_PER_MIN;
516 
517     return Py_BuildValue("NNNNNNN",
518         PyLong_FromLong(year),
519         PyLong_FromLong(month),
520         PyLong_FromLong(day),
521         PyLong_FromLong(hour),
522         PyLong_FromLong(minute),
523         PyLong_FromLong(second),
524         PyLong_FromLong(microsecond)
525     );
526 }
527 
528 
529 // Calculate a precise difference between two datetimes.
precise_diff(PyObject * self,PyObject * args)530 PyObject* precise_diff(PyObject *self, PyObject *args) {
531     PyObject* dt1;
532     PyObject* dt2;
533 
534     if (!PyArg_ParseTuple(args, "OO", &dt1, &dt2)) {
535         PyErr_SetString(
536             PyExc_ValueError, "Invalid parameters"
537         );
538         return NULL;
539     }
540 
541     int year_diff = 0;
542     int month_diff = 0;
543     int day_diff = 0;
544     int hour_diff = 0;
545     int minute_diff = 0;
546     int second_diff = 0;
547     int microsecond_diff = 0;
548     int sign = 1;
549     int year;
550     int month;
551     int leap;
552     int days_in_last_month;
553     int days_in_month;
554     int dt1_year = PyDateTime_GET_YEAR(dt1);
555     int dt2_year = PyDateTime_GET_YEAR(dt2);
556     int dt1_month = PyDateTime_GET_MONTH(dt1);
557     int dt2_month = PyDateTime_GET_MONTH(dt2);
558     int dt1_day = PyDateTime_GET_DAY(dt1);
559     int dt2_day = PyDateTime_GET_DAY(dt2);
560     int dt1_hour = 0;
561     int dt2_hour = 0;
562     int dt1_minute = 0;
563     int dt2_minute = 0;
564     int dt1_second = 0;
565     int dt2_second = 0;
566     int dt1_microsecond = 0;
567     int dt2_microsecond = 0;
568     int dt1_total_seconds = 0;
569     int dt2_total_seconds = 0;
570     int dt1_offset = 0;
571     int dt2_offset = 0;
572     int dt1_is_datetime = PyDateTime_Check(dt1);
573     int dt2_is_datetime = PyDateTime_Check(dt2);
574     char *tz1 = "";
575     char *tz2 = "";
576     int in_same_tz = 0;
577     int total_days = (
578         _day_number(dt2_year, dt2_month, dt2_day)
579         - _day_number(dt1_year, dt1_month, dt1_day)
580     );
581 
582     // If both dates are datetimes, we check
583     // If we are in the same timezone
584     if (dt1_is_datetime && dt2_is_datetime) {
585         if (_has_tzinfo(dt1)) {
586             tz1 = _get_tz_name(dt1);
587             dt1_offset = _get_offset(dt1);
588         }
589 
590         if (_has_tzinfo(dt2)) {
591             tz2 = _get_tz_name(dt2);
592             dt2_offset = _get_offset(dt2);
593         }
594 
595         in_same_tz = tz1 == tz2 && strncmp(tz1, "", 1);
596     }
597 
598     // If we have datetimes (and not only dates)
599     // we get the information we need
600     if (dt1_is_datetime) {
601         dt1_hour = PyDateTime_DATE_GET_HOUR(dt1);
602         dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1);
603         dt1_second = PyDateTime_DATE_GET_SECOND(dt1);
604         dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1);
605 
606         if ((!in_same_tz && dt1_offset != 0) || total_days == 0) {
607             dt1_hour -= dt1_offset / SECS_PER_HOUR;
608             dt1_offset %= SECS_PER_HOUR;
609             dt1_minute -= dt1_offset / SECS_PER_MIN;
610             dt1_offset %= SECS_PER_MIN;
611             dt1_second -= dt1_offset;
612 
613             if (dt1_second < 0) {
614                 dt1_second += 60;
615                 dt1_minute -= 1;
616             } else if (dt1_second > 60) {
617                 dt1_second -= 60;
618                 dt1_minute += 1;
619             }
620 
621             if (dt1_minute < 0) {
622                 dt1_minute += 60;
623                 dt1_hour -= 1;
624             } else if (dt1_minute > 60) {
625                 dt1_minute -= 60;
626                 dt1_hour += 1;
627             }
628 
629             if (dt1_hour < 0) {
630                 dt1_hour += 24;
631                 dt1_day -= 1;
632             } else if (dt1_hour > 24) {
633                 dt1_hour -= 24;
634                 dt1_day += 1;
635             }
636         }
637 
638         dt1_total_seconds = (
639             dt1_hour * SECS_PER_HOUR
640             + dt1_minute * SECS_PER_MIN
641             + dt1_second
642         );
643     }
644 
645     if (dt2_is_datetime) {
646         dt2_hour = PyDateTime_DATE_GET_HOUR(dt2);
647         dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2);
648         dt2_second = PyDateTime_DATE_GET_SECOND(dt2);
649         dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2);
650 
651         if ((!in_same_tz && dt2_offset != 0) || total_days == 0) {
652             dt2_hour -= dt2_offset / SECS_PER_HOUR;
653             dt2_offset %= SECS_PER_HOUR;
654             dt2_minute -= dt2_offset / SECS_PER_MIN;
655             dt2_offset %= SECS_PER_MIN;
656             dt2_second -= dt2_offset;
657 
658             if (dt2_second < 0) {
659                 dt2_second += 60;
660                 dt2_minute -= 1;
661             } else if (dt2_second > 60) {
662                 dt2_second -= 60;
663                 dt2_minute += 1;
664             }
665 
666             if (dt2_minute < 0) {
667                 dt2_minute += 60;
668                 dt2_hour -= 1;
669             } else if (dt2_minute > 60) {
670                 dt2_minute -= 60;
671                 dt2_hour += 1;
672             }
673 
674             if (dt2_hour < 0) {
675                 dt2_hour += 24;
676                 dt2_day -= 1;
677             } else if (dt2_hour > 24) {
678                 dt2_hour -= 24;
679                 dt2_day += 1;
680             }
681         }
682 
683         dt2_total_seconds = (
684             dt2_hour * SECS_PER_HOUR
685             + dt2_minute * SECS_PER_MIN
686             + dt2_second
687         );
688     }
689 
690     // Direct comparison between two datetimes does not work
691     // so we need to check by properties
692     int dt1_gt_dt2 = (
693         dt1_year > dt2_year
694         || (dt1_year == dt2_year && dt1_month > dt2_month)
695         || (
696             dt1_year == dt2_year
697             && dt1_month == dt2_month
698             && dt1_day > dt2_day
699         )
700         || (
701             dt1_year == dt2_year
702             && dt1_month == dt2_month
703             && dt1_day == dt2_day
704             && dt1_total_seconds > dt2_total_seconds
705         )
706         || (
707             dt1_year == dt2_year
708             && dt1_month == dt2_month
709             && dt1_day == dt2_day
710             && dt1_total_seconds == dt2_total_seconds
711             && dt1_microsecond > dt2_microsecond
712         )
713     );
714 
715     if (dt1_gt_dt2) {
716         PyObject* temp;
717         temp = dt1;
718         dt1 = dt2;
719         dt2 = temp;
720         sign = -1;
721 
722         // Retrieving properties
723         dt1_year = PyDateTime_GET_YEAR(dt1);
724         dt2_year = PyDateTime_GET_YEAR(dt2);
725         dt1_month = PyDateTime_GET_MONTH(dt1);
726         dt2_month = PyDateTime_GET_MONTH(dt2);
727         dt1_day = PyDateTime_GET_DAY(dt1);
728         dt2_day = PyDateTime_GET_DAY(dt2);
729 
730         if (dt2_is_datetime) {
731             dt1_hour = PyDateTime_DATE_GET_HOUR(dt1);
732             dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1);
733             dt1_second = PyDateTime_DATE_GET_SECOND(dt1);
734             dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1);
735         }
736 
737         if (dt1_is_datetime) {
738             dt2_hour = PyDateTime_DATE_GET_HOUR(dt2);
739             dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2);
740             dt2_second = PyDateTime_DATE_GET_SECOND(dt2);
741             dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2);
742         }
743 
744         total_days = (
745             _day_number(dt2_year, dt2_month, dt2_day)
746             - _day_number(dt1_year, dt1_month, dt1_day)
747         );
748     }
749 
750     year_diff = dt2_year - dt1_year;
751     month_diff = dt2_month - dt1_month;
752     day_diff = dt2_day - dt1_day;
753     hour_diff = dt2_hour - dt1_hour;
754     minute_diff = dt2_minute - dt1_minute;
755     second_diff = dt2_second - dt1_second;
756     microsecond_diff = dt2_microsecond - dt1_microsecond;
757 
758     if (microsecond_diff < 0) {
759         microsecond_diff += 1e6;
760         second_diff -= 1;
761     }
762 
763     if (second_diff < 0) {
764         second_diff += 60;
765         minute_diff -= 1;
766     }
767 
768     if (minute_diff < 0) {
769         minute_diff += 60;
770         hour_diff -= 1;
771     }
772 
773     if (hour_diff < 0) {
774         hour_diff += 24;
775         day_diff -= 1;
776     }
777 
778     if (day_diff < 0) {
779         // If we have a difference in days,
780         // we have to check if they represent months
781         year = dt2_year;
782         month = dt2_month;
783 
784         if (month == 1) {
785             month = 12;
786             year -= 1;
787         } else {
788             month -= 1;
789         }
790 
791         leap = _is_leap(year);
792 
793         days_in_last_month = DAYS_PER_MONTHS[leap][month];
794         days_in_month = DAYS_PER_MONTHS[_is_leap(dt2_year)][dt2_month];
795 
796         if (day_diff < days_in_month - days_in_last_month) {
797             // We don't have a full month, we calculate days
798             if (days_in_last_month < dt1_day) {
799                 day_diff += dt1_day;
800             } else {
801                 day_diff += days_in_last_month;
802             }
803         } else if (day_diff == days_in_month - days_in_last_month) {
804             // We have exactly a full month
805             // We remove the days difference
806             // and add one to the months difference
807             day_diff = 0;
808             month_diff += 1;
809         } else {
810             // We have a full month
811             day_diff += days_in_last_month;
812         }
813 
814         month_diff -= 1;
815     }
816 
817     if (month_diff < 0) {
818         month_diff += 12;
819         year_diff -= 1;
820     }
821 
822     return new_diff(
823         year_diff * sign,
824         month_diff * sign,
825         day_diff * sign,
826         hour_diff * sign,
827         minute_diff * sign,
828         second_diff * sign,
829         microsecond_diff * sign,
830         total_days * sign
831     );
832 }
833 
834 /* ------------------------------------------------------------------------- */
835 
836 static PyMethodDef helpers_methods[] = {
837     {
838         "is_leap",
839         (PyCFunction) is_leap,
840         METH_VARARGS,
841         PyDoc_STR("Checks if a year is a leap year.")
842     },
843     {
844         "is_long_year",
845         (PyCFunction) is_long_year,
846         METH_VARARGS,
847         PyDoc_STR("Checks if a year is a long year.")
848     },
849     {
850         "week_day",
851         (PyCFunction) week_day,
852         METH_VARARGS,
853         PyDoc_STR("Returns the weekday number.")
854     },
855     {
856         "days_in_year",
857         (PyCFunction) days_in_year,
858         METH_VARARGS,
859         PyDoc_STR("Returns the number of days in the given year.")
860     },
861     {
862         "timestamp",
863         (PyCFunction) timestamp,
864         METH_VARARGS,
865         PyDoc_STR("Returns the timestamp of the given datetime.")
866     },
867     {
868         "local_time",
869         (PyCFunction) local_time,
870         METH_VARARGS,
871         PyDoc_STR("Returns a UNIX time as a broken down time for a particular transition type.")
872     },
873     {
874         "precise_diff",
875         (PyCFunction) precise_diff,
876         METH_VARARGS,
877         PyDoc_STR("Calculate a precise difference between two datetimes.")
878     },
879     {NULL}
880 };
881 
882 /* ------------------------------------------------------------------------- */
883 
884 static struct PyModuleDef moduledef = {
885     PyModuleDef_HEAD_INIT,
886     "_helpers",
887     NULL,
888     -1,
889     helpers_methods,
890     NULL,
891     NULL,
892     NULL,
893     NULL,
894 };
895 
896 PyMODINIT_FUNC
PyInit__helpers(void)897 PyInit__helpers(void)
898 {
899     PyObject *module;
900 
901     PyDateTime_IMPORT;
902 
903     module = PyModule_Create(&moduledef);
904 
905     if (module == NULL)
906         return NULL;
907 
908     // Diff declaration
909     Diff_type.tp_new = PyType_GenericNew;
910     Diff_type.tp_members = Diff_members;
911     Diff_type.tp_init = (initproc)Diff_init;
912 
913     if (PyType_Ready(&Diff_type) < 0)
914         return NULL;
915 
916     PyModule_AddObject(module, "PreciseDiff", (PyObject *)&Diff_type);
917 
918     return module;
919 }
920