1
/*
2
 * This file implements core functionality for NumPy datetime.
3
 *
4
 * Written by Mark Wiebe (mwwiebe@gmail.com)
5
 * Copyright (c) 2011 by Enthought, Inc.
6
 *
7
 * See LICENSE.txt for the license.
8
 */
9

10
#define PY_SSIZE_T_CLEAN
11
#include <Python.h>
12
#include <datetime.h>
13

14
#include <time.h>
15

16
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
17
#define _MULTIARRAYMODULE
18
#include <numpy/arrayobject.h>
19

20
#include "npy_config.h"
21
#include "npy_pycompat.h"
22

23
#include "common.h"
24
#include "numpy/arrayscalars.h"
25
#include "_datetime.h"
26
#include "datetime_strings.h"
27
#include "convert_datatype.h"
28

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 1
    npy_int64 div = *d / unit;
39 1
    npy_int64 mod = *d % unit;
40 1
    if (mod < 0) {
41 1
        mod += unit;
42 1
        div -= 1;
43
    }
44
    assert(mod >= 0);
45 1
    *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 1
    npy_int32 div = *d / unit;
53 1
    npy_int32 mod = *d % unit;
54 1
    if (mod < 0) {
55 1
        mod += unit;
56 1
        div -= 1;
57
    }
58
    assert(mod >= 0);
59 1
    *d = mod;
60
    return div;
61
}
62

63
/*
64
 * Imports the PyDateTime functions so we can create these objects.
65
 * This is called during module initialization
66
 */
67
NPY_NO_EXPORT void
68 1
numpy_pydatetime_import(void)
69
{
70 1
    PyDateTime_IMPORT;
71
}
72

73
/* Exported as DATETIMEUNITS in multiarraymodule.c */
74
NPY_NO_EXPORT char const *_datetime_strings[NPY_DATETIME_NUMUNITS] = {
75
    "Y",
76
    "M",
77
    "W",
78
    "<invalid>",
79
    "D",
80
    "h",
81
    "m",
82
    "s",
83
    "ms",
84
    "us",
85
    "ns",
86
    "ps",
87
    "fs",
88
    "as",
89
    "generic"
90
};
91

92
/* Days per month, regular year and leap year */
93
NPY_NO_EXPORT int _days_per_month_table[2][12] = {
94
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
95
    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
96
};
97

98
/*
99
 * Returns 1 if the given year is a leap year, 0 otherwise.
100
 */
101
NPY_NO_EXPORT int
102 1
is_leapyear(npy_int64 year)
103
{
104 1
    return (year & 0x3) == 0 && /* year % 4 == 0 */
105 1
           ((year % 100) != 0 ||
106 1
            (year % 400) == 0);
107
}
108

109
/*
110
 * Calculates the days offset from the 1970 epoch.
111
 */
112
NPY_NO_EXPORT npy_int64
113 1
get_datetimestruct_days(const npy_datetimestruct *dts)
114
{
115
    int i, month;
116 1
    npy_int64 year, days = 0;
117
    int *month_lengths;
118

119 1
    year = dts->year - 1970;
120 1
    days = year * 365;
121

122
    /* Adjust for leap years */
123 1
    if (days >= 0) {
124
        /*
125
         * 1968 is the closest leap year before 1970.
126
         * Exclude the current year, so add 1.
127
         */
128 1
        year += 1;
129
        /* Add one day for each 4 years */
130 1
        days += year / 4;
131
        /* 1900 is the closest previous year divisible by 100 */
132 1
        year += 68;
133
        /* Subtract one day for each 100 years */
134 1
        days -= year / 100;
135
        /* 1600 is the closest previous year divisible by 400 */
136 1
        year += 300;
137
        /* Add one day for each 400 years */
138 1
        days += year / 400;
139
    }
140
    else {
141
        /*
142
         * 1972 is the closest later year after 1970.
143
         * Include the current year, so subtract 2.
144
         */
145 1
        year -= 2;
146
        /* Subtract one day for each 4 years */
147 1
        days += year / 4;
148
        /* 2000 is the closest later year divisible by 100 */
149 1
        year -= 28;
150
        /* Add one day for each 100 years */
151 1
        days -= year / 100;
152
        /* 2000 is also the closest later year divisible by 400 */
153
        /* Subtract one day for each 400 years */
154 1
        days += year / 400;
155
    }
156

157 1
    month_lengths = _days_per_month_table[is_leapyear(dts->year)];
158 1
    month = dts->month - 1;
159

160
    /* Add the months */
161 1
    for (i = 0; i < month; ++i) {
162 1
        days += month_lengths[i];
163
    }
164

165
    /* Add the days */
166 1
    days += dts->day - 1;
167

168 1
    return days;
169
}
170

171
/*
172
 * Calculates the minutes offset from the 1970 epoch.
173
 */
174
NPY_NO_EXPORT npy_int64
175 0
get_datetimestruct_minutes(const npy_datetimestruct *dts)
176
{
177 1
    npy_int64 days = get_datetimestruct_days(dts) * 24 * 60;
178 1
    days += dts->hour * 60;
179 1
    days += dts->min;
180

181 0
    return days;
182
}
183

184
/*
185
 * Modifies '*days_' to be the day offset within the year,
186
 * and returns the year.
187
 */
188
static npy_int64
189 1
days_to_yearsdays(npy_int64 *days_)
190
{
191 1
    const npy_int64 days_per_400years = (400*365 + 100 - 4 + 1);
192
    /* Adjust so it's relative to the year 2000 (divisible by 400) */
193 1
    npy_int64 days = (*days_) - (365*30 + 7);
194
    npy_int64 year;
195

196
    /* Break down the 400 year cycle to get the year and day within the year */
197 1
    year = 400 * extract_unit_64(&days, days_per_400years);
198

199
    /* Work out the year/day within the 400 year cycle */
200 1
    if (days >= 366) {
201 1
        year += 100 * ((days-1) / (100*365 + 25 - 1));
202 1
        days = (days-1) % (100*365 + 25 - 1);
203 1
        if (days >= 365) {
204 1
            year += 4 * ((days+1) / (4*365 + 1));
205 1
            days = (days+1) % (4*365 + 1);
206 1
            if (days >= 366) {
207 1
                year += (days-1) / 365;
208 1
                days = (days-1) % 365;
209
            }
210
        }
211
    }
212

213 1
    *days_ = days;
214 1
    return year + 2000;
215
}
216

217
/* Extracts the month number from a 'datetime64[D]' value */
218
NPY_NO_EXPORT int
219 1
days_to_month_number(npy_datetime days)
220
{
221
    npy_int64 year;
222
    int *month_lengths, i;
223

224 1
    year = days_to_yearsdays(&days);
225 1
    month_lengths = _days_per_month_table[is_leapyear(year)];
226

227 1
    for (i = 0; i < 12; ++i) {
228 1
        if (days < month_lengths[i]) {
229 1
            return i + 1;
230
        }
231
        else {
232 1
            days -= month_lengths[i];
233
        }
234
    }
235

236
    /* Should never get here */
237
    return 1;
238
}
239

240
/*
241
 * Fills in the year, month, day in 'dts' based on the days
242
 * offset from 1970.
243
 */
244
static void
245 1
set_datetimestruct_days(npy_int64 days, npy_datetimestruct *dts)
246
{
247
    int *month_lengths, i;
248

249 1
    dts->year = days_to_yearsdays(&days);
250 1
    month_lengths = _days_per_month_table[is_leapyear(dts->year)];
251

252 1
    for (i = 0; i < 12; ++i) {
253 1
        if (days < month_lengths[i]) {
254 1
            dts->month = i + 1;
255 1
            dts->day = (int)days + 1;
256 1
            return;
257
        }
258
        else {
259 1
            days -= month_lengths[i];
260
        }
261
    }
262
}
263

264
/*
265
 * Converts a datetime from a datetimestruct to a datetime based
266
 * on some metadata. The date is assumed to be valid.
267
 *
268
 * TODO: If meta->num is really big, there could be overflow
269
 *
270
 * Returns 0 on success, -1 on failure.
271
 */
272
NPY_NO_EXPORT int
273 1
convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta,
274
                                    const npy_datetimestruct *dts,
275
                                    npy_datetime *out)
276
{
277
    npy_datetime ret;
278 1
    NPY_DATETIMEUNIT base = meta->base;
279

280
    /* If the datetimestruct is NaT, return NaT */
281 1
    if (dts->year == NPY_DATETIME_NAT) {
282 1
        *out = NPY_DATETIME_NAT;
283 1
        return 0;
284
    }
285

286
    /* Cannot instantiate a datetime with generic units */
287 1
    if (meta->base == NPY_FR_GENERIC) {
288 0
        PyErr_SetString(PyExc_ValueError,
289
                    "Cannot create a NumPy datetime other than NaT "
290
                    "with generic units");
291 0
        return -1;
292
    }
293

294 1
    if (base == NPY_FR_Y) {
295
        /* Truncate to the year */
296 1
        ret = dts->year - 1970;
297
    }
298 1
    else if (base == NPY_FR_M) {
299
        /* Truncate to the month */
300 1
        ret = 12 * (dts->year - 1970) + (dts->month - 1);
301
    }
302
    else {
303
        /* Otherwise calculate the number of days to start */
304 1
        npy_int64 days = get_datetimestruct_days(dts);
305

306 1
        switch (base) {
307 1
            case NPY_FR_W:
308
                /* Truncate to weeks */
309 1
                if (days >= 0) {
310 1
                    ret = days / 7;
311
                }
312
                else {
313 1
                    ret = (days - 6) / 7;
314
                }
315
                break;
316
            case NPY_FR_D:
317
                ret = days;
318
                break;
319 1
            case NPY_FR_h:
320 1
                ret = days * 24 +
321 1
                      dts->hour;
322 1
                break;
323 1
            case NPY_FR_m:
324 1
                ret = (days * 24 +
325 1
                      dts->hour) * 60 +
326 1
                      dts->min;
327 1
                break;
328 1
            case NPY_FR_s:
329 1
                ret = ((days * 24 +
330 1
                      dts->hour) * 60 +
331 1
                      dts->min) * 60 +
332 1
                      dts->sec;
333 1
                break;
334 1
            case NPY_FR_ms:
335 1
                ret = (((days * 24 +
336 1
                      dts->hour) * 60 +
337 1
                      dts->min) * 60 +
338 1
                      dts->sec) * 1000 +
339 1
                      dts->us / 1000;
340 1
                break;
341 1
            case NPY_FR_us:
342 1
                ret = (((days * 24 +
343 1
                      dts->hour) * 60 +
344 1
                      dts->min) * 60 +
345 1
                      dts->sec) * 1000000 +
346 1
                      dts->us;
347 1
                break;
348 1
            case NPY_FR_ns:
349 1
                ret = ((((days * 24 +
350 1
                      dts->hour) * 60 +
351 1
                      dts->min) * 60 +
352 1
                      dts->sec) * 1000000 +
353 1
                      dts->us) * 1000 +
354 1
                      dts->ps / 1000;
355 1
                break;
356 1
            case NPY_FR_ps:
357 1
                ret = ((((days * 24 +
358 1
                      dts->hour) * 60 +
359 1
                      dts->min) * 60 +
360 1
                      dts->sec) * 1000000 +
361 1
                      dts->us) * 1000000 +
362 1
                      dts->ps;
363 1
                break;
364 1
            case NPY_FR_fs:
365
                /* only 2.6 hours */
366 1
                ret = (((((days * 24 +
367 1
                      dts->hour) * 60 +
368 1
                      dts->min) * 60 +
369 1
                      dts->sec) * 1000000 +
370 1
                      dts->us) * 1000000 +
371 1
                      dts->ps) * 1000 +
372 1
                      dts->as / 1000;
373 1
                break;
374 1
            case NPY_FR_as:
375
                /* only 9.2 secs */
376 1
                ret = (((((days * 24 +
377 1
                      dts->hour) * 60 +
378 1
                      dts->min) * 60 +
379 1
                      dts->sec) * 1000000 +
380 1
                      dts->us) * 1000000 +
381 1
                      dts->ps) * 1000000 +
382 1
                      dts->as;
383 1
                break;
384 0
            default:
385
                /* Something got corrupted */
386 0
                PyErr_SetString(PyExc_ValueError,
387
                        "NumPy datetime metadata with corrupt unit value");
388 0
                return -1;
389
        }
390
    }
391

392
    /* Divide by the multiplier */
393 1
    if (meta->num > 1) {
394 1
        if (ret >= 0) {
395 1
            ret /= meta->num;
396
        }
397
        else {
398 1
            ret = (ret - meta->num + 1) / meta->num;
399
        }
400
    }
401

402 1
    *out = ret;
403

404 1
    return 0;
405
}
406

407
/*NUMPY_API
408
 * Create a datetime value from a filled datetime struct and resolution unit.
409
 *
410
 * TO BE REMOVED - NOT USED INTERNALLY.
411
 */
412
NPY_NO_EXPORT npy_datetime
413 0
PyArray_DatetimeStructToDatetime(
414
        NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_datetimestruct *NPY_UNUSED(d))
415
{
416 0
    PyErr_SetString(PyExc_RuntimeError,
417
            "The NumPy PyArray_DatetimeStructToDatetime function has "
418
            "been removed");
419 0
    return -1;
420
}
421

422
/*NUMPY_API
423
 * Create a timdelta value from a filled timedelta struct and resolution unit.
424
 *
425
 * TO BE REMOVED - NOT USED INTERNALLY.
426
 */
427
NPY_NO_EXPORT npy_datetime
428 0
PyArray_TimedeltaStructToTimedelta(
429
        NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_timedeltastruct *NPY_UNUSED(d))
430
{
431 0
    PyErr_SetString(PyExc_RuntimeError,
432
            "The NumPy PyArray_TimedeltaStructToTimedelta function has "
433
            "been removed");
434 0
    return -1;
435
}
436

437
/*
438
 * Converts a datetime based on the given metadata into a datetimestruct
439
 */
440
NPY_NO_EXPORT int
441 1
convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
442
                                    npy_datetime dt,
443
                                    npy_datetimestruct *out)
