#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <datetime.h>

#include <time.h>

#define _MULTIARRAYMODULE
#define NPY_NO_PREFIX
#include <numpy/ndarrayobject.h>

#include "npy_config.h"

#include "numpy/npy_3kcompat.h"

#include "_datetime.h"

/* For defaults and errors */
#define NPY_FR_ERR  -1

/* Offset for number of days between Dec 31, 1969 and Jan 1, 0001
*  Assuming Gregorian calendar was always in effect (proleptic Gregorian calendar)
*/

/* Calendar Structure for Parsing Long -> Date */
typedef struct {
    int year, month, day;
} ymdstruct;

typedef struct {
    int hour, min, sec;
} hmsstruct;


/*
  ====================================================
  == Beginning of section borrowed from mx.DateTime ==
  ====================================================
*/

/*
 * Functions in the following section are borrowed from mx.DateTime version
 * 2.0.6, and hence this code is subject to the terms of the egenix public
 * license version 1.0.0
 */

#define Py_AssertWithArg(x,errortype,errorstr,a1) {if (!(x)) {PyErr_Format(errortype,errorstr,a1);goto onError;}}

/* Table with day offsets for each month (0-based, without and with leap) */
static int month_offset[2][13] = {
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};

/* Table of number of days in a month (0-based, without and with leap) */
static int days_in_month[2][12] = {
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

/* Return 1/0 iff year points to a leap year in calendar. */
static int
is_leapyear(long year)
{
    return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
}


/*
 * Return the day of the week for the given absolute date.
 * Monday is 0 and Sunday is 6
 */
static int
day_of_week(npy_longlong absdate)
{
    /* Add in four for the Thursday on Jan 1, 1970 (epoch offset)*/
    absdate += 4;

    if (absdate >= 0) {
        return absdate % 7;
    }
    else {
        return 6 + (absdate + 1) % 7;
    }
}

/*
 * Return the year offset, that is the absolute date of the day
 * 31.12.(year-1) since 31.12.1969 in the proleptic Gregorian calendar.
 */
static npy_longlong
year_offset(npy_longlong year)
{
    /* Note that 477 == 1969/4 - 1969/100 + 1969/400 */
    year--;
    if (year >= 0 || -1/4 == -1)
        return (year-1969)*365 + year/4 - year/100 + year/400 - 477;
    else
        return (year-1969)*365 + (year-3)/4 - (year-99)/100 + (year-399)/400 - 477;
}

/*
 * Modified version of mxDateTime function
 * Returns absolute number of days since Jan 1, 1970
 * assuming a proleptic Gregorian Calendar
 * Raises a ValueError if out of range month or day
 * day -1 is Dec 31, 1969, day 0 is Jan 1, 1970, day 1 is Jan 2, 1970
 */
static npy_longlong
days_from_ymd(int year, int month, int day)
{

    /* Calculate the absolute date */
    int leap;
    npy_longlong yearoffset, absdate;

    /* Is it a leap year ? */
    leap = is_leapyear(year);

    /* Negative month values indicate months relative to the years end */
    if (month < 0) month += 13;
    Py_AssertWithArg(month >= 1 && month <= 12,
                     PyExc_ValueError,
                     "month out of range (1-12): %i",
                     month);

    /* Negative values indicate days relative to the months end */
    if (day < 0) day += days_in_month[leap][month - 1] + 1;
    Py_AssertWithArg(day >= 1 && day <= days_in_month[leap][month - 1],
                     PyExc_ValueError,
                     "day out of range: %i",
                     day);

    /*
     * Number of days between Dec 31, (year - 1) and Dec 31, 1969
     *    (can be negative).
     */
    yearoffset = year_offset(year);

    if (PyErr_Occurred()) goto onError;

    /*
     * Calculate the number of days using yearoffset
     * Jan 1, 1970 is day 0 and thus Dec. 31, 1969 is day -1
     */
    absdate = day-1 + month_offset[leap][month - 1] + yearoffset;

    return absdate;

 onError:
    return 0;

}

/* Returns absolute seconds from an hour, minute, and second
 */
#define secs_from_hms(hour, min, sec, multiplier) (\
  ((hour)*3600 + (min)*60 + (sec)) * (npy_int64)(multiplier)\
)

