<commit_before>"""
Performs conversions of netCDF time coordinate data to/from datetime objects.
"""

from cpython.object cimport PyObject_RichCompare

import numpy as np
import math
import numpy
import re

from datetime import datetime as real_datetime
from datetime import timedelta
import time                     # strftime

try:
    from itertools import izip as zip
except ImportError:  # python 3.x
    pass

microsec_units = ['microseconds','microsecond', 'microsec', 'microsecs']
millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs']
sec_units =      ['second', 'seconds', 'sec', 'secs', 's']
min_units =      ['minute', 'minutes', 'min', 'mins']
hr_units =       ['hour', 'hours', 'hr', 'hrs', 'h']
day_units =      ['day', 'days', 'd']
_units = microsec_units+millisec_units+sec_units+min_units+hr_units+day_units
_calendars = ['standard', 'gregorian', 'proleptic_gregorian',
              'noleap', 'julian', 'all_leap', '365_day', '366_day', '360_day']

__version__ = '1.4.1'

# Adapted from http://delete.me.uk/2005/03/iso8601.html
ISO8601_REGEX = re.compile(r"(?P<year>[+-]?[0-9]{1,4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
                           r"(((?P<separator1>.)(?P<hour>[0-9]{1,2}):(?P<minute>[0-9]{1,2})(:(?P<second>[0-9]{1,2})(\.(?P<fraction>[0-9]+))?)?)?"
                           r"((?P<separator2>.?)(?P<timezone>Z|(([-+])([0-9]{1,2}):([0-9]{1,2}))))?)?)?)?"
                           )
TIMEZONE_REGEX = re.compile(
    "(?P<prefix>[+-])(?P<hours>[0-9]{1,2}):(?P<minutes>[0-9]{1,2})")