444
{
445
    npy_int64 days;
446

447
    /* Initialize the output to all zeros */
448 1
    memset(out, 0, sizeof(npy_datetimestruct));
449 1
    out->year = 1970;
450 1
    out->month = 1;
451 1
    out->day = 1;
452

453
    /* NaT is signaled in the year */
454 1
    if (dt == NPY_DATETIME_NAT) {
455 1
        out->year = NPY_DATETIME_NAT;
456 1
        return 0;
457
    }
458

459
    /* Datetimes can't be in generic units */
460 1
    if (meta->base == NPY_FR_GENERIC) {
461 1
        PyErr_SetString(PyExc_ValueError,
462
                    "Cannot convert a NumPy datetime value other than NaT "
463
                    "with generic units");
464 1
        return -1;
465
    }
466

467
    /* TODO: Change to a mechanism that avoids the potential overflow */
468 1
    dt *= meta->num;
469

470
    /*
471
     * Note that care must be taken with the / and % operators
472
     * for negative values.
473
     */
474 1
    switch (meta->base) {
475 1
        case NPY_FR_Y:
476 1
            out->year = 1970 + dt;
477 1
            break;
478

479 1
        case NPY_FR_M:
480 1
            out->year  = 1970 + extract_unit_64(&dt, 12);
481 1
            out->month = dt + 1;
482 1
            break;
483

484 1
        case NPY_FR_W:
485
            /* A week is 7 days */
486 1
            set_datetimestruct_days(dt * 7, out);
487 1
            break;
488

489 1
        case NPY_FR_D:
490 1
            set_datetimestruct_days(dt, out);
491 1
            break;
492

493 1
        case NPY_FR_h:
494 1
            days      = extract_unit_64(&dt, 24LL);
495 1
            set_datetimestruct_days(days, out);
496 1
            out->hour = (int)dt;
497 1
            break;
498

499 1
        case NPY_FR_m:
500 1
            days      =      extract_unit_64(&dt, 60LL*24);
501 1
            set_datetimestruct_days(days, out);
502 1
            out->hour = (int)extract_unit_64(&dt, 60LL);
503 1
            out->min  = (int)dt;
504 1
            break;
505

506 1
        case NPY_FR_s:
507 1
            days      =      extract_unit_64(&dt, 60LL*60*24);
508 1
            set_datetimestruct_days(days, out);
509 1
            out->hour = (int)extract_unit_64(&dt, 60LL*60);
510 1
            out->min  = (int)extract_unit_64(&dt, 60LL);
511 1
            out->sec  = (int)dt;
512 1
            break;
513

514 1
        case NPY_FR_ms:
515 1
            days      =      extract_unit_64(&dt, 1000LL*60*60*24);
516 1
            set_datetimestruct_days(days, out);
517 1
            out->hour = (int)extract_unit_64(&dt, 1000LL*60*60);
518 1
            out->min  = (int)extract_unit_64(&dt, 1000LL*60);
519 1
            out->sec  = (int)extract_unit_64(&dt, 1000LL);
520 1
            out->us   = (int)(dt * 1000);
521 1
            break;
522

523 1
        case NPY_FR_us:
524 1
            days      =      extract_unit_64(&dt, 1000LL*1000*60*60*24);
525 1
            set_datetimestruct_days(days, out);
526 1
            out->hour = (int)extract_unit_64(&dt, 1000LL*1000*60*60);
527 1
            out->min  = (int)extract_unit_64(&dt, 1000LL*1000*60);
528 1
            out->sec  = (int)extract_unit_64(&dt, 1000LL*1000);
529 1
            out->us   = (int)dt;
530 1
            break;
531

532 1
        case NPY_FR_ns:
533 1
            days      =      extract_unit_64(&dt, 1000LL*1000*1000*60*60*24);
534 1
            set_datetimestruct_days(days, out);
535 1
            out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*60*60);
536 1
            out->min  = (int)extract_unit_64(&dt, 1000LL*1000*1000*60);
537 1
            out->sec  = (int)extract_unit_64(&dt, 1000LL*1000*1000);
538 1
            out->us   = (int)extract_unit_64(&dt, 1000LL);
539 1
            out->ps   = (int)(dt * 1000);
540 1
            break;
541

542 1
        case NPY_FR_ps:
543 1
            days      =      extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60*24);
544 1
            set_datetimestruct_days(days, out);
545 1
            out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60);
546 1
            out->min  = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60);
547 1
            out->sec  = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
548 1
            out->us   = (int)extract_unit_64(&dt, 1000LL*1000);
549 1
            out->ps   = (int)(dt);
550 1
            break;
551

552 1
        case NPY_FR_fs:
553
            /* entire range is only +- 2.6 hours */
554 1
            out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60*60);
555 1
            if (out->hour < 0) {
556 1
                out->year  = 1969;
557 1
                out->month = 12;
558 1
                out->day   = 31;
559 1
                out->hour  += 24;
560
                assert(out->hour >= 0);
561
            }
562 1
            out->min  = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60);
563 1
            out->sec  = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000);
564 1
            out->us   = (int)extract_unit_64(&dt, 1000LL*1000*1000);
565 1
            out->ps   = (int)extract_unit_64(&dt, 1000LL);
566 1
            out->as   = (int)(dt * 1000);
567 1
            break;
568

569 1
        case NPY_FR_as:
570
            /* entire range is only +- 9.2 seconds */
571 1
            out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*1000);
572 1
            if (out->sec < 0) {
573 1
                out->year  = 1969;
574 1
                out->month = 12;
575 1
                out->day   = 31;
576 1
                out->hour  = 23;
577 1
                out->min   = 59;
578 1
                out->sec   += 60;
579
                assert(out->sec >= 0);
580
            }
581 1
            out->us   = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
582 1
            out->ps   = (int)extract_unit_64(&dt, 1000LL*1000);
583 1
            out->as   = (int)dt;
584 1
            break;
585

586 0
        default:
587 0
            PyErr_SetString(PyExc_RuntimeError,
588
                        "NumPy datetime metadata is corrupted with invalid "
589
                        "base unit");
590 0
            return -1;
591
    }
592

593
    return 0;
594
}
595

596

597
/*NUMPY_API
598
 * Fill the datetime struct from the value and resolution unit.
599
 *
600
 * TO BE REMOVED - NOT USED INTERNALLY.
601
 */
602
NPY_NO_EXPORT void
603 0
PyArray_DatetimeToDatetimeStruct(
604
        npy_datetime NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr),
605
        npy_datetimestruct *result)
606
{
607 0
    PyErr_SetString(PyExc_RuntimeError,
608
            "The NumPy PyArray_DatetimeToDatetimeStruct function has "
609
            "been removed");
610 0
    memset(result, -1, sizeof(npy_datetimestruct));
611
}
612

613
/*
614
 * FIXME: Overflow is not handled at all
615
 *   To convert from Years or Months,
616
 *   multiplication by the average is done
617
 */
618

619
/*NUMPY_API
620
 * Fill the timedelta struct from the timedelta value and resolution unit.
621
 *
622
 * TO BE REMOVED - NOT USED INTERNALLY.
623
 */
624
NPY_NO_EXPORT void
625 0
PyArray_TimedeltaToTimedeltaStruct(
626
        npy_timedelta NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr),
627
        npy_timedeltastruct *result)
628
{
629 0
    PyErr_SetString(PyExc_RuntimeError,
630
            "The NumPy PyArray_TimedeltaToTimedeltaStruct function has "
631
            "been removed");
632 0
    memset(result, -1, sizeof(npy_timedeltastruct));
633
}
634

635
/*
636
 * Creates a datetime or timedelta dtype using a copy of the provided metadata.
637
 */
638
NPY_NO_EXPORT PyArray_Descr *
639 1
create_datetime_dtype(int type_num, PyArray_DatetimeMetaData *meta)
640
{
641 1
    PyArray_Descr *dtype = NULL;
642
    PyArray_DatetimeMetaData *dt_data;
643

644
    /* Create a default datetime or timedelta */
645 1
    if (type_num == NPY_DATETIME || type_num == NPY_TIMEDELTA) {
646 1
        dtype = PyArray_DescrNewFromType(type_num);
647
    }
648
    else {
649 0
        PyErr_SetString(PyExc_RuntimeError,
650
                "Asked to create a datetime type with a non-datetime "
651
                "type number");
652 0
        return NULL;
653
    }
654

655 1
    if (dtype == NULL) {
656
        return NULL;
657
    }
658

659 1
    dt_data = &(((PyArray_DatetimeDTypeMetaData *)dtype->c_metadata)->meta);
660

661
    /* Copy the metadata */
662 1
    *dt_data = *meta;
663

664 1
    return dtype;
665
}
666

667
/*
668
 * Creates a datetime or timedelta dtype using the given unit.
669
 */
670
NPY_NO_EXPORT PyArray_Descr *
671 1
create_datetime_dtype_with_unit(int type_num, NPY_DATETIMEUNIT unit)
672
{
673
    PyArray_DatetimeMetaData meta;
674 1
    meta.base = unit;
675 1
    meta.num = 1;
676 1
    return create_datetime_dtype(type_num, &meta);
677
}
678

679
/*
680
 * This function returns a pointer to the DateTimeMetaData
681
 * contained within the provided datetime dtype.
682
 */
683
NPY_NO_EXPORT PyArray_DatetimeMetaData *
684 1
get_datetime_metadata_from_dtype(PyArray_Descr *dtype)
685
{
686 1
    if (!PyDataType_ISDATETIME(dtype)) {
687 0
        PyErr_SetString(PyExc_TypeError,
688
                "cannot get datetime metadata from non-datetime type");
689 0
        return NULL;
690
    }
691

692 1
    return &(((PyArray_DatetimeDTypeMetaData *)dtype->c_metadata)->meta);
693
}
694

695
/* strtol does not know whether to put a const qualifier on endptr, wrap
696
 * it so we can put this cast in one place.
697
 */
698
NPY_NO_EXPORT long int
699 0
strtol_const(char const *str, char const **endptr, int base) {
700 1
    return strtol(str, (char**)endptr, base);
701
}
702

703
/*
704
 * Converts a substring given by 'str' and 'len' into
705
 * a date time unit multiplier + enum value, which are populated
706
 * into out_meta. Other metadata is left along.
707
 *
708
 * 'metastr' is only used in the error message, and may be NULL.
709
 *
710
 * Returns 0 on success, -1 on failure.
711
 */
712
NPY_NO_EXPORT int
713 1
parse_datetime_extended_unit_from_string(char const *str, Py_ssize_t len,
714
                                    char const *metastr,
715
                                    PyArray_DatetimeMetaData *out_meta)
716
{
717 1
    char const *substr = str, *substrend = NULL;
718 1
    int den = 1;
719

720
    /* First comes an optional integer multiplier */
721 1
    out_meta->num = (int)strtol_const(substr, &substrend, 10);
722 1
    if (substr == substrend) {
723 1
        out_meta->num = 1;
724
    }
725
    substr = substrend;
726

727
    /* Next comes the unit itself, followed by either '/' or the string end */
728
    substrend = substr;
729 1
    while (substrend-str < len && *substrend != '/') {
730 1
        ++substrend;
731
    }
732 1
    if (substr == substrend) {
733
        goto bad_input;
734
    }
735 1
    out_meta->base = parse_datetime_unit_from_string(substr,
736
                                                     substrend - substr,
737
                                                     metastr);
738 1
    if (out_meta->base == NPY_FR_ERROR ) {
739
        return -1;
740
    }
741 1
    substr = substrend;
742

743
    /* Next comes an optional integer denominator */
744 1
    if (substr-str < len && *substr == '/') {
745 1
        substr++;
746 1
        den = (int)strtol_const(substr, &substrend, 10);
747
        /* If the '/' exists, there must be a number followed by ']' */
748 1
        if (substr == substrend || *substrend != ']') {
749
            goto bad_input;
750
        }
751 1
        substr = substrend + 1;
752
    }
753 1
    else if (substr-str != len) {
754
        goto bad_input;
755
    }
756

757 1
    if (den != 1) {
758 1
        if (convert_datetime_divisor_to_multiple(
759
                                out_meta, den, metastr) < 0) {
760
            return -1;
761
        }
762
    }
763

764
    return 0;
765

766 0
bad_input:
767 0
    if (metastr != NULL) {
768 0
        PyErr_Format(PyExc_TypeError,
769
                "Invalid datetime metadata string \"%s\" at position %zd",
770
                metastr, substr-metastr);
771
    }
772
    else {
773 0
        PyErr_Format(PyExc_TypeError,
774
                "Invalid datetime metadata string \"%s\"",
775
                str);
776
    }
777

778
    return -1;
779
}
780

781
/*
782
 * Parses the metadata string into the metadata C structure.
783
 *
784
 * Returns 0 on success, -1 on failure.
785
 */
786
NPY_NO_EXPORT int
787 1
parse_datetime_metadata_from_metastr(char const *metastr, Py_ssize_t len,
788
                                    PyArray_DatetimeMetaData *out_meta)
789
{
790 1
    char const *substr = metastr, *substrend = NULL;
791

792
    /* Treat the empty string as generic units */
793 1
    if (len == 0) {
794 1
        out_meta->base = NPY_FR_GENERIC;
795 1
        out_meta->num = 1;
796

797 1
        return 0;
798
    }
799

800
    /* The metadata string must start with a '[' */
801 1
    if (len < 3 || *substr++ != '[') {
802
        goto bad_input;
803
    }
804

805
    substrend = substr;
806 1
    while (substrend - metastr < len && *substrend != ']') {
807 1
        ++substrend;
808
    }
809 1
    if (substrend - metastr == len || substr == substrend) {
810
        substr = substrend;
811
        goto bad_input;
812
    }
813

814
    /* Parse the extended unit inside the [] */
815 1
    if (parse_datetime_extended_unit_from_string(substr, substrend-substr,
816
                                                    metastr, out_meta) < 0) {
817
        return -1;
818
    }
819

820 1
    substr = substrend+1;
821

822 1
    if (substr - metastr != len) {
823
        goto bad_input;
824
    }
825

826
    return 0;
827

828 0
bad_input:
829 0
    if (substr != metastr) {
830 0
        PyErr_Format(PyExc_TypeError,
831
                "Invalid datetime metadata string \"%s\" at position %zd",
832
                metastr, substr - metastr);
833
    }
834
    else {
835 0
        PyErr_Format(PyExc_TypeError,
836
                "Invalid datetime metadata string \"%s\"",
837
                metastr);
838
    }
839

840
    return -1;
841
}
842

843
/*
844
 * Converts a datetype dtype string into a dtype descr object.
845
 * The "type" string should be NULL-terminated.
846
 */