/*
 * Takes a number of days since Jan 1, 1970 (positive or negative)
 * and returns the year. month, and day in the proleptic
 * Gregorian calendar
 *
 * Examples:
 *
 * -1 returns 1969, 12, 31
 * 0  returns 1970, 1, 1
 * 1  returns 1970, 1, 2
 */

static ymdstruct
days_to_ymdstruct(npy_datetime dlong)
{
    ymdstruct ymd;
    long year;
    npy_longlong yearoffset;
    int leap, dayoffset;
    int month = 1, day = 1;
    int *monthoffset;

    dlong += 1;

    /* Approximate year */
    year = 1970 + dlong / 365.2425;

    /* Apply corrections to reach the correct year */
    while (1) {
        /* Calculate the year offset */
        yearoffset = year_offset(year);

        /*
         * Backward correction: absdate must be greater than the
         * yearoffset
         */
        if (yearoffset >= dlong) {
            year--;
            continue;
        }

        dayoffset = dlong - yearoffset;
        leap = is_leapyear(year);

        /* Forward correction: non leap years only have 365 days */
        if (dayoffset > 365 && !leap) {
            year++;
            continue;
        }
        break;
    }

    /* Now iterate to find the month */
    monthoffset = month_offset[leap];
    for (month = 1; month < 13; month++) {
        if (monthoffset[month] >= dayoffset)
            break;
    }
    day = dayoffset - month_offset[leap][month-1];

    ymd.year  = year;
    ymd.month = month;
    ymd.day   = day;

    return ymd;
}

/*
 * Converts an integer number of seconds in a day to hours minutes seconds.
 * It assumes seconds is between 0 and 86399.
 */

static hmsstruct
seconds_to_hmsstruct(npy_longlong dlong)
{
    int hour, minute, second;
    hmsstruct hms;

    hour   = dlong / 3600;
    minute = (dlong % 3600) / 60;
    second = dlong - (hour*3600 + minute*60);

    hms.hour   = hour;
    hms.min = minute;
    hms.sec = second;

    return hms;
}

/*
  ====================================================
  == End of section adapted from mx.DateTime       ==
  ====================================================
*/


/*==================================================
// Parsing DateTime struct and returns a date-time number
// =================================================

 Structure is assumed to be already normalized
*/

/*NUMPY_API
 * Create a datetime value from a filled datetime struct and resolution unit.
 */