def JulianDayFromDate(date, calendar='standard'):
    """

    creates a Julian Day from a 'datetime-like' object.  Returns the fractional
    Julian Day (resolution approx 0.1 second).

    if calendar='standard' or 'gregorian' (default), Julian day follows Julian
    Calendar on and before 1582-10-5, Gregorian calendar after 1582-10-15.

    if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.

    if calendar='julian', Julian Day follows julian calendar.

    Algorithm:

    <NAME> (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
    Virginia. p. 63

    """

    # based on redate.py by <NAME>.

    # check if input was scalar and change return accordingly
    isscalar = False
    try:
        date[0]
    except:
        isscalar = True

    date = np.atleast_1d(np.array(date))
    year = np.empty(len(date), dtype=np.int32)
    month = year.copy()
    day = year.copy()
    hour = year.copy()
    minute = year.copy()
    second = year.copy()
    microsecond = year.copy()
    for i, d in enumerate(date):
        year[i] = d.year
        month[i] = d.month
        day[i] = d.day
        hour[i] = d.hour
        minute[i] = d.minute
        second[i] = d.second
        microsecond[i] = d.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    month_lt_3 = month < 3
    month[month_lt_3] = month[month_lt_3] + 12
    year[month_lt_3] = year[month_lt_3] - 1

    # MC - assure array
    # A = np.int64(year / 100)
    A = (year / 100).astype(np.int64)

    # MC
    # jd = int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + \
    #      day - 1524.5
    jd = 365. * year + np.int32(0.25 * year + 2000.) + np.int32(30.6001 * (month + 1)) + \
        day + 1718994.5

    # optionally adjust the jd for the switch from
    # the Julian to Gregorian Calendar
    # here assumed to have occurred the day after 1582 October 4
    if calendar in ['standard', 'gregorian']:
        # MC - do not have to be contiguous dates
        # if np.min(jd) >= 2299170.5:
        #     # 1582 October 15 (Gregorian Calendar)
        #     B = 2 - A + np.int32(A / 4)
        # elif np.max(jd) < 2299160.5:
        #     # 1582 October 5 (Julian Calendar)
        #     B = np.zeros(len(jd))
        # else:
        #     print(date, calendar, jd)
        #     raise ValueError(
        #         'impossible date (falls in gap between end of Julian calendar and beginning of Gregorian calendar')
        if np.any((jd >= 2299160.5) & (jd < 2299170.5)): # missing days in Gregorian calendar
            raise ValueError(
                'impossible date (falls in gap between end of Julian calendar and beginning of Gregorian calendar')
        B = np.zeros(len(jd))             # 1582 October 5 (Julian Calendar)
        ii = np.where(jd >= 2299170.5)[0] # 1582 October 15 (Gregorian Calendar)
        if ii.size>0:
            B[ii] = 2 - A[ii] + np.int32(A[ii] / 4)
    elif calendar == 'proleptic_gregorian':
        B = 2 - A + np.int32(A / 4)
    elif calendar == 'julian':
        B = np.zeros(len(jd))
    else:
        raise ValueError(
            'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar)

    # adjust for Julian calendar if necessary
    jd = jd + B

    # Add a small offset (proportional to Julian date) for correct re-conversion.
    # This is about 45 microseconds in 2000 for Julian date starting -4712.
    # (pull request #433).
    eps = np.finfo(float).eps
    eps = np.maximum(eps*jd, eps)
    jd += eps

    if isscalar:
        return jd[0]
    else:
        return jd


cdef _NoLeapDayFromDate(date):
    """

creates a Julian Day for a calendar with no leap years from a datetime
instance.  Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    if (month < 3):
        month = month + 12
        year = year - 1

    jd = int(365. * (year + 4716)) + int(30.6001 * (month + 1)) + \
        day - 1524.5

    return jd


cdef _AllLeapFromDate(date):
    """

creates a Julian Day for a calendar where all years have 366 days from
a 'datetime-like' object.
Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    if (month < 3):
        month = month + 12
        year = year - 1

    jd = int(366. * (year + 4716)) + int(30.6001 * (month + 1)) + \
        day - 1524.5

    return jd


cdef _360DayFromDate(date):
    """

creates a Julian Day for a calendar where all months have 30 daysfrom
a 'datetime-like' object.
Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day

    return jd


def DateFromJulianDay(JD, calendar='standard'):
    """

    returns a 'datetime-like' object given Julian Day. Julian Day is a
    fractional day with a resolution of approximately 0.1 seconds.

    if calendar='standard' or 'gregorian' (default), Julian day follows Julian
    Calendar on and before 1582-10-5, Gregorian calendar after  1582-10-15.

    if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.

    if calendar='julian', Julian Day follows julian calendar.

    The datetime object is a 'real' datetime object if the date falls in
    the Gregorian calendar (i.e. calendar='proleptic_gregorian', or
    calendar = 'standard'/'gregorian' and the date is after 1582-10-15).
    Otherwise, it's a 'phony' datetime object which is actually an instance
    of netcdftime.datetime.


    Algorithm:

    <NAME> (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
    Virginia. p. 63
    """

    # based on redate.py by <NAME>.

    julian = np.array(JD, dtype=float)

    if np.min(julian) < 0:
        raise ValueError('Julian Day must be positive')

    dayofwk = np.atleast_1d(np.int32(np.fmod(np.int32(julian + 1.5), 7)))
    # get the day (Z) and the fraction of the day (F)
    # add 0.000005 which is 452 ms in case of jd being after
    # second 23:59:59 of a day we want to round to the next day see issue #75
    Z = np.atleast_1d(np.int32(np.round(julian)))
    F = np.atleast_1d(julian + 0.5 - Z).astype(np.float64)
    if calendar in ['standard', 'gregorian']:
        # MC
        # alpha = int((Z - 1867216.25)/36524.25)
        # A = Z + 1 + alpha - int(alpha/4)
        alpha = np.int32(((Z - 1867216.) - 0.25) / 36524.25)
        A = Z + 1 + alpha - np.int32(0.25 * alpha)
        # check if dates before oct 5th 1582 are in the array
        ind_before = np.where(julian < 2299160.5)[0]
        if len(ind_before) > 0:
            A[ind_before] = Z[ind_before]

    elif calendar == 'proleptic_gregorian':
        # MC
        # alpha = int((Z - 1867216.25)/36524.25)
        # A = Z + 1 + alpha - int(alpha/4)
        alpha = np.int32(((Z - 1867216.) - 0.25) / 36524.25)
        A = Z + 1 + alpha - np.int32(0.25 * alpha)
    elif calendar == 'julian':
        A = Z
    else:
        raise ValueError(
            'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar)

    B = A + 1524
    # MC
    # C = int((B - 122.1)/365.25)
    # D = int(365.25 * C)
    C = np.atleast_1d(np.int32(6680. + ((B - 2439870.) - 122.1) / 365.25))
    D = np.atleast_1d(np.int32(365 * C + np.int32(0.25 * C)))
    E = np.atleast_1d(np.int32((B - D) / 30.6001))

    # Convert to date
    day = np.clip(B - D - np.int64(30.6001 * E) + F, 1, None)
    nday = B - D - 123
    dayofyr = nday - 305
    ind_nday_before = np.where(nday <= 305)[0]
    if len(ind_nday_before) > 0:
        dayofyr[ind_nday_before] = nday[ind_nday_before] + 60
    # MC
    # if E < 14:
    #     month = E - 1
    # else:
    #     month = E - 13

    # if month > 2:
    #     year = C - 4716
    # else:
    #     year = C - 4715
    month = E - 1
    month[month > 12] = month[month > 12] - 12
    year = C - 4715
    year[month > 2] = year[month > 2] - 1
    year[year <= 0] = year[year <= 0] - 1

    # a leap year?
    leap = np.zeros(len(year),dtype=dayofyr.dtype)
    leap[year % 4 == 0] = 1
    if calendar == 'proleptic_gregorian':
        leap[(year % 100 == 0) & (year % 400 != 0)] = 0
    elif calendar in ['standard', 'gregorian']:
        leap[(year % 100 == 0) & (year % 400 != 0) & (julian < 2299160.5)] = 0

    inc_idx = np.where((leap == 1) & (month > 2))[0]
    dayofyr[inc_idx] = dayofyr[inc_idx] + leap[inc_idx]

    # Subtract the offset from JulianDayFromDate from the microseconds (pull
    # request #433).
    eps = np.finfo(float).eps
    eps = np.maximum(eps*julian, eps)
    hour = np.clip((F * 24.).astype(np.int64), 0, 23)
    F   -= hour / 24.
    minute = np.clip((F * 1440.).astype(np.int64), 0, 59)
    # this is an overestimation due to added offset in JulianDayFromDate
    second = np.clip((F - minute / 1440.) * 86400., 0, None)
    microsecond = (second % 1)*1.e6
    # remove the offset from the microsecond calculation.
    microsecond = np.clip(microsecond - eps*86400.*1e6, 0, 999999)

    # convert year, month, day, hour, minute, second to int32
    year = year.astype(np.int32)
    month = month.astype(np.int32)
    day = day.astype(np.int32)
    hour = hour.astype(np.int32)
    minute = minute.astype(np.int32)
    second = second.astype(np.int32)
    microsecond = microsecond.astype(np.int32)

    # check if input was scalar and change return accordingly
    isscalar = False
    try:
        JD[0]
    except:
        isscalar = True
    # return a 'real' datetime instance if calendar is gregorian.
    if calendar in 'proleptic_gregorian' or \
            (calendar in ['standard', 'gregorian'] and len(ind_before) == 0):
        if not isscalar:
            return np.array([real_datetime(*args)
                             for args in
                             zip(year, month, day, hour, minute, second,
                                 microsecond)])

        else:
            return real_datetime(year[0], month[0], day[0], hour[0],
                                 minute[0], second[0], microsecond[0])
    else:
        # or else, return a 'datetime-like' instance.
        if not isscalar:
            return np.array([datetime(*args, calendar)
                             for args in
                             zip(year, month, day, hour, minute,
                                 second, microsecond, dayofwk, dayofyr)])
        else:
            return datetime(year[0], month[0], day[0], hour[0],
                            minute[0], second[0], microsecond[0], dayofwk[0],
                            dayofyr[0], calendar)


cdef _DateFromNoLeapDay(JD):
    """

returns a 'datetime-like' object given Julian Day for a calendar with no leap
days. Julian Day is a fractional day with a resolution of approximately 0.1 seconds.

    """

    # based on redate.py by <NAME>.

    if JD < 0:
        year_offset = int(-JD) // 365 + 1
        JD += year_offset * 365
    else:
        year_offset = 0

    dayofwk = int(math.fmod(int(JD + 1.5), 7))
    (F, Z) = math.modf(JD + 0.5)<commit_msg>Specify calendar as a keyword argument.<commit_after>"""
Performs conversions of netCDF time coordinate data to/from datetime objects.
"""

from cpython.object cimport PyObject_RichCompare

import numpy as np
import math
import numpy
import re

from datetime import datetime as real_datetime
from datetime import timedelta
import time                     # strftime

try:
    from itertools import izip as zip
except ImportError:  # python 3.x
    pass

microsec_units = ['microseconds','microsecond', 'microsec', 'microsecs']
millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs']
sec_units =      ['second', 'seconds', 'sec', 'secs', 's']
min_units =      ['minute', 'minutes', 'min', 'mins']
hr_units =       ['hour', 'hours', 'hr', 'hrs', 'h']
day_units =      ['day', 'days', 'd']
_units = microsec_units+millisec_units+sec_units+min_units+hr_units+day_units
_calendars = ['standard', 'gregorian', 'proleptic_gregorian',
              'noleap', 'julian', 'all_leap', '365_day', '366_day', '360_day']

__version__ = '1.4.1'

# Adapted from http://delete.me.uk/2005/03/iso8601.html
ISO8601_REGEX = re.compile(r"(?P<year>[+-]?[0-9]{1,4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
                           r"(((?P<separator1>.)(?P<hour>[0-9]{1,2}):(?P<minute>[0-9]{1,2})(:(?P<second>[0-9]{1,2})(\.(?P<fraction>[0-9]+))?)?)?"
                           r"((?P<separator2>.?)(?P<timezone>Z|(([-+])([0-9]{1,2}):([0-9]{1,2}))))?)?)?)?"
                           )
TIMEZONE_REGEX = re.compile(
    "(?P<prefix>[+-])(?P<hours>[0-9]{1,2}):(?P<minutes>[0-9]{1,2})")

def JulianDayFromDate(date, calendar='standard'):
    """

    creates a Julian Day from a 'datetime-like' object.  Returns the fractional
    Julian Day (resolution approx 0.1 second).

    if calendar='standard' or 'gregorian' (default), Julian day follows Julian
    Calendar on and before 1582-10-5, Gregorian calendar after 1582-10-15.

    if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.

    if calendar='julian', Julian Day follows julian calendar.

    Algorithm:

    <NAME> (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
    Virginia. p. 63

    """

    # based on redate.py by <NAME>.

    # check if input was scalar and change return accordingly
    isscalar = False
    try:
        date[0]
    except:
        isscalar = True

    date = np.atleast_1d(np.array(date))
    year = np.empty(len(date), dtype=np.int32)
    month = year.copy()
    day = year.copy()
    hour = year.copy()
    minute = year.copy()
    second = year.copy()
    microsecond = year.copy()
    for i, d in enumerate(date):
        year[i] = d.year
        month[i] = d.month
        day[i] = d.day
        hour[i] = d.hour
        minute[i] = d.minute
        second[i] = d.second
        microsecond[i] = d.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    month_lt_3 = month < 3
    month[month_lt_3] = month[month_lt_3] + 12
    year[month_lt_3] = year[month_lt_3] - 1

    # MC - assure array
    # A = np.int64(year / 100)
    A = (year / 100).astype(np.int64)

    # MC
    # jd = int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + \
    #      day - 1524.5
    jd = 365. * year + np.int32(0.25 * year + 2000.) + np.int32(30.6001 * (month + 1)) + \
        day + 1718994.5

    # optionally adjust the jd for the switch from
    # the Julian to Gregorian Calendar
    # here assumed to have occurred the day after 1582 October 4
    if calendar in ['standard', 'gregorian']:
        # MC - do not have to be contiguous dates
        # if np.min(jd) >= 2299170.5:
        #     # 1582 October 15 (Gregorian Calendar)
        #     B = 2 - A + np.int32(A / 4)
        # elif np.max(jd) < 2299160.5:
        #     # 1582 October 5 (Julian Calendar)
        #     B = np.zeros(len(jd))
        # else:
        #     print(date, calendar, jd)
        #     raise ValueError(
        #         'impossible date (falls in gap between end of Julian calendar and beginning of Gregorian calendar')
        if np.any((jd >= 2299160.5) & (jd < 2299170.5)): # missing days in Gregorian calendar
            raise ValueError(
                'impossible date (falls in gap between end of Julian calendar and beginning of Gregorian calendar')
        B = np.zeros(len(jd))             # 1582 October 5 (Julian Calendar)
        ii = np.where(jd >= 2299170.5)[0] # 1582 October 15 (Gregorian Calendar)
        if ii.size>0:
            B[ii] = 2 - A[ii] + np.int32(A[ii] / 4)
    elif calendar == 'proleptic_gregorian':
        B = 2 - A + np.int32(A / 4)
    elif calendar == 'julian':
        B = np.zeros(len(jd))
    else:
        raise ValueError(
            'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar)

    # adjust for Julian calendar if necessary
    jd = jd + B

    # Add a small offset (proportional to Julian date) for correct re-conversion.
    # This is about 45 microseconds in 2000 for Julian date starting -4712.
    # (pull request #433).
    eps = np.finfo(float).eps
    eps = np.maximum(eps*jd, eps)
    jd += eps

    if isscalar:
        return jd[0]
    else:
        return jd


cdef _NoLeapDayFromDate(date):
    """

creates a Julian Day for a calendar with no leap years from a datetime
instance.  Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    if (month < 3):
        month = month + 12
        year = year - 1

    jd = int(365. * (year + 4716)) + int(30.6001 * (month + 1)) + \
        day - 1524.5

    return jd


cdef _AllLeapFromDate(date):
    """

creates a Julian Day for a calendar where all years have 366 days from
a 'datetime-like' object.
Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    # Start Meeus algorithm (variables are in his notation)
    if (month < 3):
        month = month + 12
        year = year - 1

    jd = int(366. * (year + 4716)) + int(30.6001 * (month + 1)) + \
        day - 1524.5

    return jd


cdef _360DayFromDate(date):
    """

creates a Julian Day for a calendar where all months have 30 daysfrom
a 'datetime-like' object.
Returns the fractional Julian Day (resolution approx 0.1 second).

    """

    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second
    microsecond = date.microsecond
    # Convert time to fractions of a day
    day = day + hour / 24.0 + minute / 1440.0 + (second + microsecond/1.e6) / 86400.0

    jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day

    return jd


def DateFromJulianDay(JD, calendar='standard'):
    """

    returns a 'datetime-like' object given Julian Day. Julian Day is a
    fractional day with a resolution of approximately 0.1 seconds.

    if calendar='standard' or 'gregorian' (default), Julian day follows Julian
    Calendar on and before 1582-10-5, Gregorian calendar after  1582-10-15.

    if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.

    if calendar='julian', Julian Day follows julian calendar.

    The datetime object is a 'real' datetime object if the date falls in
    the Gregorian calendar (i.e. calendar='proleptic_gregorian', or
    calendar = 'standard'/'gregorian' and the date is after 1582-10-15).
    Otherwise, it's a 'phony' datetime object which is actually an instance
    of netcdftime.datetime.


    Algorithm:

    <NAME> (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
    Virginia. p. 63
    """

    # based on redate.py by <NAME>.

    julian = np.array(JD, dtype=float)

    if np.min(julian) < 0:
        raise ValueError('Julian Day must be positive')

    dayofwk = np.atleast_1d(np.int32(np.fmod(np.int32(julian + 1.5), 7)))
    # get the day (Z) and the fraction of the day (F)
    # add 0.000005 which is 452 ms in case of jd being after
    # second 23:59:59 of a day we want to round to the next day see issue #75
    Z = np.atleast_1d(np.int32(np.round(julian)))
    F = np.atleast_1d(julian + 0.5 - Z).astype(np.float64)
    if calendar in ['standard', 'gregorian']:
        # MC
        # alpha = int((Z - 1867216.25)/36524.25)
        # A = Z + 1 + alpha - int(alpha/4)
        alpha = np.int32(((Z - 1867216.) - 0.25) / 36524.25)
        A = Z + 1 + alpha - np.int32(0.25 * alpha)
        # check if dates before oct 5th 1582 are in the array
        ind_before = np.where(julian < 2299160.5)[0]
        if len(ind_before) > 0:
            A[ind_before] = Z[ind_before]

    elif calendar == 'proleptic_gregorian':
        # MC
        # alpha = int((Z - 1867216.25)/36524.25)
        # A = Z + 1 + alpha - int(alpha/4)
        alpha = np.int32(((Z - 1867216.) - 0.25) / 36524.25)
        A = Z + 1 + alpha - np.int32(0.25 * alpha)
    elif calendar == 'julian':
        A = Z
    else:
        raise ValueError(
            'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar)

    B = A + 1524
    # MC
    # C = int((B - 122.1)/365.25)
    # D = int(365.25 * C)
    C = np.atleast_1d(np.int32(6680. + ((B - 2439870.) - 122.1) / 365.25))
    D = np.atleast_1d(np.int32(365 * C + np.int32(0.25 * C)))
    E = np.atleast_1d(np.int32((B - D) / 30.6001))

    # Convert to date
    day = np.clip(B - D - np.int64(30.6001 * E) + F, 1, None)
    nday = B - D - 123
    dayofyr = nday - 305
    ind_nday_before = np.where(nday <= 305)[0]
    if len(ind_nday_before) > 0:
        dayofyr[ind_nday_before] = nday[ind_nday_before] + 60
    # MC
    # if E < 14:
    #     month = E - 1
    # else:
    #     month = E - 13

    # if month > 2:
    #     year = C - 4716
    # else:
    #     year = C - 4715
    month = E - 1
    month[month > 12] = month[month > 12] - 12
    year = C - 4715
    year[month > 2] = year[month > 2] - 1
    year[year <= 0] = year[year <= 0] - 1

    # a leap year?
    leap = np.zeros(len(year),dtype=dayofyr.dtype)
    leap[year % 4 == 0] = 1
    if calendar == 'proleptic_gregorian':
        leap[(year % 100 == 0) & (year % 400 != 0)] = 0
    elif calendar in ['standard', 'gregorian']:
        leap[(year % 100 == 0) & (year % 400 != 0) & (julian < 2299160.5)] = 0

    inc_idx = np.where((leap == 1) & (month > 2))[0]
    dayofyr[inc_idx] = dayofyr[inc_idx] + leap[inc_idx]

    # Subtract the offset from JulianDayFromDate from the microseconds (pull
    # request #433).
    eps = np.finfo(float).eps
    eps = np.maximum(eps*julian, eps)
    hour = np.clip((F * 24.).astype(np.int64), 0, 23)
    F   -= hour / 24.
    minute = np.clip((F * 1440.).astype(np.int64), 0, 59)
    # this is an overestimation due to added offset in JulianDayFromDate
    second = np.clip((F - minute / 1440.) * 86400., 0, None)
    microsecond = (second % 1)*1.e6
    # remove the offset from the microsecond calculation.
    microsecond = np.clip(microsecond - eps*86400.*1e6, 0, 999999)

    # convert year, month, day, hour, minute, second to int32
    year = year.astype(np.int32)
    month = month.astype(np.int32)
    day = day.astype(np.int32)
    hour = hour.astype(np.int32)
    minute = minute.astype(np.int32)
    second = second.astype(np.int32)
    microsecond = microsecond.astype(np.int32)

    # check if input was scalar and change return accordingly
    isscalar = False
    try:
        JD[0]
    except:
        isscalar = True
    # return a 'real' datetime instance if calendar is gregorian.
    if calendar in 'proleptic_gregorian' or \
            (calendar in ['standard', 'gregorian'] and len(ind_before) == 0):
        if not isscalar:
            return np.array([real_datetime(*args)
                             for args in
                             zip(year, month, day, hour, minute, second,
                                 microsecond)])

        else:
            return real_datetime(year[0], month[0], day[0], hour[0],
                                 minute[0], second[0], microsecond[0])
    else:
        # or else, return a 'datetime-like' instance.
        if not isscalar:
            return np.array([datetime(*args, calendar=calendar)
                             for args in
                             zip(year, month, day, hour, minute,
                                 second, microsecond, dayofwk, dayofyr)])
        else:
            return datetime(year[0], month[0], day[0], hour[0],
                            minute[0], second[0], microsecond[0], dayofwk[0],
                            dayofyr[0], calendar)


cdef _DateFromNoLeapDay(JD):
    """

returns a 'datetime-like' object given Julian Day for a calendar with no leap
days. Julian Day is a fractional day with a resolution of approximately 0.1 seconds.

    """

    # based on redate.py by <NAME>.

    if JD < 0:
        year_offset = int(-JD) // 365 + 1
        JD += year_offset * 365
    else:
        year_offset = 0

    dayofwk = int(math.fmod(int(JD + 1.5), 7))
    (F, Z) = math.modf(JD + 0.5)
{'fraction_non_alphanumeric': 0.13274497647788192, 'fraction_numerical': 0.05900587141242114, 'mean_word_length': 2.2813210482230466, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4843582', 'n_tokens_mistral': 10579, 'n_tokens_neox': 9229, 'n_words': 2720}