847
NPY_NO_EXPORT PyArray_Descr *
848 1
parse_dtype_from_datetime_typestr(char const *typestr, Py_ssize_t len)
849
{
850
    PyArray_DatetimeMetaData meta;
851 1
    char const *metastr = NULL;
852 1
    int is_timedelta = 0;
853 1
    Py_ssize_t metalen = 0;
854

855 1
    if (len < 2) {
856 0
        PyErr_Format(PyExc_TypeError,
857
                "Invalid datetime typestr \"%s\"",
858
                typestr);
859 0
        return NULL;
860
    }
861

862
    /*
863
     * First validate that the root is correct,
864
     * and get the metadata string address
865
     */
866 1
    if (typestr[0] == 'm' && typestr[1] == '8') {
867 1
        is_timedelta = 1;
868 1
        metastr = typestr + 2;
869 1
        metalen = len - 2;
870
    }
871 1
    else if (typestr[0] == 'M' && typestr[1] == '8') {
872 1
        is_timedelta = 0;
873 1
        metastr = typestr + 2;
874 1
        metalen = len - 2;
875
    }
876 1
    else if (len >= 11 && strncmp(typestr, "timedelta64", 11) == 0) {
877 1
        is_timedelta = 1;
878 1
        metastr = typestr + 11;
879 1
        metalen = len - 11;
880
    }
881 1
    else if (len >= 10 && strncmp(typestr, "datetime64", 10) == 0) {
882 1
        is_timedelta = 0;
883 1
        metastr = typestr + 10;
884 1
        metalen = len - 10;
885
    }
886
    else {
887 0
        PyErr_Format(PyExc_TypeError,
888
                "Invalid datetime typestr \"%s\"",
889
                typestr);
890 0
        return NULL;
891
    }
892

893
    /* Parse the metadata string into a metadata struct */
894 1
    if (parse_datetime_metadata_from_metastr(metastr, metalen, &meta) < 0) {
895
        return NULL;
896
    }
897

898 1
    return create_datetime_dtype(is_timedelta ? NPY_TIMEDELTA : NPY_DATETIME,
899
                                    &meta);
900
}
901

902
static NPY_DATETIMEUNIT _multiples_table[16][4] = {
903
    {12, 52, 365},                            /* NPY_FR_Y */
904
    {NPY_FR_M, NPY_FR_W, NPY_FR_D},
905
    {4,  30, 720},                            /* NPY_FR_M */
906
    {NPY_FR_W, NPY_FR_D, NPY_FR_h},
907
    {7,  168, 10080},                         /* NPY_FR_W */
908
    {NPY_FR_D, NPY_FR_h, NPY_FR_m},
909
    {0},                                      /* Gap for removed NPY_FR_B */
910
    {0},
911
    {24, 1440, 86400},                        /* NPY_FR_D */
912
    {NPY_FR_h, NPY_FR_m, NPY_FR_s},
913
    {60, 3600},                               /* NPY_FR_h */
914
    {NPY_FR_m, NPY_FR_s},
915
    {60, 60000},                              /* NPY_FR_m */
916
    {NPY_FR_s, NPY_FR_ms},
917
    {1000, 1000000},                          /* >=NPY_FR_s */
918
    {0, 0}
919
};
920

921

922

923
/*
924
 * Translate divisors into multiples of smaller units.
925
 * 'metastr' is used for the error message if the divisor doesn't work,
926
 * and can be NULL if the metadata didn't come from a string.
927
 *
928
 * This function only affects the 'base' and 'num' values in the metadata.
929
 *
930
 * Returns 0 on success, -1 on failure.
931
 */
932
NPY_NO_EXPORT int
933 1
convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta,
934
                                    int den, char const *metastr)
935
{
936
    int i, num, ind;
937
    NPY_DATETIMEUNIT *totry;
938
    NPY_DATETIMEUNIT *baseunit;
939
    int q, r;
940

941 1
    if (meta->base == NPY_FR_GENERIC) {
942 0
        PyErr_SetString(PyExc_ValueError,
943
            "Can't use 'den' divisor with generic units");
944 0
        return -1;
945
    }
946

947 1
    ind = ((int)meta->base - (int)NPY_FR_Y)*2;
948 1
    totry = _multiples_table[ind];
949 1
    baseunit = _multiples_table[ind + 1];
950

951 1
    num = 3;
952 1
    if (meta->base == NPY_FR_W) {
953
        num = 4;
954
    }
955 1
    else if (meta->base > NPY_FR_D) {
956 1
        num = 2;
957
    }
958 1
    if (meta->base >= NPY_FR_s) {
959 1
        ind = ((int)NPY_FR_s - (int)NPY_FR_Y)*2;
960 1
        totry = _multiples_table[ind];
961 1
        baseunit = _multiples_table[ind + 1];
962 1
        baseunit[0] = meta->base + 1;
963 1
        baseunit[1] = meta->base + 2;
964 1
        if (meta->base == NPY_FR_as - 1) {
965 1
            num = 1;
966
        }
967 1
        if (meta->base == NPY_FR_as) {
968 1
            num = 0;
969
        }
970
    }
971

972 1
    for (i = 0; i < num; i++) {
973 1
        q = totry[i] / den;
974 1
        r = totry[i] % den;
975 1
        if (r == 0) {
976
            break;
977
        }
978
    }
979 1
    if (i == num) {
980 1
        if (metastr == NULL) {
981 0
            PyErr_Format(PyExc_ValueError,
982
                    "divisor (%d) is not a multiple of a lower-unit "
983
                    "in datetime metadata", den);
984
        }
985
        else {
986 1
            PyErr_Format(PyExc_ValueError,
987
                    "divisor (%d) is not a multiple of a lower-unit "
988
                    "in datetime metadata \"%s\"", den, metastr);
989
        }
990
        return -1;
991
    }
992 1
    meta->base = baseunit[i];
993 1
    meta->num *= q;
994

995 1
    return 0;
996
}
997

998
/*
999
 * Lookup table for factors between datetime units, except
1000
 * for years and months.
1001
 */
1002
static npy_uint32
1003
_datetime_factors[] = {
1004
    1,  /* Years - not used */
1005
    1,  /* Months - not used */
1006
    7,  /* Weeks -> Days */
1007
    1,  /* Business Days - was removed but a gap still exists in the enum */
1008
    24, /* Days -> Hours */
1009
    60, /* Hours -> Minutes */
1010
    60, /* Minutes -> Seconds */
1011
    1000,
1012
    1000,
1013
    1000,
1014
    1000,
1015
    1000,
1016
    1000,
1017
    1,   /* Attoseconds are the smallest base unit */
1018
    0    /* Generic units don't have a conversion */
1019
};
1020

1021
/*
1022
 * Returns the scale factor between the units. Does not validate
1023
 * that bigbase represents larger units than littlebase, or that
1024
 * the units are not generic.
1025
 *
1026
 * Returns 0 if there is an overflow.
1027
 */
1028
static npy_uint64
1029
get_datetime_units_factor(NPY_DATETIMEUNIT bigbase, NPY_DATETIMEUNIT littlebase)
1030
{
1031
    npy_uint64 factor = 1;
1032
    NPY_DATETIMEUNIT unit = bigbase;
1033

1034 1
    while (unit < littlebase) {
1035 1
        factor *= _datetime_factors[unit];
1036
        /*
1037
         * Detect overflow by disallowing the top 16 bits to be 1.
1038
         * That allows a margin of error much bigger than any of
1039
         * the datetime factors.
1040
         */
1041 1
        if (factor&0xff00000000000000ULL) {
1042
            return 0;
1043
        }
1044 1
        ++unit;
1045
    }
1046
    return factor;
1047
}
1048

1049
/* Euclidean algorithm on two positive numbers */
1050
static npy_uint64
1051
_uint64_euclidean_gcd(npy_uint64 x, npy_uint64 y)
1052
{
1053
    npy_uint64 tmp;
1054

1055 1
    if (x > y) {
1056 1
        tmp = x;
1057 1
        x = y;
1058 1
        y = tmp;
1059
    }
1060 1
    while (x != y && y != 0) {
1061 1
        tmp = x % y;
1062 1
        x = y;
1063 1
        y = tmp;
1064
    }
1065

1066
    return x;
1067
}
1068

1069
/*
1070
 * Computes the conversion factor to convert data with 'src_meta' metadata
1071
 * into data with 'dst_meta' metadata.
1072
 *
1073
 * If overflow occurs, both out_num and out_denom are set to 0, but
1074
 * no error is set.
1075
 */
1076
NPY_NO_EXPORT void
1077 1
get_datetime_conversion_factor(PyArray_DatetimeMetaData *src_meta,
1078
                                PyArray_DatetimeMetaData *dst_meta,
1079
                                npy_int64 *out_num, npy_int64 *out_denom)
1080
{
1081
    int src_base, dst_base, swapped;
1082 1
    npy_uint64 num = 1, denom = 1, tmp, gcd;
1083

1084
    /* Generic units change to the destination with no conversion factor */
1085 1
    if (src_meta->base == NPY_FR_GENERIC) {
1086 1
        *out_num = 1;
1087 1
        *out_denom = 1;
1088 1
        return;
1089
    }
1090
    /*
1091
     * Converting to a generic unit from something other than a generic
1092
     * unit is an error.
1093
     */
1094 1
    else if (dst_meta->base == NPY_FR_GENERIC) {
1095 1
        PyErr_SetString(PyExc_ValueError,
1096
                    "Cannot convert from specific units to generic "
1097
                    "units in NumPy datetimes or timedeltas");
1098 1
        *out_num = 0;
1099 1
        *out_denom = 0;
1100 1
        return;
1101
    }
1102

1103 1
    if (src_meta->base <= dst_meta->base) {
1104
        src_base = src_meta->base;
1105
        dst_base = dst_meta->base;
1106
        swapped = 0;
1107
    }
1108
    else {
1109 1
        src_base = dst_meta->base;
1110 1
        dst_base = src_meta->base;
1111 1
        swapped = 1;
1112
    }
1113

1114 1
    if (src_base != dst_base) {
1115
        /*
1116
         * Conversions between years/months and other units use
1117
         * the factor averaged over the 400 year leap year cycle.
1118
         */
1119 1
        if (src_base == NPY_FR_Y) {
1120 1
            if (dst_base == NPY_FR_M) {
1121
                num *= 12;
1122
            }
1123 1
            else if (dst_base == NPY_FR_W) {
1124
                num *= (97 + 400*365);
1125
                denom *= 400*7;
1126
            }
1127
            else {
1128
                /* Year -> Day */
1129
                num *= (97 + 400*365);
1130
                denom *= 400;
1131
                /* Day -> dst_base */
1132 1
                num *= get_datetime_units_factor(NPY_FR_D, dst_base);
1133
            }
1134
        }
1135 1
        else if (src_base == NPY_FR_M) {
1136 1
            if (dst_base == NPY_FR_W) {
1137
                num *= (97 + 400*365);
1138
                denom *= 400*12*7;
1139
            }
1140
            else {
1141
                /* Month -> Day */
1142
                num *= (97 + 400*365);
1143
                denom *= 400*12;
1144
                /* Day -> dst_base */
1145 1
                num *= get_datetime_units_factor(NPY_FR_D, dst_base);
1146
            }
1147
        }
1148
        else {
1149
            num *= get_datetime_units_factor(src_base, dst_base);
1150
        }
1151
    }
1152

1153
    /* If something overflowed, make both num and denom 0 */
1154 1
    if (denom == 0 || num == 0) {
1155 1
        PyErr_Format(PyExc_OverflowError,
1156
                    "Integer overflow while computing the conversion "
1157
                    "factor between NumPy datetime units %s and %s",
1158
                    _datetime_strings[src_base],
1159
                    _datetime_strings[dst_base]);
1160 1
        *out_num = 0;
1161 1
        *out_denom = 0;
1162 1
        return;
1163
    }
1164

1165
    /* Swap the numerator and denominator if necessary */
1166 1
    if (swapped) {
1167 1
        tmp = num;
1168 1
        num = denom;
1169 1
        denom = tmp;
1170
    }
1171

1172 1
    num *= src_meta->num;
1173 1
    denom *= dst_meta->num;
1174

1175
    /* Return as a fraction in reduced form */
1176 1
    gcd = _uint64_euclidean_gcd(num, denom);
1177 1
    *out_num = (npy_int64)(num / gcd);
1178 1
    *out_denom = (npy_int64)(denom / gcd);
1179
}
1180

1181
/*
1182
 * Determines whether the 'divisor' metadata divides evenly into
1183
 * the 'dividend' metadata.
1184
 */
1185
NPY_NO_EXPORT npy_bool
1186 1
datetime_metadata_divides(
1187
                        PyArray_DatetimeMetaData *dividend,
1188
                        PyArray_DatetimeMetaData *divisor,
1189
                        int strict_with_nonlinear_units)
1190
{
1191
    npy_uint64 num1, num2;
1192

1193
    /*
1194
     * Any unit can always divide into generic units. In other words, we
1195
     * should be able to convert generic units into any more specific unit.
1196
     */
1197 1
    if (dividend->base == NPY_FR_GENERIC) {
1198
        return 1;
1199
    }
1200
    /*
1201
     * However, generic units cannot always divide into more specific units.
1202
     * We cannot safely convert datetimes with units back into generic units.
1203
     */
1204 1
    else if (divisor->base == NPY_FR_GENERIC) {
1205
        return 0;
1206
    }
1207

1208 1
    num1 = (npy_uint64)dividend->num;
1209 1
    num2 = (npy_uint64)divisor->num;
1210

1211
    /* If the bases are different, factor in a conversion */
1212 1
    if (dividend->base != divisor->base) {
1213
        /*
1214
         * Years and Months are incompatible with
1215
         * all other units (except years and months are compatible
1216
         * with each other).
1217
         */
1218 1
        if (dividend->base == NPY_FR_Y) {
1219 1
            if (divisor->base == NPY_FR_M) {
1220 0
                num1 *= 12;
1221
            }
1222 1
            else if (strict_with_nonlinear_units) {
1223
                return 0;
1224
            }
1225
            else {
1226
                /* Could do something complicated here */
1227 1
                return 1;
1228
            }
1229
        }
1230 1
        else if (divisor->base == NPY_FR_Y) {
1231 0
            if (dividend->base == NPY_FR_M) {
1232 0
                num2 *= 12;
1233
            }
1234 0
            else if (strict_with_nonlinear_units) {
1235
                return 0;
1236
            }
1237
            else {
1238
                /* Could do something complicated here */
1239 0
                return 1;
1240
            }
1241
        }
1242 1
        else if (dividend->base == NPY_FR_M || divisor->base == NPY_FR_M) {
1243 1
            if (strict_with_nonlinear_units) {
1244
                return 0;
1245
            }
1246
            else {
1247
                /* Could do something complicated here */
1248 1
                return 1;
1249
            }
1250
        }
1251

1252
        /* Take the greater base (unit sizes are decreasing in enum) */
1253 0
        if (dividend->base > divisor->base) {
1254 0
            num2 *= get_datetime_units_factor(divisor->base, dividend->base);
1255 0
            if (num2 == 0) {
1256
                return 0;
1257
            }
1258
        }
1259
        else {
1260 0
            num1 *= get_datetime_units_factor(dividend->base, divisor->base);
1261 0
            if (num1 == 0) {
1262
                return 0;
1263
            }
1264
        }
1265
    }
1266

1267
    /* Crude, incomplete check for overflow */
1268 1
    if (num1&0xff00000000000000LL || num2&0xff00000000000000LL ) {
1269
        return 0;
1270
    }
1271

1272 1
    return (num1 % num2) == 0;
1273
}
1274