NPY_NO_EXPORT npy_datetime
PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
{
    npy_datetime ret;
    npy_longlong days = 0; /* The absolute number of days since Jan 1, 1970 */

    if (fr > NPY_FR_M) {
        days = days_from_ymd(d->year, d->month, d->day);
    }
    if (fr == NPY_FR_Y) {
        ret = d->year - 1970;
    }
    else if (fr == NPY_FR_M) {
        ret = (d->year - 1970) * 12 + d->month - 1;
    }
    else if (fr == NPY_FR_W) {
        /* This is just 7-days for now. */
        if (days >= 0) {
            ret = days / 7;
        }
        else {
            ret = (days - 6) / 7;
        }
    }
    else if (fr == NPY_FR_B) {
        npy_longlong x;
        int dotw = day_of_week(days);

        if (dotw > 4) {
            /* Invalid business day */
            ret = 0;
        }
        else {
            if (days >= 0) {
                /* offset to adjust first week */
                x = days - 4;
            }
            else {
                x = days - 2;
            }
            ret = 2 + (x / 7) * 5 + x % 7;
        }
    }
    else if (fr == NPY_FR_D) {
        ret = days;
    }
    else if (fr == NPY_FR_h) {
        ret = days * 24 + d->hour;
    }
    else if (fr == NPY_FR_m) {
        ret = days * 1440 + d->hour * 60 + d->min;
    }
    else if (fr == NPY_FR_s) {
        ret = days * (npy_int64)(86400) +
            secs_from_hms(d->hour, d->min, d->sec, 1);
    }
    else if (fr == NPY_FR_ms) {
        ret = days * (npy_int64)(86400000)
            + secs_from_hms(d->hour, d->min, d->sec, 1000)
            + (d->us / 1000);
    }
    else if (fr == NPY_FR_us) {
        npy_int64 num = 86400 * 1000;
        num *= (npy_int64)(1000);
        ret = days * num + secs_from_hms(d->hour, d->min, d->sec, 1000000)
            + d->us;
    }
    else if (fr == NPY_FR_ns) {
        npy_int64 num = 86400 * 1000;
        num *= (npy_int64)(1000 * 1000);
        ret = days * num + secs_from_hms(d->hour, d->min, d->sec, 1000000000)
            + d->us * (npy_int64)(1000) + (d->ps / 1000);
    }
    else if (fr == NPY_FR_ps) {
        npy_int64 num2 = 1000 * 1000;
        npy_int64 num1;

        num2 *= (npy_int64)(1000 * 1000);
        num1 = (npy_int64)(86400) * num2;
        ret = days * num1 + secs_from_hms(d->hour, d->min, d->sec, num2)
            + d->us * (npy_int64)(1000000) + d->ps;
    }
    else if (fr == NPY_FR_fs) {
        /* only 2.6 hours */
        npy_int64 num2 = 1000000;
        num2 *= (npy_int64)(1000000);
        num2 *= (npy_int64)(1000);

        /* get number of seconds as a postive or negative number */
        if (days >= 0) {
            ret = secs_from_hms(d->hour, d->min, d->sec, 1);
        }
        else {
            ret = ((d->hour - 24)*3600 + d->min*60 + d->sec);
        }
        ret = ret * num2 + d->us * (npy_int64)(1000000000)
            + d->ps * (npy_int64)(1000) + (d->as / 1000);
    }
    else if (fr == NPY_FR_as) {
        /* only 9.2 secs */
        npy_int64 num1, num2;

        num1 = 1000000;
        num1 *= (npy_int64)(1000000);
        num2 = num1 * (npy_int64)(1000000);

        if (days >= 0) {
            ret = d->sec;
        }
        else {
            ret = d->sec - 60;
        }
        ret = ret * num2 + d->us * num1 + d->ps * (npy_int64)(1000000)
            + d->as;
    }
    else {
        /* Shouldn't get here */
        PyErr_SetString(PyExc_ValueError, "invalid internal frequency");
        ret = -1;
    }

    return ret;
}

/* Uses Average values when frequency is Y, M, or B */

#define _DAYS_PER_MONTH 30.436875
#define _DAYS_PER_YEAR  365.2425

/*NUMPY_API
 * Create a timdelta value from a filled timedelta struct and resolution unit.
 */