1275
/*
1276
 * This provides the casting rules for the DATETIME data type units.
1277
 */
1278
NPY_NO_EXPORT npy_bool
1279 1
can_cast_datetime64_units(NPY_DATETIMEUNIT src_unit,
1280
                          NPY_DATETIMEUNIT dst_unit,
1281
                          NPY_CASTING casting)
1282
{
1283 1
    switch (casting) {
1284
        /* Allow anything with unsafe casting */
1285
        case NPY_UNSAFE_CASTING:
1286
            return 1;
1287

1288
        /*
1289
         * Can cast between all units with 'same_kind' casting.
1290
         */
1291 1
        case NPY_SAME_KIND_CASTING:
1292 1
            if (src_unit == NPY_FR_GENERIC || dst_unit == NPY_FR_GENERIC) {
1293 1
                return src_unit == NPY_FR_GENERIC;
1294
            }
1295
            else {
1296
                return 1;
1297
            }
1298

1299
        /*
1300
         * Casting is only allowed towards more precise units with 'safe'
1301
         * casting.
1302
         */
1303 1
        case NPY_SAFE_CASTING:
1304 1
            if (src_unit == NPY_FR_GENERIC || dst_unit == NPY_FR_GENERIC) {
1305 1
                return src_unit == NPY_FR_GENERIC;
1306
            }
1307
            else {
1308 1
                return (src_unit <= dst_unit);
1309
            }
1310

1311
        /* Enforce equality with 'no' or 'equiv' casting */
1312 0
        default:
1313 0
            return src_unit == dst_unit;
1314
    }
1315
}
1316

1317
/*
1318
 * This provides the casting rules for the TIMEDELTA data type units.
1319
 *
1320
 * Notably, there is a barrier between the nonlinear years and
1321
 * months units, and all the other units.
1322
 */
1323
NPY_NO_EXPORT npy_bool
1324 1
can_cast_timedelta64_units(NPY_DATETIMEUNIT src_unit,
1325
                          NPY_DATETIMEUNIT dst_unit,
1326
                          NPY_CASTING casting)
1327
{
1328 1
    switch (casting) {
1329
        /* Allow anything with unsafe casting */
1330
        case NPY_UNSAFE_CASTING:
1331
            return 1;
1332

1333
        /*
1334
         * Only enforce the 'date units' vs 'time units' barrier with
1335
         * 'same_kind' casting.
1336
         */
1337 1
        case NPY_SAME_KIND_CASTING:
1338 1
            if (src_unit == NPY_FR_GENERIC || dst_unit == NPY_FR_GENERIC) {
1339 1
                return src_unit == NPY_FR_GENERIC;
1340
            }
1341
            else {
1342 1
                return (src_unit <= NPY_FR_M && dst_unit <= NPY_FR_M) ||
1343
                       (src_unit > NPY_FR_M && dst_unit > NPY_FR_M);
1344
            }
1345

1346
        /*
1347
         * Enforce the 'date units' vs 'time units' barrier and that
1348
         * casting is only allowed towards more precise units with
1349
         * 'safe' casting.
1350
         */
1351 1
        case NPY_SAFE_CASTING:
1352 1
            if (src_unit == NPY_FR_GENERIC || dst_unit == NPY_FR_GENERIC) {
1353 1
                return src_unit == NPY_FR_GENERIC;
1354
            }
1355
            else {
1356 1
                return (src_unit <= dst_unit) &&
1357 1
                       ((src_unit <= NPY_FR_M && dst_unit <= NPY_FR_M) ||
1358
                        (src_unit > NPY_FR_M && dst_unit > NPY_FR_M));
1359
            }
1360

1361
        /* Enforce equality with 'no' or 'equiv' casting */
1362 0
        default:
1363 0
            return src_unit == dst_unit;
1364
    }
1365
}
1366

1367
/*
1368
 * This provides the casting rules for the DATETIME data type metadata.
1369
 */
1370
NPY_NO_EXPORT npy_bool
1371 1
can_cast_datetime64_metadata(PyArray_DatetimeMetaData *src_meta,
1372
                             PyArray_DatetimeMetaData *dst_meta,
1373
                             NPY_CASTING casting)
1374
{
1375 1
    switch (casting) {
1376
        case NPY_UNSAFE_CASTING:
1377
            return 1;
1378

1379 1
        case NPY_SAME_KIND_CASTING:
1380 1
            return can_cast_datetime64_units(src_meta->base, dst_meta->base,
1381
                                             casting);
1382

1383 1
        case NPY_SAFE_CASTING:
1384 1
            return can_cast_datetime64_units(src_meta->base, dst_meta->base,
1385 1
                                                             casting) &&
1386 1
                   datetime_metadata_divides(src_meta, dst_meta, 0);
1387

1388 1
        default:
1389 1
            return src_meta->base == dst_meta->base &&
1390 1
                   src_meta->num == dst_meta->num;
1391
    }
1392
}
1393

1394
/*
1395
 * This provides the casting rules for the TIMEDELTA data type metadata.
1396
 */
1397
NPY_NO_EXPORT npy_bool
1398 1
can_cast_timedelta64_metadata(PyArray_DatetimeMetaData *src_meta,
1399
                             PyArray_DatetimeMetaData *dst_meta,
1400
                             NPY_CASTING casting)
1401
{
1402 1
    switch (casting) {
1403
        case NPY_UNSAFE_CASTING:
1404
            return 1;
1405

1406 1
        case NPY_SAME_KIND_CASTING:
1407 1
            return can_cast_timedelta64_units(src_meta->base, dst_meta->base,
1408
                                             casting);
1409

1410 1
        case NPY_SAFE_CASTING:
1411 1
            return can_cast_timedelta64_units(src_meta->base, dst_meta->base,
1412 1
                                                             casting) &&
1413 1
                   datetime_metadata_divides(src_meta, dst_meta, 1);
1414

1415 0
        default:
1416 0
            return src_meta->base == dst_meta->base &&
1417 0
                   src_meta->num == dst_meta->num;
1418
    }
1419
}
1420

1421
/*
1422
 * Tests whether a datetime64 can be cast from the source metadata
1423
 * to the destination metadata according to the specified casting rule.
1424
 *
1425
 * Returns -1 if an exception was raised, 0 otherwise.
1426
 */
1427
NPY_NO_EXPORT int
1428 1
raise_if_datetime64_metadata_cast_error(char *object_type,
1429
                            PyArray_DatetimeMetaData *src_meta,
1430
                            PyArray_DatetimeMetaData *dst_meta,
1431
                            NPY_CASTING casting)
1432
{
1433 1
    if (can_cast_datetime64_metadata(src_meta, dst_meta, casting)) {
1434
        return 0;
1435
    }
1436
    else {
1437
        PyObject *errmsg;
1438 0
        errmsg = PyUnicode_FromFormat("Cannot cast %s "
1439
                    "from metadata ", object_type);
1440 0
        errmsg = append_metastr_to_string(src_meta, 0, errmsg);
1441 0
        PyUString_ConcatAndDel(&errmsg,
1442
                PyUnicode_FromString(" to "));
1443 0
        errmsg = append_metastr_to_string(dst_meta, 0, errmsg);
1444 0
        PyUString_ConcatAndDel(&errmsg,
1445
                PyUnicode_FromFormat(" according to the rule %s",
1446
                        npy_casting_to_string(casting)));
1447 0
        PyErr_SetObject(PyExc_TypeError, errmsg);
1448 0
        Py_DECREF(errmsg);
1449
        return -1;
1450
    }
1451
}
1452

1453
/*
1454
 * Tests whether a timedelta64 can be cast from the source metadata
1455
 * to the destination metadata according to the specified casting rule.
1456
 *
1457
 * Returns -1 if an exception was raised, 0 otherwise.
1458
 */
1459
NPY_NO_EXPORT int
1460 1
raise_if_timedelta64_metadata_cast_error(char *object_type,
1461
                            PyArray_DatetimeMetaData *src_meta,
1462
                            PyArray_DatetimeMetaData *dst_meta,
1463
                            NPY_CASTING casting)
1464
{
1465 1
    if (can_cast_timedelta64_metadata(src_meta, dst_meta, casting)) {
1466
        return 0;
1467
    }
1468
    else {
1469
        PyObject *errmsg;
1470 1
        errmsg = PyUnicode_FromFormat("Cannot cast %s "
1471
                    "from metadata ", object_type);
1472 1
        errmsg = append_metastr_to_string(src_meta, 0, errmsg);
1473 1
        PyUString_ConcatAndDel(&errmsg,
1474
                PyUnicode_FromString(" to "));
1475 1
        errmsg = append_metastr_to_string(dst_meta, 0, errmsg);
1476 1
        PyUString_ConcatAndDel(&errmsg,
1477
                PyUnicode_FromFormat(" according to the rule %s",
1478
                        npy_casting_to_string(casting)));
1479 1
        PyErr_SetObject(PyExc_TypeError, errmsg);
1480 1
        Py_DECREF(errmsg);
1481
        return -1;
1482
    }
1483
}
1484

1485
/*
1486
 * Computes the GCD of the two date-time metadata values. Raises
1487
 * an exception if there is no reasonable GCD, such as with
1488
 * years and days.
1489
 *
1490
 * The result is placed in 'out_meta'.
1491
 *
1492
 * Returns 0 on success, -1 on failure.
1493
 */
1494
NPY_NO_EXPORT int
1495 1
compute_datetime_metadata_greatest_common_divisor(
1496
                        PyArray_DatetimeMetaData *meta1,
1497
                        PyArray_DatetimeMetaData *meta2,
1498
                        PyArray_DatetimeMetaData *out_meta,
1499
                        int strict_with_nonlinear_units1,
1500
                        int strict_with_nonlinear_units2)
1501
{
1502
    NPY_DATETIMEUNIT base;
1503
    npy_uint64 num1, num2, num;
1504

1505
    /* If either unit is generic, adopt the metadata from the other one */
1506 1
    if (meta1->base == NPY_FR_GENERIC) {
1507 1
        *out_meta = *meta2;
1508 1
        return 0;
1509
    }
1510 1
    else if (meta2->base == NPY_FR_GENERIC) {
1511 1
        *out_meta = *meta1;
1512 1
        return 0;
1513
    }
1514

1515 1
    num1 = (npy_uint64)meta1->num;
1516 1
    num2 = (npy_uint64)meta2->num;
1517

1518
    /* First validate that the units have a reasonable GCD */
1519 1
    if (meta1->base == meta2->base) {
1520
        base = meta1->base;
1521
    }
1522
    else {
1523
        /*
1524
         * Years and Months are incompatible with
1525
         * all other units (except years and months are compatible
1526
         * with each other).
1527
         */
1528 1
        if (meta1->base == NPY_FR_Y) {
1529 1
            if (meta2->base == NPY_FR_M) {
1530 1
                base = NPY_FR_M;
1531 1
                num1 *= 12;
1532
            }
1533 1
            else if (strict_with_nonlinear_units1) {
1534
                goto incompatible_units;
1535
            }
1536
            else {
1537
                base = meta2->base;
1538
                /* Don't multiply num1 since there is no even factor */
1539
            }
1540
        }
1541 1
        else if (meta2->base == NPY_FR_Y) {
1542 1
            if (meta1->base == NPY_FR_M) {
1543 1
                base = NPY_FR_M;
1544 1
                num2 *= 12;
1545
            }
1546 1
            else if (strict_with_nonlinear_units2) {
1547
                goto incompatible_units;
1548
            }
1549
            else {
1550
                base = meta1->base;
1551
                /* Don't multiply num2 since there is no even factor */
1552
            }
1553
        }
1554 1
        else if (meta1->base == NPY_FR_M) {
1555 1
            if (strict_with_nonlinear_units1) {
1556
                goto incompatible_units;
1557
            }
1558
            else {
1559
                base = meta2->base;
1560
                /* Don't multiply num1 since there is no even factor */
1561
            }
1562
        }
1563 1
        else if (meta2->base == NPY_FR_M) {
1564 1
            if (strict_with_nonlinear_units2) {
1565
                goto incompatible_units;
1566
            }
1567
            else {
1568
                base = meta1->base;
1569
                /* Don't multiply num2 since there is no even factor */
1570
            }
1571
        }
1572

1573
        /* Take the greater base (unit sizes are decreasing in enum) */
1574 1
        if (meta1->base > meta2->base) {
1575
            base = meta1->base;
1576 1
            num2 *= get_datetime_units_factor(meta2->base, meta1->base);
1577 1
            if (num2 == 0) {
1578
                goto units_overflow;
1579
            }
1580
        }
1581
        else {
1582
            base = meta2->base;
1583 1
            num1 *= get_datetime_units_factor(meta1->base, meta2->base);
1584 1
            if (num1 == 0) {
1585
                goto units_overflow;
1586
            }
1587
        }
1588
    }
1589

1590
    /* Compute the GCD of the resulting multipliers */
1591 1
    num = _uint64_euclidean_gcd(num1, num2);
1592

1593
    /* Fill the 'out_meta' values */
1594 1
    out_meta->base = base;
1595 1
    out_meta->num = (int)num;
1596 1
    if (out_meta->num <= 0 || num != (npy_uint64)out_meta->num) {
1597
        goto units_overflow;
1598
    }
1599

1600
    return 0;
1601

1602 1
incompatible_units: {
1603
        PyObject *errmsg;
1604 1
        errmsg = PyUnicode_FromString("Cannot get "
1605
                    "a common metadata divisor for "
1606
                    "NumPy datetime metadata ");
1607 1
        errmsg = append_metastr_to_string(meta1, 0, errmsg);
1608 1
        PyUString_ConcatAndDel(&errmsg,
1609
                PyUnicode_FromString(" and "));
1610 1
        errmsg = append_metastr_to_string(meta2, 0, errmsg);
1611 1
        PyUString_ConcatAndDel(&errmsg,
1612
                PyUnicode_FromString(" because they have "
1613
                    "incompatible nonlinear base time units"));
1614 1
        PyErr_SetObject(PyExc_TypeError, errmsg);
1615 1
        Py_DECREF(errmsg);
1616
        return -1;
1617
    }
1618 1
units_overflow: {
1619
        PyObject *errmsg;
1620 1
        errmsg = PyUnicode_FromString("Integer overflow "
1621
                    "getting a common metadata divisor for "
1622
                    "NumPy datetime metadata ");
1623 1
        errmsg = append_metastr_to_string(meta1, 0, errmsg);
1624 1
        PyUString_ConcatAndDel(&errmsg,
1625
                PyUnicode_FromString(" and "));
1626 1
        errmsg = append_metastr_to_string(meta2, 0, errmsg);
1627 1
        PyErr_SetObject(PyExc_OverflowError, errmsg);
1628 1
        Py_DECREF(errmsg);
1629
        return -1;
1630
    }
1631
}
1632

1633
/*
1634
 * Both type1 and type2 must be either NPY_DATETIME or NPY_TIMEDELTA.
1635
 * Applies the type promotion rules between the two types, returning
1636
 * the promoted type.
1637
 */
1638
NPY_NO_EXPORT PyArray_Descr *
1639 1
datetime_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2)
1640
{
1641
    int type_num1, type_num2;
1642
    PyArray_Descr *dtype;
1643
    int is_datetime;
1644

1645 1
    type_num1 = type1->type_num;
1646 1
    type_num2 = type2->type_num;
1647

1648 1
    is_datetime = (type_num1 == NPY_DATETIME || type_num2 == NPY_DATETIME);
1649

1650
    /* Create a DATETIME or TIMEDELTA dtype */
1651 1
    dtype = PyArray_DescrNewFromType(is_datetime ? NPY_DATETIME :
1652
                                                   NPY_TIMEDELTA);
1653 1
    if (dtype == NULL) {
1654
        return NULL;
1655
    }
1656

1657
    /*
1658
     * Get the metadata GCD, being strict about nonlinear units for
1659
     * timedelta and relaxed for datetime.
1660
     */
1661 1
    if (compute_datetime_metadata_greatest_common_divisor(
1662
                                    get_datetime_metadata_from_dtype(type1),
1663
                                    get_datetime_metadata_from_dtype(type2),
1664
                                    get_datetime_metadata_from_dtype(dtype),
1665
                                    type_num1 == NPY_TIMEDELTA,
1666
                                    type_num2 == NPY_TIMEDELTA) < 0) {
1667 1
        Py_DECREF(dtype);
1668
        return NULL;
1669
    }
1670

1671
    return dtype;
1672
}
1673

1674
/*
1675
 * Converts a substring given by 'str' and 'len' into
1676
 * a date time unit enum value. The 'metastr' parameter
1677
 * is used for error messages, and may be NULL.
1678
 *
1679
 * Returns NPY_DATETIMEUNIT on success, NPY_FR_ERROR on failure.
1680
 */
1681
NPY_NO_EXPORT NPY_DATETIMEUNIT
1682 1
parse_datetime_unit_from_string(char const *str, Py_ssize_t len, char const *metastr)
1683
{
1684
    /* Use switch statements so the compiler can make it fast */
1685 1
    if (len == 1) {
1686 1
        switch (str[0]) {
1687
            case 'Y':
1688
                return NPY_FR_Y;
1689 1
            case 'M':
1690 1
                return NPY_FR_M;
1691 1
            case 'W':
1692 1
                return NPY_FR_W;
1693 1
            case 'D':
1694 1
                return NPY_FR_D;
1695 1
            case 'h':
1696 1
                return NPY_FR_h;
1697 1
            case 'm':
1698 1
                return NPY_FR_m;
1699 1
            case 's':
1700 1
                return NPY_FR_s;
1701
        }
1702
    }
1703
    /* All the two-letter units are variants of seconds */
1704 1
    else if (len == 2 && str[1] == 's') {
1705 1
        switch (str[0]) {
1706
            case 'm':
1707
                return NPY_FR_ms;
1708 1
            case 'u':
1709 1
                return NPY_FR_us;
1710 1
            case 'n':
1711 1
                return NPY_FR_ns;
1712 1
            case 'p':
1713 1
                return NPY_FR_ps;
1714 1
            case 'f':
1715 1
                return NPY_FR_fs;
1716 1
            case 'a':
1717 1
                return NPY_FR_as;
1718
        }
1719
    }
1720 1
    else if (len == 3 && !strncmp(str, "\xce\xbcs", 3)) {
1721
        /* greek small letter mu, utf8-encoded */
1722
        return NPY_FR_us;
1723
    }
1724 1
    else if (len == 7 && !strncmp(str, "generic", 7)) {
1725
        return NPY_FR_GENERIC;
1726
    }
1727

1728
    /* If nothing matched, it's an error */
1729 1
    if (metastr == NULL) {
1730 0
        PyErr_Format(PyExc_TypeError,
1731
                "Invalid datetime unit \"%s\" in metadata",
1732
                str);
1733
    }
1734
    else {
1735 1
        PyErr_Format(PyExc_TypeError,
1736
                "Invalid datetime unit in metadata string \"%s\"",
1737
                metastr);
1738
    }
1739
    return NPY_FR_ERROR;
1740
}
1741

1742

1743
NPY_NO_EXPORT PyObject *
1744 1
convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta)
1745
{
1746
    PyObject *dt_tuple;
1747

1748 1
    dt_tuple = PyTuple_New(2);
1749 1
    if (dt_tuple == NULL) {
1750
        return NULL;
1751
    }
1752

1753 1
    PyTuple_SET_ITEM(dt_tuple, 0,
1754
            PyUnicode_FromString(_datetime_strings[meta->base]));
1755 1
    PyTuple_SET_ITEM(dt_tuple, 1,
1756
            PyLong_FromLong(meta->num));
1757

1758 1
    return dt_tuple;
1759
}
1760

1761
/*
1762
 * Converts a metadata tuple into a datetime metadata C struct.
1763
 *
1764
 * Returns 0 on success, -1 on failure.
1765
 */
1766
NPY_NO_EXPORT int
1767 1
convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
1768
                                        PyArray_DatetimeMetaData *out_meta,
1769
                                        npy_bool from_pickle)
1770
{
1771 1
    int den = 1;
1772

1773 1
    if (!PyTuple_Check(tuple)) {
1774 1
        PyErr_Format(PyExc_TypeError,
1775
                "Require tuple for tuple to NumPy "
1776
                "datetime metadata conversion, not %R", tuple);
1777 1
        return -1;
1778
    }
1779

1780 1
    Py_ssize_t tuple_size = PyTuple_GET_SIZE(tuple);
1781 1
    if (tuple_size < 2 || tuple_size > 4) {
1782 0
        PyErr_SetString(PyExc_TypeError,
1783
                        "Require tuple of size 2 to 4 for "
1784
                        "tuple to NumPy datetime metadata conversion");
1785 0
        return -1;
1786
    }
1787

1788 1
    PyObject *unit_str = PyTuple_GET_ITEM(tuple, 0);
1789 1
    if (PyBytes_Check(unit_str)) {
1790
        /* Allow bytes format strings: convert to unicode */
1791 1
        PyObject *tmp = PyUnicode_FromEncodedObject(unit_str, NULL, NULL);
1792 1
        if (tmp == NULL) {
1793
            return -1;
1794
        }
1795
        unit_str = tmp;
1796
    }
1797
    else {
1798 1
        Py_INCREF(unit_str);
1799
    }
1800

1801
    Py_ssize_t len;
1802 1
    char const *basestr = PyUnicode_AsUTF8AndSize(unit_str, &len);
1803 1
    if (basestr == NULL) {
1804 0
        Py_DECREF(unit_str);
1805
        return -1;
1806
    }
1807

1808 1
    out_meta->base = parse_datetime_unit_from_string(basestr, len, NULL);
1809 1
    if (out_meta->base == NPY_FR_ERROR) {
1810 0
        Py_DECREF(unit_str);
1811
        return -1;
1812
    }
1813

1814 1
    Py_DECREF(unit_str);
1815

1816
    /* Convert the values to longs */
1817 1
    out_meta->num = PyLong_AsLong(PyTuple_GET_ITEM(tuple, 1));
1818 1
    if (error_converting(out_meta->num)) {
1819
        return -1;
1820
    }
1821

1822
    /*
1823
     * The event metadata was removed way back in numpy 1.7 (cb4545), but was
1824
     * not deprecated at the time.
1825
     */
1826

1827
    /* (unit, num, event) */
1828 1
    if (tuple_size == 3) {
1829
        /* Numpy 1.14, 2017-08-11 */
1830 1
        if (DEPRECATE(
1831
                "When passing a 3-tuple as (unit, num, event), the event "
1832
                "is ignored (since 1.7) - use (unit, num) instead") < 0) {
1833
            return -1;
1834
        }
1835
    }
1836
    /* (unit, num, den, event) */
1837 1
    else if (tuple_size == 4) {
1838 1
        PyObject *event = PyTuple_GET_ITEM(tuple, 3);
1839 1
        if (from_pickle) {
1840
            /* if (event == 1) */
1841 1
            PyObject *one = PyLong_FromLong(1);
1842 1
            if (one == NULL) {
1843
                return -1;
1844
            }
1845 1
            int equal_one = PyObject_RichCompareBool(event, one, Py_EQ);
1846 1
            Py_DECREF(one);
1847 1
            if (equal_one == -1) {
1848
                return -1;
1849
            }
1850

1851
            /* if the event data is not 1, it had semantics different to how
1852
             * datetime types now behave, which are no longer respected.
1853
             */
1854 1
            if (!equal_one) {
1855 0
                if (PyErr_WarnEx(PyExc_UserWarning,
1856
                        "Loaded pickle file contains non-default event data "
1857
                        "for a datetime type, which has been ignored since 1.7",
1858
                        1) < 0) {
1859
                    return -1;
1860
                }
1861
            }
1862
        }
1863 1
        else if (event != Py_None) {
1864
            /* Numpy 1.14, 2017-08-11 */
1865 1
            if (DEPRECATE(
1866
                    "When passing a 4-tuple as (unit, num, den, event), the "
1867
                    "event argument is ignored (since 1.7), so should be None"
1868
                    ) < 0) {
1869
                return -1;
1870
            }
1871
        }
1872 1
        den = PyLong_AsLong(PyTuple_GET_ITEM(tuple, 2));
1873 1
        if (error_converting(den)) {
1874
            return -1;
1875
        }
1876
    }
1877

1878 1
    if (out_meta->num <= 0 || den <= 0) {
1879 0
        PyErr_SetString(PyExc_TypeError,
1880
                        "Invalid tuple values for "
1881
                        "tuple to NumPy datetime metadata conversion");
1882 0
        return -1;
1883
    }
1884

1885 1
    if (den != 1) {
1886 0
        if (convert_datetime_divisor_to_multiple(out_meta, den, NULL) < 0) {
1887
            return -1;
1888
        }
1889
    }
1890

1891
    return 0;
1892
}
1893

1894
/*
1895
 * Converts an input object into datetime metadata. The input
1896
 * may be either a string or a tuple.
1897
 *
1898
 * Returns 0 on success, -1 on failure.
1899
 */
1900
NPY_NO_EXPORT int
1901 1
convert_pyobject_to_datetime_metadata(PyObject *obj,
1902
                                      PyArray_DatetimeMetaData *out_meta)
1903
{
1904 1
    if (PyTuple_Check(obj)) {
1905 1
        return convert_datetime_metadata_tuple_to_datetime_metadata(
1906
            obj, out_meta, NPY_FALSE);
1907
    }
1908

1909
    /* Get a UTF8 string */
1910 1
    PyObject *utf8 = NULL;
1911 1
    if (PyBytes_Check(obj)) {
1912
        /* Allow bytes format strings: convert to unicode */
1913 1
        utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL);
1914 1
        if (utf8 == NULL) {
1915
            return -1;
1916
        }
1917
    }
1918 1
    else if (PyUnicode_Check(obj)) {
1919 1
        utf8 = obj;
1920 1
        Py_INCREF(utf8);
1921
    }
1922
    else {
1923 0
        PyErr_SetString(PyExc_TypeError,
1924
                "Invalid object for specifying NumPy datetime metadata");
1925 0
        return -1;
1926
    }
1927

1928 1
    Py_ssize_t len = 0;
1929 1
    char const *str = PyUnicode_AsUTF8AndSize(utf8, &len);
1930 1
    if (str == NULL) {
1931 0
        Py_DECREF(utf8);
1932
        return -1;
1933
    }
1934

1935 1
    if (len > 0 && str[0] == '[') {
1936 1
        int r = parse_datetime_metadata_from_metastr(str, len, out_meta);
1937 1
        Py_DECREF(utf8);
1938
        return r;
1939
    }
1940
    else {
1941 1
        if (parse_datetime_extended_unit_from_string(str, len,
1942
                                                NULL, out_meta) < 0) {
1943 0
            Py_DECREF(utf8);
1944
            return -1;
1945
        }
1946

1947 1
        Py_DECREF(utf8);
1948
        return 0;
1949
    }
1950
}
1951

1952
/*
1953
 * 'ret' is a PyUString containing the datetime string, and this
1954
 * function appends the metadata string to it.
1955
 *
1956
 * If 'skip_brackets' is true, skips the '[]'.
1957
 *
1958
 * This function steals the reference 'ret'
1959
 */
1960
NPY_NO_EXPORT PyObject *
1961 1
append_metastr_to_string(PyArray_DatetimeMetaData *meta,
1962
                                    int skip_brackets,
1963
                                    PyObject *ret)
1964
{
1965
    PyObject *res;
1966
    int num;
1967
    char const *basestr;
1968

1969 1
    if (ret == NULL) {
1970
        return NULL;
1971
    }
1972

1973 1
    if (meta->base == NPY_FR_GENERIC) {
1974
        /* Without brackets, give a string "generic" */
1975 1
        if (skip_brackets) {
1976 0
            PyUString_ConcatAndDel(&ret, PyUnicode_FromString("generic"));
1977 0
            return ret;
1978
        }
1979
        /* But with brackets, append nothing */
1980
        else {
1981
            return ret;
1982
        }
1983
    }
1984

1985 1
    num = meta->num;
1986 1
    if (meta->base >= 0 && meta->base < NPY_DATETIME_NUMUNITS) {
1987 1
        basestr = _datetime_strings[meta->base];
1988
    }
1989
    else {
1990 0
        PyErr_SetString(PyExc_RuntimeError,
1991
                "NumPy datetime metadata is corrupted");
1992 0
        return NULL;
1993
    }
1994

1995 1
    if (num == 1) {
1996 1
        if (skip_brackets) {
1997 1
            res = PyUnicode_FromFormat("%s", basestr);
1998
        }
1999
        else {
2000 1
            res = PyUnicode_FromFormat("[%s]", basestr);
2001
        }
2002
    }
2003
    else {
2004 1
        if (skip_brackets) {
2005 1
            res = PyUnicode_FromFormat("%d%s", num, basestr);
2006
        }
2007
        else {
2008 1
            res = PyUnicode_FromFormat("[%d%s]", num, basestr);
2009
        }
2010
    }
2011

2012 1
    PyUString_ConcatAndDel(&ret, res);
2013 1
    return ret;
2014
}
2015

2016
/*
2017
 * Adjusts a datetimestruct based on a seconds offset. Assumes
2018
 * the current values are valid.
2019
 */