NPY_NO_EXPORT npy_datetime
PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d)
{
    npy_datetime ret;

    if (fr == NPY_FR_Y) {
        ret = d->day / _DAYS_PER_YEAR;
    }
    else if (fr == NPY_FR_M) {
        ret = d->day / _DAYS_PER_MONTH;
    }
    else if (fr == NPY_FR_W) {
        /* This is just 7-days for now. */
        if (d->day >= 0) {
            ret = d->day / 7;
        }
        else {
            ret = (d->day - 6) / 7;
        }
    }
    else if (fr == NPY_FR_B) {
        /*
         * What is the meaning of a relative Business day?
         *
         * This assumes you want to take the day difference and
         * convert it to business-day difference (removing 2 every 7).
         */
        ret = (d->day / 7) * 5 + d->day % 7;
    }
    else if (fr == NPY_FR_D) {
        ret = d->day;
    }
    else if (fr == NPY_FR_h) {
        ret = d->day + d->sec / 3600;
    }
    else if (fr == NPY_FR_m) {
        ret = d->day * (npy_int64)(1440) + d->sec / 60;
    }
    else if (fr == NPY_FR_s) {
        ret = d->day * (npy_int64)(86400) + d->sec;
    }
    else if (fr == NPY_FR_ms) {
        ret = d->day * (npy_int64)(86400000) + d->sec * 1000 + d->us / 1000;
    }
    else if (fr == NPY_FR_us) {
        npy_int64 num = 86400000;
        num *= (npy_int64)(1000);
        ret = d->day * num + d->sec * (npy_int64)(1000000) + d->us;
    }
    else if (fr == NPY_FR_ns) {
        npy_int64 num = 86400000;
        num *= (npy_int64)(1000000);
        ret = d->day * num + d->sec * (npy_int64)(1000000000)
            + d->us * (npy_int64)(1000) + (d->ps / 1000);
    }
    else if (fr == NPY_FR_ps) {
        npy_int64 num2, num1;

        num2 = 1000000;
        num2 *= (npy_int64)(1000000);
        num1 = (npy_int64)(86400) * num2;

        ret = d->day * num1 + d->sec * num2 + d->us * (npy_int64)(1000000)
            + d->ps;
    }
    else if (fr == NPY_FR_fs) {
        /* only 2.6 hours */
        npy_int64 num2 = 1000000000;
        num2 *= (npy_int64)(1000000);
        ret = d->sec * num2 + d->us * (npy_int64)(1000000000)
            + d->ps * (npy_int64)(1000) + (d->as / 1000);
    }
    else if (fr == NPY_FR_as) {
        /* only 9.2 secs */
        npy_int64 num1, num2;

        num1 = 1000000;
        num1 *= (npy_int64)(1000000);
        num2 = num1 * (npy_int64)(1000000);
        ret = d->sec * num2 + d->us * num1 + d->ps * (npy_int64)(1000000)
            + d->as;
    }
    else {
        /* Shouldn't get here */
        PyErr_SetString(PyExc_ValueError, "invalid internal frequency");
        ret = -1;
    }

    return ret;
}



/*NUMPY_API
 * Fill the datetime struct from the value and resolution unit.
 */