2020
NPY_NO_EXPORT void
2021 0
add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds)
2022
{
2023
    int minutes;
2024

2025 0
    dts->sec += seconds;
2026 0
    minutes = extract_unit_32(&dts->sec, 60);
2027 0
    add_minutes_to_datetimestruct(dts, minutes);
2028
}
2029

2030
/*
2031
 * Adjusts a datetimestruct based on a minutes offset. Assumes
2032
 * the current values are valid.
2033
 */
2034
NPY_NO_EXPORT void
2035 1
add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
2036
{
2037
    int isleap;
2038

2039 1
    dts->min += minutes;
2040

2041
    /* propagate invalid minutes into hour and day changes */
2042 1
    dts->hour += extract_unit_32(&dts->min,  60);
2043 1
    dts->day  += extract_unit_32(&dts->hour, 24);
2044

2045
    /* propagate invalid days into month and year changes */
2046 1
    if (dts->day < 1) {
2047 1
        dts->month--;
2048 1
        if (dts->month < 1) {
2049 1
            dts->year--;
2050 1
            dts->month = 12;
2051
        }
2052 1
        isleap = is_leapyear(dts->year);
2053 1
        dts->day += _days_per_month_table[isleap][dts->month-1];
2054
    }
2055 1
    else if (dts->day > 28) {
2056 1
        isleap = is_leapyear(dts->year);
2057 1
        if (dts->day > _days_per_month_table[isleap][dts->month-1]) {
2058 0
            dts->day -= _days_per_month_table[isleap][dts->month-1];
2059 0
            dts->month++;
2060 0
            if (dts->month > 12) {
2061 0
                dts->year++;
2062 0
                dts->month = 1;
2063
            }
2064
        }
2065
    }
2066
}
2067

2068
/*
2069
 * Tests for and converts a Python datetime.datetime or datetime.date
2070
 * object into a NumPy npy_datetimestruct.
2071
 *
2072
 * While the C API has PyDate_* and PyDateTime_* functions, the following
2073
 * implementation just asks for attributes, and thus supports
2074
 * datetime duck typing. The tzinfo time zone conversion would require
2075
 * this style of access anyway.
2076
 *
2077
 * 'out_bestunit' gives a suggested unit based on whether the object
2078
 *      was a datetime.date or datetime.datetime object.
2079
 *
2080
 * If 'apply_tzinfo' is 1, this function uses the tzinfo to convert
2081
 * to UTC time, otherwise it returns the struct with the local time.
2082
 *
2083
 * Returns -1 on error, 0 on success, and 1 (with no error set)
2084
 * if obj doesn't have the needed date or datetime attributes.
2085
 */
2086
NPY_NO_EXPORT int
2087 1
convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out,
2088
                                     NPY_DATETIMEUNIT *out_bestunit,
2089
                                     int apply_tzinfo)
2090
{
2091
    PyObject *tmp;
2092
    int isleap;
2093

2094
    /* Initialize the output to all zeros */
2095 1
    memset(out, 0, sizeof(npy_datetimestruct));
2096 1
    out->month = 1;
2097 1
    out->day = 1;
2098

2099
    /* Need at least year/month/day attributes */
2100 1
    if (!PyObject_HasAttrString(obj, "year") ||
2101 1
            !PyObject_HasAttrString(obj, "month") ||
2102 1
            !PyObject_HasAttrString(obj, "day")) {
2103
        return 1;
2104
    }
2105

2106
    /* Get the year */
2107 1
    tmp = PyObject_GetAttrString(obj, "year");
2108 1
    if (tmp == NULL) {
2109
        return -1;
2110
    }
2111 1
    out->year = PyLong_AsLong(tmp);
2112 1
    if (error_converting(out->year)) {
2113 0
        Py_DECREF(tmp);
2114
        return -1;
2115
    }
2116 1
    Py_DECREF(tmp);
2117

2118
    /* Get the month */
2119 1
    tmp = PyObject_GetAttrString(obj, "month");
2120 1
    if (tmp == NULL) {
2121
        return -1;
2122
    }
2123 1
    out->month = PyLong_AsLong(tmp);
2124 1
    if (error_converting(out->month)) {
2125 0
        Py_DECREF(tmp);
2126
        return -1;
2127
    }
2128 1
    Py_DECREF(tmp);
2129

2130
    /* Get the day */
2131 1
    tmp = PyObject_GetAttrString(obj, "day");
2132 1
    if (tmp == NULL) {
2133
        return -1;
2134
    }
2135 1
    out->day = PyLong_AsLong(tmp);
2136 1
    if (error_converting(out->day)) {
2137 0
        Py_DECREF(tmp);
2138
        return -1;
2139
    }
2140 1
    Py_DECREF(tmp);
2141

2142
    /* Validate that the month and day are valid for the year */
2143 1
    if (out->month < 1 || out->month > 12) {
2144
        goto invalid_date;
2145
    }
2146 1
    isleap = is_leapyear(out->year);
2147 1
    if (out->day < 1 ||
2148 1
                out->day > _days_per_month_table[isleap][out->month-1]) {
2149
        goto invalid_date;
2150
    }
2151

2152
    /* Check for time attributes (if not there, return success as a date) */
2153 1
    if (!PyObject_HasAttrString(obj, "hour") ||
2154 1
            !PyObject_HasAttrString(obj, "minute") ||
2155 1
            !PyObject_HasAttrString(obj, "second") ||
2156 1
            !PyObject_HasAttrString(obj, "microsecond")) {
2157
        /* The best unit for date is 'D' */
2158 1
        if (out_bestunit != NULL) {
2159 1
            *out_bestunit = NPY_FR_D;
2160
        }
2161
        return 0;
2162
    }
2163

2164
    /* Get the hour */
2165 1
    tmp = PyObject_GetAttrString(obj, "hour");
2166 1
    if (tmp == NULL) {
2167
        return -1;
2168
    }
2169 1
    out->hour = PyLong_AsLong(tmp);
2170 1
    if (error_converting(out->hour)) {
2171 0
        Py_DECREF(tmp);
2172
        return -1;
2173
    }
2174 1
    Py_DECREF(tmp);
2175

2176
    /* Get the minute */
2177 1
    tmp = PyObject_GetAttrString(obj, "minute");
2178 1
    if (tmp == NULL) {
2179
        return -1;
2180
    }
2181 1
    out->min = PyLong_AsLong(tmp);
2182 1
    if (error_converting(out->min)) {
2183 0
        Py_DECREF(tmp);
2184
        return -1;
2185
    }
2186 1
    Py_DECREF(tmp);
2187

2188
    /* Get the second */
2189 1
    tmp = PyObject_GetAttrString(obj, "second");
2190 1
    if (tmp == NULL) {
2191
        return -1;
2192
    }
2193 1
    out->sec = PyLong_AsLong(tmp);
2194 1
    if (error_converting(out->sec)) {
2195 0
        Py_DECREF(tmp);
2196
        return -1;
2197
    }
2198 1
    Py_DECREF(tmp);
2199

2200
    /* Get the microsecond */
2201 1
    tmp = PyObject_GetAttrString(obj, "microsecond");
2202 1
    if (tmp == NULL) {
2203
        return -1;
2204
    }
2205 1
    out->us = PyLong_AsLong(tmp);
2206 1
    if (error_converting(out->us)) {
2207 0
        Py_DECREF(tmp);
2208
        return -1;
2209
    }
2210 1
    Py_DECREF(tmp);
2211

2212 1
    if (out->hour < 0 || out->hour >= 24 ||
2213 1
            out->min < 0 || out->min >= 60 ||
2214 1
            out->sec < 0 || out->sec >= 60 ||
2215 1
            out->us < 0 || out->us >= 1000000) {
2216
        goto invalid_time;
2217
    }
2218

2219
    /* Apply the time zone offset if it exists */
2220 1
    if (apply_tzinfo && PyObject_HasAttrString(obj, "tzinfo")) {
2221 1
        tmp = PyObject_GetAttrString(obj, "tzinfo");
2222 1
        if (tmp == NULL) {
2223
            return -1;
2224
        }
2225 1
        if (tmp == Py_None) {
2226 1
            Py_DECREF(tmp);
2227
        }
2228
        else {
2229
            PyObject *offset;
2230
            int seconds_offset, minutes_offset;
2231

2232
            /* 2016-01-14, 1.11 */
2233 1
            PyErr_Clear();
2234 1
            if (DEPRECATE(
2235
                    "parsing timezone aware datetimes is deprecated; "
2236
                    "this will raise an error in the future") < 0) {
2237 1
                Py_DECREF(tmp);
2238
                return -1;
2239
            }
2240

2241
            /* The utcoffset function should return a timedelta */
2242 1
            offset = PyObject_CallMethod(tmp, "utcoffset", "O", obj);
2243 1
            if (offset == NULL) {
2244 0
                Py_DECREF(tmp);
2245
                return -1;
2246
            }
2247 1
            Py_DECREF(tmp);
2248

2249
            /*
2250
             * The timedelta should have a function "total_seconds"
2251
             * which contains the value we want.
2252
             */
2253 1
            tmp = PyObject_CallMethod(offset, "total_seconds", "");
2254 1
            Py_DECREF(offset);
2255 1
            if (tmp == NULL) {
2256
                return -1;
2257
            }
2258
            /* Rounding here is no worse than the integer division below.
2259
             * Only whole minute offsets are supported by numpy anyway.
2260
             */
2261 1
            seconds_offset = (int)PyFloat_AsDouble(tmp);
2262 1
            if (error_converting(seconds_offset)) {
2263 0
                Py_DECREF(tmp);
2264
                return -1;
2265
            }
2266 1
            Py_DECREF(tmp);
2267

2268
            /* Convert to a minutes offset and apply it */
2269 1
            minutes_offset = seconds_offset / 60;
2270

2271 1
            add_minutes_to_datetimestruct(out, -minutes_offset);
2272
        }
2273
    }
2274

2275
    /* The resolution of Python's datetime is 'us' */
2276 1
    if (out_bestunit != NULL) {
2277 1
        *out_bestunit = NPY_FR_us;
2278
    }
2279

2280
    return 0;
2281

2282 0
invalid_date:
2283 0
    PyErr_Format(PyExc_ValueError,
2284
            "Invalid date (%" NPY_INT64_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ") when converting to NumPy datetime",
2285
            out->year, out->month, out->day);
2286 0
    return -1;
2287

2288 0
invalid_time:
2289 0
    PyErr_Format(PyExc_ValueError,
2290
            "Invalid time (%" NPY_INT32_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ") when converting "
2291
            "to NumPy datetime",
2292
            out->hour, out->min, out->sec, out->us);
2293 0
    return -1;
2294
}
2295

2296
/*
2297
 * Gets a tzoffset in minutes by calling the fromutc() function on
2298
 * the Python datetime.tzinfo object.
2299
 */
2300
NPY_NO_EXPORT int
2301 1
get_tzoffset_from_pytzinfo(PyObject *timezone_obj, npy_datetimestruct *dts)
2302
{
2303
    PyObject *dt, *loc_dt;
2304
    npy_datetimestruct loc_dts;
2305

2306
    /* Create a Python datetime to give to the timezone object */
2307 1
    dt = PyDateTime_FromDateAndTime((int)dts->year, dts->month, dts->day,
2308
                            dts->hour, dts->min, 0, 0);
2309 1
    if (dt == NULL) {
2310
        return -1;
2311
    }
2312

2313
    /* Convert the datetime from UTC to local time */
2314 1
    loc_dt = PyObject_CallMethod(timezone_obj, "fromutc", "O", dt);
2315 1
    Py_DECREF(dt);
2316 1
    if (loc_dt == NULL) {
2317
        return -1;
2318
    }
2319

2320
    /* Convert the local datetime into a datetimestruct */
2321 1
    if (convert_pydatetime_to_datetimestruct(loc_dt, &loc_dts, NULL, 0) < 0) {
2322 0
        Py_DECREF(loc_dt);
2323
        return -1;
2324
    }
2325

2326 1
    Py_DECREF(loc_dt);
2327

2328
    /* Calculate the tzoffset as the difference between the datetimes */
2329 1
    return (int)(get_datetimestruct_minutes(&loc_dts) -
2330 1
                 get_datetimestruct_minutes(dts));
2331
}
2332

2333
/*
2334
 * Converts a PyObject * into a datetime, in any of the forms supported.
2335
 *
2336
 * If the units metadata isn't known ahead of time, set meta->base
2337
 * to -1, and this function will populate meta with either default
2338
 * values or values from the input object.
2339
 *
2340
 * The 'casting' parameter is used to control what kinds of inputs
2341
 * are accepted, and what happens. For example, with 'unsafe' casting,
2342
 * unrecognized inputs are converted to 'NaT' instead of throwing an error,
2343
 * while with 'safe' casting an error will be thrown if any precision
2344
 * from the input will be thrown away.
2345
 *
2346
 * Returns -1 on error, 0 on success.
2347
 */
2348
NPY_NO_EXPORT int
2349 1
convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
2350
                                NPY_CASTING casting, npy_datetime *out)
2351
{
2352 1
    if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
2353 1
        PyObject *utf8 = NULL;
2354

2355
        /* Convert to an UTF8 string for the date parser */
2356 1
        if (PyBytes_Check(obj)) {
2357 1
            utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL);
2358 1
            if (utf8 == NULL) {
2359
                return -1;
2360
            }
2361
        }
2362
        else {
2363 1
            utf8 = obj;
2364 1
            Py_INCREF(utf8);
2365
        }
2366

2367 1
        Py_ssize_t len = 0;
2368 1
        char const *str = PyUnicode_AsUTF8AndSize(utf8, &len);
2369 1
        if (str == NULL) {
2370 0
            Py_DECREF(utf8);
2371
            return -1;
2372
        }
2373

2374
        /* Parse the ISO date */
2375
        npy_datetimestruct dts;
2376 1
        NPY_DATETIMEUNIT bestunit = NPY_FR_ERROR;
2377 1
        if (parse_iso_8601_datetime(str, len, meta->base, casting,
2378
                                &dts, &bestunit, NULL) < 0) {
2379 1
            Py_DECREF(utf8);
2380
            return -1;
2381
        }
2382

2383
        /* Use the detected unit if none was specified */
2384 1
        if (meta->base == NPY_FR_ERROR) {
2385 1
            meta->base = bestunit;
2386 1
            meta->num = 1;
2387
        }
2388

2389 1
        if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) {
2390 0
            Py_DECREF(utf8);
2391
            return -1;
2392
        }
2393

2394 1
        Py_DECREF(utf8);
2395
        return 0;
2396
    }
2397
    /* Do no conversion on raw integers */