NPY_NO_EXPORT void
PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
                                 npy_datetimestruct *result)
{
    int year = 1970, month = 1, day = 1,
        hour = 0, min = 0, sec = 0,
        us = 0, ps = 0, as = 0;

    npy_int64 tmp;
    ymdstruct ymd;
    hmsstruct hms;

    /*
     * Note that what looks like val / N and val % N for positive numbers maps to
     * [val - (N-1)] / N and [N-1 + (val+1) % N] for negative numbers (with the 2nd
     * value, the remainder, being positive in both cases).
     */
    if (fr == NPY_FR_Y) {
        year = 1970 + val;
    }
    else if (fr == NPY_FR_M) {
        if (val >= 0) {
            year  = 1970 + val / 12;
            month = val % 12 + 1;
        }
        else {
            year  = 1969 + (val + 1) / 12;
            month = 12 + (val + 1)% 12;
        }
    }
    else if (fr == NPY_FR_W) {
        /* A week is the same as 7 days */
        ymd = days_to_ymdstruct(val * 7);
        year  = ymd.year;
        month = ymd.month;
        day   = ymd.day;
    }
    else if (fr == NPY_FR_B) {
        /* Number of business days since Thursday, 1-1-70 */
        npy_longlong absdays;
        /*
         * A buisness day is M T W Th F (i.e. all but Sat and Sun.)
         * Convert the business day to the number of actual days.
         *
         * Must convert [0,1,2,3,4,5,6,7,...] to
         *                  [0,1,4,5,6,7,8,11,...]
         * and  [...,-9,-8,-7,-6,-5,-4,-3,-2,-1,0] to
         *        [...,-13,-10,-9,-8,-7,-6,-3,-2,-1,0]
         */
        if (val >= 0) {
            absdays = 7 * ((val + 3) / 5) + ((val + 3) % 5) - 3;
        }
        else {
            /* Recall how C computes / and % with negative numbers */
            absdays = 7 * ((val - 1) / 5) + ((val - 1) % 5) + 1;
        }
        ymd = days_to_ymdstruct(absdays);
        year  = ymd.year;
        month = ymd.month;
        day   = ymd.day;
    }
    else if (fr == NPY_FR_D) {
        ymd = days_to_ymdstruct(val);
        year  = ymd.year;
        month = ymd.month;
        day   = ymd.day;
    }
    else if (fr == NPY_FR_h) {
        if (val >= 0) {
            ymd  = days_to_ymdstruct(val / 24);
            hour  = val % 24;
        }
        else {
            ymd  = days_to_ymdstruct((val - 23) / 24);
            hour = 23 + (val + 1) % 24;
        }
        year  = ymd.year;
        month = ymd.month;
        day   = ymd.day;
    }
    else if (fr == NPY_FR_m) {
        if (val >= 0) {
            ymd = days_to_ymdstruct(val / 1440);
            min = val % 1440;
        }
        else {
            ymd = days_to_ymdstruct((val - 1439) / 1440);
            min = 1439 + (val + 1) % 1440;
        }
        hms = seconds_to_hmsstruct(min * 60);
        year   = ymd.year;
        month  = ymd.month;
        day    = ymd.day;
        hour   = hms.hour;
        min = hms.min;
    }
    else if (fr == NPY_FR_s) {
        if (val >= 0) {
            ymd = days_to_ymdstruct(val / 86400);
            sec = val % 86400;
        }
        else {
            ymd = days_to_ymdstruct((val - 86399) / 86400);
            sec = 86399 + (val + 1) % 86400;
        }
        hms = seconds_to_hmsstruct(sec);
        year   = ymd.year;
        month  = ymd.month;
        day    = ymd.day;
        hour   = hms.hour;
        min = hms.min;
        sec = hms.sec;
    }
    else if (fr == NPY_FR_ms) {
        if (val >= 0) {
            ymd = days_to_ymdstruct(val / 86400000);
            tmp  = val % 86400000;
        }
        else {
            ymd = days_to_ymdstruct((val - 86399999) / 86400000);
            tmp  = 86399999 + (val + 1) % 86399999;
        }
        hms = seconds_to_hmsstruct(tmp / 1000);
        us  = (tmp % 1000)*1000;
        year    = ymd.year;
        month   = ymd.month;
        day     = ymd.day;
        hour    = hms.hour;
        min     = hms.min;
        sec     = hms.sec;
    }
    else if (fr == NPY_FR_us) {
        npy_int64 num1, num2;
        num1 = 86400000;
        num1 *= 1000;
        num2 = num1 - 1;
        if (val >= 0) {
            ymd = days_to_ymdstruct(val / num1);
            tmp = val % num1;
        }
        else {
            ymd = days_to_ymdstruct((val - num2)/ num1);
            tmp = num2 + (val + 1) % num1;
        }
        hms = seconds_to_hmsstruct(tmp / 1000000);
        us = tmp % 1000000;
        year    = ymd.year;
        month   = ymd.month;
        day     = ymd.day;
        hour    = hms.hour;
        min     = hms.min;
        sec     = hms.sec;
    }
    else if (fr == NPY_FR_ns) {
        npy_int64 num1, num2, num3;
        num1 = 86400000;
        num1 *= 1000000000;
        num2 = num1 - 1;
        num3 = 1000000;
        num3 *= 1000000;
        if (val >= 0) {
            ymd = days_to_ymdstruct(val / num1);
            tmp = val % num1;
        }
        else {
            ymd = days_to_ymdstruct((val - num2)/ num1);
            tmp = num2 + (val + 1) % num1;
        }
        hms = seconds_to_hmsstruct(tmp / 1000000000);
        tmp = tmp % 1000000000;
        us = tmp / 1000;
        ps = (tmp % 1000) * (npy_int64)(1000);
        year    = ymd.year;
        month   = ymd.month;
        day     = ymd.day;
        hour    = hms.hour;
        min     = hms.min;
        sec     = hms.sec;
    }
    else if (fr == NPY_FR_ps) {
        npy_int64 num1, num2, num3;
        num3 = 1000000000;
        num3 *= (npy_int64)(1000);
        num1 = (npy_int64)(86400) * num3;
        num2 = num1 - 1;

        if (val >= 0) {
            ymd = days_to_ymdstruct(val / num1);
            tmp = val % num1;
        }
        else {
            ymd = days_to_ymdstruct((val - num2) / num1);
            tmp = num2 + (val + 1) % num1;
        }
        hms = seconds_to_hmsstruct(tmp / num3);
        tmp = tmp % num3;
        us = tmp / 1000000;
        ps = tmp % 1000000;
        year    = ymd.year;
        month   = ymd.month;
        day     = ymd.day;
        hour    = hms.hour;
        min     = hms.min;
        sec     = hms.sec;
    }
    else if (fr == NPY_FR_fs) {
        /* entire range is only += 2.6 hours */
        npy_int64 num1, num2;
        num1 = 1000000000;
        num1 *= (npy_int64)(1000);
        num2 = num1 * (npy_int64)(1000);

        if (val >= 0) {
            sec = val / num2;
            tmp = val % num2;
            hms = seconds_to_hmsstruct(sec);
            hour = hms.hour;
            min = hms.min;
            sec = hms.sec;
        }
        else {
            /* tmp (number of fs) will be positive after this segment */
            year = 1969;
            day = 31;
            month = 12;
            sec = (val - (num2-1))/num2;
            tmp = (num2-1) + (val + 1) % num2;
            if (sec == 0) {
                /* we are at the last second */
                hour = 23;
                min = 59;
                sec = 59;
            }
            else {
                hour = 24 + (sec - 3599)/3600;
                sec = 3599 + (sec+1)%3600;
                min = sec / 60;
                sec = sec % 60;
            }
        }
        us = tmp / 1000000000;
        tmp = tmp % 1000000000;
        ps = tmp / 1000;
        as = (tmp % 1000) * (npy_int64)(1000);
    }
    else if (fr == NPY_FR_as) {
        /* entire range is only += 9.2 seconds */
        npy_int64 num1, num2, num3;
        num1 = 1000000;
        num2 = num1 * (npy_int64)(1000000);
        num3 = num2 * (npy_int64)(1000000);
        if (val >= 0) {
            hour = 0;
            min = 0;
            sec = val / num3;
            tmp = val % num3;
        }
        else {
            year = 1969;
            day = 31;
            month = 12;
            hour = 23;
            min = 59;
            sec = 60 + (val - (num3-1)) / num3;
            tmp = (num3-1) + (val+1) % num3;
        }
        us = tmp / num2;
        tmp = tmp % num2;
        ps = tmp / num1;
        as = tmp % num1;
    }
    else {
        PyErr_SetString(PyExc_RuntimeError, "invalid internal time resolution");
    }

    result->year  = year;
    result->month = month;
    result->day   = day;
    result->hour  = hour;
    result->min   = min;
    result->sec   = sec;
    result->us    = us;
    result->ps    = ps;
    result->as    = as;

    return;
}

/*
 * FIXME: Overflow is not handled at all
 *   To convert from Years, Months, and Business Days, multiplication by the average is done
 */

/*NUMPY_API
 * Fill the timedelta struct from the timedelta value and resolution unit.
 */
NPY_NO_EXPORT void
PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
                                 npy_timedeltastruct *result)
{
    npy_longlong day=0;
    int sec=0, us=0, ps=0, as=0;
    npy_bool negative=0;

    /*
     * Note that what looks like val / N and val % N for positive numbers maps to
     * [val - (N-1)] / N and [N-1 + (val+1) % N] for negative numbers (with the 2nd
     * value, the remainder, being positive in both cases).
     */

    if (val < 0) {
        val = -val;
        negative = 1;
    }
    if (fr == NPY_FR_Y) {
        day = val * _DAYS_PER_YEAR;
    }
    else if (fr == NPY_FR_M) {
        day = val * _DAYS_PER_MONTH;
    }
    else if (fr == NPY_FR_W) {
        day = val * 7;
    }
    else if (fr == NPY_FR_B) {
        /* Number of business days since Thursday, 1-1-70 */
        day = (val * 7) / 5;
    }
    else if (fr == NPY_FR_D) {
        day = val;
    }
    else if (fr == NPY_FR_h) {
        day = val / 24;
        sec = (val % 24)*3600;
    }
    else if (fr == NPY_FR_m) {
        day = val / 1440;
        sec = (val % 1440)*60;
    }
    else if (fr == NPY_FR_s) {
        day = val / (86400);
        sec = val % 86400;
    }
    else if (fr == NPY_FR_ms) {
        day = val / 86400000;
        val = val % 86400000;
        sec = val / 1000;
        us = (val % 1000)*1000;
    }
    else if (fr == NPY_FR_us) {
        npy_int64 num1;
        num1 = 86400000;
        num1 *= 1000;
        day = val / num1;
        us = val % num1;
        sec = us / 1000000;
        us = us % 1000000;
    }
    else if (fr == NPY_FR_ns) {
        npy_int64 num1;
        num1 = 86400000;
        num1 *= 1000000;
        day = val / num1;
        val = val % num1;
        sec = val / 1000000000;
        val = val % 1000000000;
        us  = val / 1000;
        ps  = (val % 1000) * (npy_int64)(1000);
    }
    else if (fr == NPY_FR_ps) {
        npy_int64 num1, num2;
        num2 = 1000000000;
        num2 *= (npy_int64)(1000);
        num1 = (npy_int64)(86400) * num2;

        day = val / num1;
        ps = val % num1;
        sec = ps / num2;
        ps = ps % num2;
        us = ps / 1000000;
        ps = ps % 1000000;
    }
    else if (fr == NPY_FR_fs) {
        /* entire range is only += 9.2 hours */
        npy_int64 num1, num2;
        num1 = 1000000000;
        num2 = num1 * (npy_int64)(1000000);

        day = 0;
        sec = val / num2;
        val = val % num2;
        us = val / num1;
        val = val % num1;
        ps = val / 1000;
        as = (val % 1000) * (npy_int64)(1000);
    }
    else if (fr == NPY_FR_as) {
        /* entire range is only += 2.6 seconds */
        npy_int64 num1, num2, num3;
        num1 = 1000000;
        num2 = num1 * (npy_int64)(1000000);
        num3 = num2 * (npy_int64)(1000000);
        day = 0;
        sec = val / num3;
        as = val % num3;
        us = as / num2;
        as = as % num2;
        ps = as / num1;
        as = as % num1;
    }
    else {
        PyErr_SetString(PyExc_RuntimeError, "invalid internal time resolution");
    }

    if (negative) {
        result->day = -day;
        result->sec = -sec;
        result->us = -us;
        result->ps = -ps;
        result->as = -as;
    }
    else {
        result->day   = day;
        result->sec   = sec;
        result->us    = us;
        result->ps    = ps;
        result->as    = as;
    }
    return;
}