2398 1
    else if (PyLong_Check(obj)) {
2399
        /* Don't allow conversion from an integer without specifying a unit */
2400 1
        if (meta->base == NPY_FR_ERROR || meta->base == NPY_FR_GENERIC) {
2401 1
            PyErr_SetString(PyExc_ValueError, "Converting an integer to a "
2402
                            "NumPy datetime requires a specified unit");
2403 1
            return -1;
2404
        }
2405 1
        *out = PyLong_AsLongLong(obj);
2406 1
        if (error_converting(*out)) {
2407
            return -1;
2408
        }
2409
        return 0;
2410
    }
2411
    /* Datetime scalar */
2412 1
    else if (PyArray_IsScalar(obj, Datetime)) {
2413 1
        PyDatetimeScalarObject *dts = (PyDatetimeScalarObject *)obj;
2414

2415
        /* Copy the scalar directly if units weren't specified */
2416 1
        if (meta->base == NPY_FR_ERROR) {
2417 1
            *meta = dts->obmeta;
2418 1
            *out = dts->obval;
2419

2420 1
            return 0;
2421
        }
2422
        /* Otherwise do a casting transformation */
2423
        else {
2424
            /* Allow NaT (not-a-time) values to slip through any rule */
2425 1
            if (dts->obval != NPY_DATETIME_NAT &&
2426 1
                        raise_if_datetime64_metadata_cast_error(
2427
                                "NumPy timedelta64 scalar",
2428
                                &dts->obmeta, meta, casting) < 0) {
2429
                return -1;
2430
            }
2431
            else {
2432 1
                return cast_datetime_to_datetime(&dts->obmeta, meta,
2433
                                                    dts->obval, out);
2434
            }
2435
        }
2436
    }
2437
    /* Datetime zero-dimensional array */
2438 1
    else if (PyArray_Check(obj) &&
2439 1
              PyArray_NDIM((PyArrayObject *)obj) == 0 &&
2440 1
              PyArray_DESCR((PyArrayObject *)obj)->type_num == NPY_DATETIME) {
2441 1
        PyArrayObject *arr = (PyArrayObject *)obj;
2442
        PyArray_DatetimeMetaData *arr_meta;
2443 1
        npy_datetime dt = 0;
2444

2445 1
        arr_meta = get_datetime_metadata_from_dtype(PyArray_DESCR(arr));
2446 1
        if (arr_meta == NULL) {
2447
            return -1;
2448
        }
2449 1
        PyArray_DESCR(arr)->f->copyswap(&dt,
2450
                                PyArray_DATA(arr),
2451 1
                                PyArray_ISBYTESWAPPED(arr),
2452
                                obj);
2453

2454
        /* Copy the value directly if units weren't specified */
2455 1
        if (meta->base == NPY_FR_ERROR) {
2456 1
            *meta = *arr_meta;
2457 1
            *out = dt;
2458

2459 1
            return 0;
2460
        }
2461
        /* Otherwise do a casting transformation */
2462
        else {
2463
            /* Allow NaT (not-a-time) values to slip through any rule */
2464 1
            if (dt != NPY_DATETIME_NAT &&
2465 1
                        raise_if_datetime64_metadata_cast_error(
2466
                                "NumPy timedelta64 scalar",
2467
                                arr_meta, meta, casting) < 0) {
2468
                return -1;
2469
            }
2470
            else {
2471 1
                return cast_datetime_to_datetime(arr_meta, meta, dt, out);
2472
            }
2473
        }
2474
    }
2475
    /* Convert from a Python date or datetime object */
2476
    else {
2477
        int code;
2478
        npy_datetimestruct dts;
2479 1
        NPY_DATETIMEUNIT bestunit = NPY_FR_ERROR;
2480

2481 1
        code = convert_pydatetime_to_datetimestruct(obj, &dts, &bestunit, 1);
2482 1
        if (code == -1) {
2483 1
            return -1;
2484
        }
2485 1
        else if (code == 0) {
2486
            /* Use the detected unit if none was specified */
2487 1
            if (meta->base == NPY_FR_ERROR) {
2488 1
                meta->base = bestunit;
2489 1
                meta->num = 1;
2490
            }
2491
            else {
2492
                PyArray_DatetimeMetaData obj_meta;
2493 1
                obj_meta.base = bestunit;
2494 1
                obj_meta.num = 1;
2495

2496 1
                if (raise_if_datetime64_metadata_cast_error(
2497
                                bestunit == NPY_FR_D ? "datetime.date object"
2498
                                                 : "datetime.datetime object",
2499
                                &obj_meta, meta, casting) < 0) {
2500 0
                    return -1;
2501
                }
2502
            }
2503

2504 1
            return convert_datetimestruct_to_datetime(meta, &dts, out);
2505
        }
2506
    }
2507

2508
    /*
2509
     * With unsafe casting, convert unrecognized objects into NaT
2510
     * and with same_kind casting, convert None into NaT
2511
     */
2512 1
    if (casting == NPY_UNSAFE_CASTING ||
2513 1
            (obj == Py_None && casting == NPY_SAME_KIND_CASTING)) {
2514 1
        if (meta->base == NPY_FR_ERROR) {
2515 1
            meta->base = NPY_FR_GENERIC;
2516 1
            meta->num = 1;
2517
        }
2518 1
        *out = NPY_DATETIME_NAT;
2519 1
        return 0;
2520
    }
2521
    else {
2522 1
        PyErr_SetString(PyExc_ValueError,
2523
                "Could not convert object to NumPy datetime");
2524 1
        return -1;
2525
    }
2526
}
2527

2528
/*
2529
 * Converts a PyObject * into a timedelta, in any of the forms supported
2530
 *
2531
 * If the units metadata isn't known ahead of time, set meta->base
2532
 * to -1, and this function will populate meta with either default
2533
 * values or values from the input object.
2534
 *
2535
 * The 'casting' parameter is used to control what kinds of inputs
2536
 * are accepted, and what happens. For example, with 'unsafe' casting,
2537
 * unrecognized inputs are converted to 'NaT' instead of throwing an error,
2538
 * while with 'safe' casting an error will be thrown if any precision
2539
 * from the input will be thrown away.
2540
 *
2541
 * Returns -1 on error, 0 on success.
2542
 */
2543
NPY_NO_EXPORT int
2544 1
convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
2545
                                NPY_CASTING casting, npy_timedelta *out)
2546
{
2547 1
    if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
2548 1
        PyObject *utf8 = NULL;
2549 1
        int succeeded = 0;
2550

2551
        /* Convert to an UTF8 string for the date parser */
2552 1
        if (PyBytes_Check(obj)) {
2553 1
            utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL);
2554 1
            if (utf8 == NULL) {
2555 1
                return -1;
2556
            }
2557
        }
2558
        else {
2559 1
            utf8 = obj;
2560 1
            Py_INCREF(utf8);
2561
        }
2562

2563 1
        Py_ssize_t len = 0;
2564 1
        char const *str = PyUnicode_AsUTF8AndSize(utf8, &len);
2565 1
        if (str == NULL) {
2566 0
            Py_DECREF(utf8);
2567
            return -1;
2568
        }
2569

2570
        /* Check for a NaT string */
2571 1
        if (len <= 0 || (len == 3 &&
2572 1
                        tolower(str[0]) == 'n' &&
2573 1
                        tolower(str[1]) == 'a' &&
2574 1
                        tolower(str[2]) == 't')) {
2575 1
            *out = NPY_DATETIME_NAT;
2576 1
            succeeded = 1;
2577
        }
2578
        /* Parse as an integer */
2579
        else {
2580 1
            char *strend = NULL;
2581

2582 1
            *out = strtol(str, &strend, 10);
2583 1
            if (strend - str == len) {
2584 1
                succeeded = 1;
2585
            }
2586
        }
2587 1
        Py_DECREF(utf8);
2588

2589 1
        if (succeeded) {
2590
            /* Use generic units if none was specified */
2591 1
            if (meta->base == NPY_FR_ERROR) {
2592 1
                meta->base = NPY_FR_GENERIC;
2593 1
                meta->num = 1;
2594
            }
2595

2596
            return 0;
2597
        }
2598
    }
2599
    /* Do no conversion on raw integers */
2600 1
    else if (PyLong_Check(obj)) {
2601
        /* Use the default unit if none was specified */
2602 1
        if (meta->base == NPY_FR_ERROR) {
2603 1
            meta->base = NPY_DATETIME_DEFAULTUNIT;
2604 1
            meta->num = 1;
2605
        }
2606

2607 1
        *out = PyLong_AsLongLong(obj);
2608 1
        if (error_converting(*out)) {
2609
            return -1;
2610
        }
2611
        return 0;
2612
    }
2613
    /* Timedelta scalar */
2614 1
    else if (PyArray_IsScalar(obj, Timedelta)) {
2615 1
        PyTimedeltaScalarObject *dts = (PyTimedeltaScalarObject *)obj;
2616

2617
        /* Copy the scalar directly if units weren't specified */
2618 1
        if (meta->base == NPY_FR_ERROR) {
2619 1
            *meta = dts->obmeta;
2620 1
            *out = dts->obval;
2621

2622 1
            return 0;
2623
        }
2624
        /* Otherwise do a casting transformation */
2625
        else {
2626
            /* Allow NaT (not-a-time) values to slip through any rule */
2627 1
            if (dts->obval != NPY_DATETIME_NAT &&
2628 1
                        raise_if_timedelta64_metadata_cast_error(
2629
                                "NumPy timedelta64 scalar",
2630
                                &dts->obmeta, meta, casting) < 0) {
2631
                return -1;
2632
            }
2633
            else {
2634 1
                return cast_timedelta_to_timedelta(&dts->obmeta, meta,
2635
                                                    dts->obval, out);
2636
            }
2637
        }
2638
    }
2639
    /* Timedelta zero-dimensional array */
2640 1
    else if (PyArray_Check(obj) &&
2641 1
             PyArray_NDIM((PyArrayObject *)obj) == 0 &&
2642 1
             PyArray_DESCR((PyArrayObject *)obj)->type_num == NPY_TIMEDELTA) {
2643 1
        PyArrayObject *arr = (PyArrayObject *)obj;
2644
        PyArray_DatetimeMetaData *arr_meta;
2645 1
        npy_timedelta dt = 0;
2646

2647 1
        arr_meta = get_datetime_metadata_from_dtype(PyArray_DESCR(arr));
2648 1
        if (arr_meta == NULL) {
2649
            return -1;
2650
        }
2651 1
        PyArray_DESCR(arr)->f->copyswap(&dt,
2652
                                PyArray_DATA(arr),
2653 1
                                PyArray_ISBYTESWAPPED(arr),
2654
                                obj);
2655

2656
        /* Copy the value directly if units weren't specified */
2657 1
        if (meta->base == NPY_FR_ERROR) {
2658 1
            *meta = *arr_meta;
2659 1
            *out = dt;
2660

2661 1
            return 0;
2662
        }
2663
        /* Otherwise do a casting transformation */
2664
        else {
2665
            /* Allow NaT (not-a-time) values to slip through any rule */
2666 1
            if (dt != NPY_DATETIME_NAT &&
2667 1
                        raise_if_timedelta64_metadata_cast_error(
2668
                                "NumPy timedelta64 scalar",
2669
                                arr_meta, meta, casting) < 0) {
2670
                return -1;
2671
            }
2672
            else {
2673 1
                return cast_timedelta_to_timedelta(arr_meta, meta, dt, out);
2674
            }
2675
        }
2676
    }
2677
    /* Convert from a Python timedelta object */
2678 1
    else if (PyObject_HasAttrString(obj, "days") &&
2679 1
                PyObject_HasAttrString(obj, "seconds") &&
2680 1
                PyObject_HasAttrString(obj, "microseconds")) {
2681
        PyObject *tmp;
2682
        PyArray_DatetimeMetaData us_meta;
2683
        npy_timedelta td;
2684
        npy_int64 days;
2685 1
        int seconds = 0, useconds = 0;
2686

2687
        /* Get the days */
2688 1
        tmp = PyObject_GetAttrString(obj, "days");
2689 1
        if (tmp == NULL) {
2690
            return -1;
2691
        }
2692 1
        days = PyLong_AsLongLong(tmp);
2693 1
        if (error_converting(days)) {
2694 0
            Py_DECREF(tmp);
2695
            return -1;
2696
        }
2697 1
        Py_DECREF(tmp);
2698

2699
        /* Get the seconds */
2700 1
        tmp = PyObject_GetAttrString(obj, "seconds");
2701 1
        if (tmp == NULL) {
2702
            return -1;
2703
        }
2704 1
        seconds = PyLong_AsLong(tmp);
2705 1
        if (error_converting(seconds)) {
2706 0
            Py_DECREF(tmp);
2707
            return -1;
2708
        }
2709 1
        Py_DECREF(tmp);
2710

2711
        /* Get the microseconds */
2712 1
        tmp = PyObject_GetAttrString(obj, "microseconds");
2713 1
        if (tmp == NULL) {
2714
            return -1;
2715
        }
2716 1
        useconds = PyLong_AsLong(tmp);
2717 1
        if (error_converting(useconds)) {
2718 0
            Py_DECREF(tmp);
2719
            return -1;
2720
        }
2721 1
        Py_DECREF(tmp);
2722

2723 1
        td = days*(24*60*60*1000000LL) + seconds*1000000LL + useconds;
2724

2725
        /* Use microseconds if none was specified */
2726 1
        if (meta->base == NPY_FR_ERROR) {
2727 1
            meta->base = NPY_FR_us;
2728 1
            meta->num = 1;
2729

2730 1
            *out = td;
2731

2732 1
            return 0;
2733
        }
2734
        else {
2735
            /*
2736
             * Detect the largest unit where every value after is zero,
2737
             * to allow safe casting to seconds if microseconds is zero,
2738
             * for instance.
2739
             */
2740 1
            if (td % 1000LL != 0) {
2741 0
                us_meta.base = NPY_FR_us;
2742
            }
2743 1
            else if (td % 1000000LL != 0) {
2744 0
                us_meta.base = NPY_FR_ms;
2745
            }
2746 1
            else if (td % (60*1000000LL) != 0) {
2747 1
                us_meta.base = NPY_FR_s;
2748
            }
2749 1
            else if (td % (60*60*1000000LL) != 0) {
2750 0
                us_meta.base = NPY_FR_m;
2751
            }
2752 1
            else if (td % (24*60*60*1000000LL) != 0) {
2753 0
                us_meta.base = NPY_FR_h;
2754
            }
2755 1
            else if (td % (7*24*60*60*1000000LL) != 0) {
2756 1
                us_meta.base = NPY_FR_D;
2757
            }
2758
            else {
2759 1
                us_meta.base = NPY_FR_W;
2760
            }
2761 1
            us_meta.num = 1;
2762

2763 1
            if (raise_if_timedelta64_metadata_cast_error(
2764
                                "datetime.timedelta object",
2765
                                &us_meta, meta, casting) < 0) {
2766
                return -1;
2767
            }
2768
            else {
2769
                /* Switch back to microseconds for the casting operation */
2770 1
                us_meta.base = NPY_FR_us;
2771

2772 1
                return cast_timedelta_to_timedelta(&us_meta, meta, td, out);
2773
            }
2774
        }
2775
    }
2776

2777
    /*
2778
     * With unsafe casting, convert unrecognized objects into NaT
2779
     * and with same_kind casting, convert None into NaT
2780
     */
2781 1
    if (casting == NPY_UNSAFE_CASTING ||
2782 1
            (obj == Py_None && casting == NPY_SAME_KIND_CASTING)) {
2783 1
        if (meta->base == NPY_FR_ERROR) {
2784 1
            meta->base = NPY_FR_GENERIC;
2785 1
            meta->num = 1;
2786
        }
2787 1
        *out = NPY_DATETIME_NAT;
2788 1
        return 0;
2789
    }
2790 1
    else if (PyArray_IsScalar(obj, Integer)) {
2791
        /* Use the default unit if none was specified */
2792 1
        if (meta->base == NPY_FR_ERROR) {
2793 1
            meta->base = NPY_DATETIME_DEFAULTUNIT;
2794 1
            meta->num = 1;
2795
        }
2796

2797 1
        *out = PyLong_AsLongLong(obj);
2798 1
        if (error_converting(*out)) {
2799
            return -1;
2800
        }
2801
        return 0;
2802
    }
2803
    else {
2804 1
        PyErr_SetString(PyExc_ValueError,
2805
                "Could not convert object to NumPy timedelta");
2806 1
        return -1;
2807
    }
2808
}
2809

2810
/*
2811
 * Converts a datetime into a PyObject *.
2812
 *
2813
 * Not-a-time is returned as the string "NaT".
2814
 * For days or coarser, returns a datetime.date.
2815
 * For microseconds or coarser, returns a datetime.datetime.
2816
 * For units finer than microseconds, returns an integer.
2817
 */
2818
NPY_NO_EXPORT PyObject *
2819 1
convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta)
2820
{
2821 1
    PyObject *ret = NULL;
2822
    npy_datetimestruct dts;
2823

2824
    /*
2825
     * Convert NaT (not-a-time) and any value with generic units
2826
     * into None.
2827
     */
2828 1
    if (dt == NPY_DATETIME_NAT || meta->base == NPY_FR_GENERIC) {
2829 1
        Py_RETURN_NONE;
2830
    }
2831

2832
    /* If the type's precision is greater than microseconds, return an int */
2833 1
    if (meta->base > NPY_FR_us) {
2834 1
        return PyLong_FromLongLong(dt);
2835
    }
2836

2837
    /* Convert to a datetimestruct */
2838 1
    if (convert_datetime_to_datetimestruct(meta, dt, &dts) < 0) {
2839
        return NULL;
2840
    }
2841

2842
    /*
2843
     * If the year is outside the range of years supported by Python's
2844
     * datetime, or the datetime64 falls on a leap second,
2845
     * return a raw int.
2846
     */
2847 1
    if (dts.year < 1 || dts.year > 9999 || dts.sec == 60) {
2848 1
        return PyLong_FromLongLong(dt);
2849
    }
2850

2851
    /* If the type's precision is greater than days, return a datetime */
2852 1
    if (meta->base > NPY_FR_D) {
2853 1
        ret = PyDateTime_FromDateAndTime(dts.year, dts.month, dts.day,
2854
                                dts.hour, dts.min, dts.sec, dts.us);
2855
    }
2856
    /* Otherwise return a date */
2857
    else {
2858 1
        ret = PyDate_FromDate(dts.year, dts.month, dts.day);
2859
    }
2860

2861
    return ret;
2862
}
2863

2864
/*
2865
 * Converts a timedelta into a PyObject *.
2866
 *
2867
 * Not-a-time is returned as the string "NaT".
2868
 * For microseconds or coarser, returns a datetime.timedelta.
2869
 * For units finer than microseconds, returns an integer.
2870
 */
2871
NPY_NO_EXPORT PyObject *
2872 0
convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
2873
{
2874
    npy_timedelta value;
2875 0
    int days = 0, seconds = 0, useconds = 0;
2876

2877
    /*
2878
     * Convert NaT (not-a-time) into None.
2879
     */
2880 0
    if (td == NPY_DATETIME_NAT) {
2881 0
        Py_RETURN_NONE;
2882
    }
2883

2884
    /*
2885
     * If the type's precision is greater than microseconds, is
2886
     * Y/M/B (nonlinear units), or is generic units, return an int
2887
     */
2888 0
    if (meta->base > NPY_FR_us ||
2889 0
                    meta->base == NPY_FR_Y ||
2890 0
                    meta->base == NPY_FR_M ||
2891
                    meta->base == NPY_FR_GENERIC) {
2892 0
        return PyLong_FromLongLong(td);
2893
    }
2894

2895 0
    value = td;
2896

2897
    /* Apply the unit multiplier (TODO: overflow treatment...) */
2898 0
    value *= meta->num;
2899

2900
    /* Convert to days/seconds/useconds */
2901 0
    switch (meta->base) {
2902 0
        case NPY_FR_W:
2903 0
            days = value * 7;
2904 0
            break;
2905 0
        case NPY_FR_D:
2906 0
            days = value;
2907 0
            break;
2908 0
        case NPY_FR_h:
2909 0
            days = extract_unit_64(&value, 24ULL);
2910 0
            seconds = value*60*60;
2911 0
            break;
2912 0
        case NPY_FR_m:
2913 0
            days = extract_unit_64(&value, 60ULL*24);
2914 0
            seconds = value*60;
2915 0
            break;
2916 0
        case NPY_FR_s:
2917 0
            days = extract_unit_64(&value, 60ULL*60*24);
2918 0
            seconds = value;
2919 0
            break;
2920 0
        case NPY_FR_ms:
2921 0
            days     = extract_unit_64(&value, 1000ULL*60*60*24);
2922 0
            seconds  = extract_unit_64(&value, 1000ULL);
2923 0
            useconds = value*1000;
2924 0
            break;
2925 0
        case NPY_FR_us:
2926 0
            days     = extract_unit_64(&value, 1000ULL*1000*60*60*24);
2927 0
            seconds  = extract_unit_64(&value, 1000ULL*1000);
2928 0
            useconds = value;
2929 0
            break;
2930
        default:
2931
            // unreachable, handled by the `if` above
2932
            assert(NPY_FALSE);
2933
            break;
2934
    }
2935
    /*
2936
     * If it would overflow the datetime.timedelta days, return a raw int
2937
     */
2938 0
    if (days < -999999999 || days > 999999999) {
2939 0
        return PyLong_FromLongLong(td);
2940
    }
2941
    else {
2942 0
        return PyDelta_FromDSU(days, seconds, useconds);
2943
    }
2944
}
2945

2946
/*
2947
 * Returns true if the datetime metadata matches
2948
 */
2949
NPY_NO_EXPORT npy_bool
2950 1
has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2)
2951
{
2952
    PyArray_DatetimeMetaData *meta1, *meta2;
2953

2954 1
    if ((type1->type_num != NPY_DATETIME &&
2955 1
                        type1->type_num != NPY_TIMEDELTA) ||
2956 1
                    (type2->type_num != NPY_DATETIME &&
2957
                        type2->type_num != NPY_TIMEDELTA)) {
2958
        return 0;
2959
    }
2960

2961 1
    meta1 = get_datetime_metadata_from_dtype(type1);
2962 1
    if (meta1 == NULL) {
2963 0
        PyErr_Clear();
2964 0
        return 0;
2965
    }
2966 1
    meta2 = get_datetime_metadata_from_dtype(type2);
2967 1
    if (meta2 == NULL) {
2968 0
        PyErr_Clear();
2969 0
        return 0;
2970
    }
2971

2972
    /* For generic units, the num is ignored */
2973 1
    if (meta1->base == NPY_FR_GENERIC && meta2->base == NPY_FR_GENERIC) {
2974
        return 1;
2975
    }
2976

2977 1
    return meta1->base == meta2->base &&
2978 1
            meta1->num == meta2->num;
2979
}
2980

2981
/*
2982
 * Casts a single datetime from having src_meta metadata into
2983
 * dst_meta metadata.
2984
 *
2985
 * Returns 0 on success, -1 on failure.
2986
 */
2987
NPY_NO_EXPORT int
2988 1
cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta,
2989
                          PyArray_DatetimeMetaData *dst_meta,
2990
                          npy_datetime src_dt,
2991
                          npy_datetime *dst_dt)
2992
{
2993
    npy_datetimestruct dts;
2994

2995
    /* If the metadata is the same, short-circuit the conversion */
2996 1
    if (src_meta->base == dst_meta->base &&
2997 1
            src_meta->num == dst_meta->num) {
2998 1
        *dst_dt = src_dt;
2999 1
        return 0;
3000
    }
3001

3002
    /* Otherwise convert through a datetimestruct */
3003 1
    if (convert_datetime_to_datetimestruct(src_meta, src_dt, &dts) < 0) {
3004 0
            *dst_dt = NPY_DATETIME_NAT;
3005 0
            return -1;
3006
    }
3007 1
    if (convert_datetimestruct_to_datetime(dst_meta, &dts, dst_dt) < 0) {
3008 0
        *dst_dt = NPY_DATETIME_NAT;
3009 0
        return -1;
3010
    }
3011

3012
    return 0;
3013
}
3014

3015
/*
3016
 * Casts a single timedelta from having src_meta metadata into
3017
 * dst_meta metadata.
3018
 *
3019
 * Returns 0 on success, -1 on failure.
3020
 */
3021
NPY_NO_EXPORT int
3022 1
cast_timedelta_to_timedelta(PyArray_DatetimeMetaData *src_meta,
3023
                          PyArray_DatetimeMetaData *dst_meta,
3024
                          npy_timedelta src_dt,
3025
                          npy_timedelta *dst_dt)
3026
{
3027 1
    npy_int64 num = 0, denom = 0;
3028

3029
    /* If the metadata is the same, short-circuit the conversion */
3030 1
    if (src_meta->base == dst_meta->base &&
3031 1
            src_meta->num == dst_meta->num) {
3032 1
        *dst_dt = src_dt;
3033 1
        return 0;
3034
    }
3035

3036
    /* Get the conversion factor */
3037 1
    get_datetime_conversion_factor(src_meta, dst_meta, &num, &denom);
3038

3039 1
    if (num == 0) {
3040
        return -1;
3041
    }
3042

3043
    /* Apply the scaling */
3044 1
    if (src_dt < 0) {
3045 1
        *dst_dt = (src_dt * num - (denom - 1)) / denom;
3046
    }
3047
    else {
3048 1
        *dst_dt = src_dt * num / denom;
3049
    }
3050

3051
    return 0;
3052
}
3053

3054
/*
3055
 * Returns true if the object is something that is best considered
3056
 * a Datetime, false otherwise.
3057
 */
3058
static NPY_GCC_NONNULL(1) npy_bool
3059 1
is_any_numpy_datetime(PyObject *obj)
3060
{
3061 1
    return (PyArray_IsScalar(obj, Datetime) ||
3062 1
            (PyArray_Check(obj) && (
3063 1
                PyArray_DESCR((PyArrayObject *)obj)->type_num ==
3064 1
                                                        NPY_DATETIME)) ||
3065 1
            PyDate_Check(obj) ||
3066 1
            PyDateTime_Check(obj));
3067
}
3068

3069
/*
3070
 * Returns true if the object is something that is best considered
3071
 * a Timedelta, false otherwise.
3072
 */
3073
static npy_bool
3074 1
is_any_numpy_timedelta(PyObject *obj)
3075
{
3076 1
    return (PyArray_IsScalar(obj, Timedelta) ||
3077 1
        (PyArray_Check(obj) && (
3078 1
            PyArray_DESCR((PyArrayObject *)obj)->type_num == NPY_TIMEDELTA)) ||
3079 1
        PyDelta_Check(obj));
3080
}
3081

3082
/*
3083
 * Returns true if the object is something that is best considered
3084
 * a Datetime or Timedelta, false otherwise.
3085
 */
3086
NPY_NO_EXPORT npy_bool
3087 1
is_any_numpy_datetime_or_timedelta(PyObject *obj)
3088
{
3089 1
    return obj != NULL &&
3090 1
           (is_any_numpy_datetime(obj) ||
3091 1
            is_any_numpy_timedelta(obj));
3092
}
3093

3094
/*
3095
 * Converts an array of PyObject * into datetimes and/or timedeltas,
3096
 * based on the values in type_nums.
3097
 *
3098
 * If inout_meta->base is -1, uses GCDs to calculate the metadata, filling
3099
 * in 'inout_meta' with the resulting metadata. Otherwise uses the provided
3100
 * 'inout_meta' for all the conversions.
3101
 *
3102
 * When obj[i] is NULL, out_value[i] will be set to NPY_DATETIME_NAT.
3103
 *
3104
 * Returns 0 on success, -1 on failure.
3105
 */
3106
NPY_NO_EXPORT int
3107 1
convert_pyobjects_to_datetimes(int count,
3108
                               PyObject **objs, const int *type_nums,
3109
                               NPY_CASTING casting,
3110
                               npy_int64 *out_values,
3111
                               PyArray_DatetimeMetaData *inout_meta)
3112
{
3113
    int i, is_out_strict;
3114
    PyArray_DatetimeMetaData *meta;
3115

3116
    /* No values trivially succeeds */
3117 1
    if (count == 0) {
3118
        return 0;
3119
    }
3120

3121
    /* Use the inputs to resolve the unit metadata if requested */
3122 1
    if (inout_meta->base == NPY_FR_ERROR) {
3123
        /* Allocate an array of metadata corresponding to the objects */
3124 1
        meta = PyArray_malloc(count * sizeof(PyArray_DatetimeMetaData));
3125 1
        if (meta == NULL) {
3126 0
            PyErr_NoMemory();
3127 0
            return -1;
3128
        }
3129

3130
        /* Convert all the objects into timedeltas or datetimes */
3131 1
        for (i = 0; i < count; ++i) {
3132 1
            meta[i].base = NPY_FR_ERROR;
3133 1
            meta[i].num = 1;
3134

3135
            /* NULL -> NaT */
3136 1
            if (objs[i] == NULL) {
3137 1
                out_values[i] = NPY_DATETIME_NAT;
3138 1
                meta[i].base = NPY_FR_GENERIC;
3139
            }
3140 1
            else if (type_nums[i] == NPY_DATETIME) {
3141 1
                if (convert_pyobject_to_datetime(&meta[i], objs[i],
3142 1
                                            casting, &out_values[i]) < 0) {
3143