#!/usr/bin/env python

# This file is part of chelsa_isimip3b_ba_1km.
#
# chelsa_isimip3b_ba_1km is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# chelsa_isimip3b_ba_1km is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with chelsa_cmip6.  If not, see <https://www.gnu.org/licenses/>.

# functions ####################################################################
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def mkdir(dir = None):
    '''
    Creates the given directory.

    Parameters
    ----------
    dir : char
             Directory
    '''
    import os as __os
    if not dir is None:
        __abs_dir = __os.path.abspath(dir)
        if not __os.path.exists(__abs_dir):
            __os.makedirs(__abs_dir)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def rmdir(dir = None):
    '''
    Removes the given directory.

    Parameters
    ----------
    dir : char
             Directory
    '''
    import os as __os
    import shutil as __shutil
    if __os.path.exists(dir) and __os.path.isdir(dir):
        __shutil.rmtree(dir)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def search_file_list(dir = None, include = None, exclude = None, lsymlink = True, followlinks = True):
    '''
    Searches in the given dir for specified filename patterns.

    Parameters
    ----------
    dir : char
             Directory to search in.
    include : char or list (optional)
             String or list of patterns to include files.
    exclude : char or list (optional)
             String or list of patterns to exclude files.
    followlinks : bool (optional)
             True to follow links.
    Returns
    -------
    file_list : list of char.
             List of files.
    '''

    import os as __os
    import re as __re

    # if None, replace with ''-pattern (everything)
    if include is None:
        include = ['']
    # if string, put the string into a list
    elif type(include) is str:
        include = [include]
    # if list/tuple, check every element of the list for None (and replace with ''-pattern)
    elif type(include) in [list, tuple]:
        include = [inc if not inc is None else '' for inc in include]
    # if string, put string into a list
    if type(exclude) is str:
        exclude = [exclude]
    file_list = []
    for dirpath, dirnames, filenames in __os.walk(dir, followlinks=followlinks):
        for filename in filenames:
            # determine if dirpath+filename contains all include-patterns
            # if all(__re.search(inc, __os.path.join(dirpath, filename)) for inc in include):
            if all(__re.search(inc, filename) for inc in include):
                # if exclude was not prescribed
                if exclude is None:
                    file_list.append(__os.path.join(dirpath,filename))
                # if exclude was prescribed
                elif not any([__re.search(exc, filename) for exc in exclude]):
                    file_list.append(__os.path.join(dirpath,filename))
    return file_list


# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def get_calendar_from_type(type=None):
    '''
    Converts cftime datatypes to one of cf-convention calendar strings:
    360_day, 365_day, 366_day, proleptic_gregorian, gregorian, julian or standard

    Parameters
    ----------
    type : type
           cftime datatype

    Returns
    -------
    out : str
           cf-convention calendar string.
    '''
    import cftime as __cftime

    __type_conversion_dict = {
                              __cftime.Datetime360Day: '360_day',
                              __cftime.DatetimeNoLeap: '365_day',
                              __cftime.DatetimeAllLeap: '366_day',
                              __cftime.DatetimeProlepticGregorian: 'proleptic_gregorian',
                              __cftime.DatetimeGregorian: 'gregorian',
                              __cftime.DatetimeJulian: 'julian',
                             }

    return __type_conversion_dict.get(type, 'standard')
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


def get_type_from_calendar(calendar=None):
    '''
    Converts cf-convention calendar strings to cftime datatypes:
    Datetime360Day, DatetimeNoLeap, DatetimeAllLeap, DatetimeProlepticGregorian, DatetimeGregorian or DatetimeJulian

    Parameters
    ----------
    calendar : str
           cf-convention calendar string.

    Returns
    -------
    out : type
           cftime datatype

    '''
    import cftime as __cftime

    __calendar_conversion_dict = {
                                  '360_day': __cftime.Datetime360Day,
                                  '365_day': __cftime.DatetimeNoLeap,
                                  '366_day': __cftime.DatetimeAllLeap,
                                  'proleptic_gregorian': __cftime.DatetimeProlepticGregorian,
                                  'gregorian': __cftime.DatetimeGregorian,
                                  'standard': __cftime.DatetimeGregorian,
                                  'julian': __cftime.DatetimeJulian,
                                 }

    return __calendar_conversion_dict.get(calendar, __cftime.DatetimeGregorian)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


def leapyear_check(year=None, calendar_type='standard'):
    '''
    Determines wether a given year is a leap year.

    Parameters
    ----------
    year : int
           The year to check.
    calendar_type : char, optional
           Calendar type ("standard", "gregorian", "proleptic_gregorian", "julian").
    Returns
    -------
    leap_year : bool
           True, if given year is a leap year.
    '''

    if (year is None):
        raise NameError('ERROR: no year prescribed.')

    __year = year + 1 if (year < 0 and calendar_type in ['standard', 'gregorian', 'julian']) else year

    if calendar_type in ['standard', 'gregorian']:
        # pre 1582 the calendar is julian, afterwards gregorian
        if __year <= 1582:
            return (__year % 4 == 0)
        else:
            return ((__year % 4 == 0) and (__year % 100 != 0)) or ((__year % 4 == 0) and ((__year % 100 == 0) and (__year % 400 == 0)))
    elif calendar_type in ['proleptic_gregorian']:
        return ((__year % 4 == 0) and (__year % 100 != 0)) or ((__year % 4 == 0) and ((__year % 100 == 0) and (__year % 400 == 0)))
    elif calendar_type in ['julian']:
        return (__year % 4 == 0)
    elif calendar_type in ['366', '366_day']:
        return True
    else:
        raise NameError('ERROR: unknown calendar type (calendar_type)')
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


def date_converter(date=None, date_type=None):
    '''
    Converts the given date into the given date_type. Changing the dates accordingly,
    i.e. 31.12 (gregorian) will be converted to 30.12 (360_days) etc.

    Parameters
    ----------
    date : date object
           Date to convert.
    date_type : type object
           The date type to convert the date.
    Returns
    -------
    out : date object
           Input date converted to the given date_type.
    '''

    import numpy as __np
    import cftime as __cftime

    if date_type in [__cftime.DatetimeJulian, __cftime.DatetimeNoLeap]:
        if date.month == 2 and date.day == 29:
            print('  %Warning: need to convert {0:04}-2-29 to {0:04}-2-28'.format(date.year))
            return date_type(date.year, 2, 28, date.hour, date.minute, date.second, date.microsecond)
        else:
            return date_type(date.year, date.month, date.day, date.hour, date.minute, date.second, date.microsecond)
    elif date_type in [__cftime.Datetime360Day]:
        if date.day == 31:
            print('  %Warning: need to convert {0:04}-{1:02d}-31 to {0:04}-{1:02d}-30'.format(date.year, date.month))
            return date_type(date.year, date.month, 30, date.hour, date.minute, date.second, date.microsecond)
        else:
            return date_type(date.year, date.month, date.day, date.hour, date.minute, date.second, date.microsecond)
    elif date_type in [__np.datetime64]:
        return date_type(__cftime.DatetimeProlepticGregorian(date.year, date.month, date.day,
                                                             date.hour, date.minute, date.second, date.microsecond))
    else:
        return date_type(date.year, date.month, date.day, date.hour, date.minute, date.second, date.microsecond)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


def get_date_list(year_start=1961, year_stop=1970, calendar_type='standard', ldates=False):
    '''
    Returns a list of dates.

    Parameters
    ----------
    year_start : int
           First year of the date list.
    year_stop : int
           Last year of the date list.
    calendar_type : str, optional
           Calendar type ("standard", "gregorian", "proleptic_gregorian", "noleap", "365_day",
                          "360_day", "julian", "all_leap", "366_day").
    ldates : bool, optional
           Swtich output from seperate lists of years/months/days to list of dates (default False).

    Returns
    -------
    years : 1d array
           List of years.
           shape: [Ntime]
    months : 1d array
           List of months.
           shape: [Ntime]
    days : 1d array
           List of days.
           shape: [Ntime]
    OR
    dates : 1d array
           List of dates (if ldates == True).


    Following definitions from udunits:
    http://www.unidata.ucar.edu/software/thredds/current/netcdf-java/CDM/CalendarDateTime.html
    '''
    import numpy as __np

    if (year_start is None):
        raise NameError('ERROR: first year of date list is missing (year_start).')
    if (year_stop is None):
        raise NameError('ERROR: first year of date list is missing (year_stop).')
    if (year_stop < year_start):
        raise NameError('ERROR: year_stop < year_start')
    __days = []
    __months = []
    __years = []

    if calendar_type in ['standard', 'gregorian', 'proleptic_gregorian', 'julian']:
        __year_list = __np.arange(year_start, year_stop + 1)
        __year_list = __year_list[__year_list != 0] if calendar_type in ['standard', 'gregorian', 'julian'] else __year_list
        for __y in __year_list:
            # convert astonomical year numbering to anno domini numbering
            # WARNING: __leapyear_check also assumes astronomical year numbering
            if __y != 1582 or calendar_type in ['proleptic_gregorian', 'julian', 'standard']:
                # except for 1582 all days are set with respect to the calendar type
                for __m in __np.arange(12):
                    for __d in __np.arange(monlen(year=__y, month=__m + 1, calendar_type=calendar_type)):
                        __days.append(__d+1)
                        __months.append(__m+1)
                        __years.append(__y)
            else:
                # for 1582 there is no date between 1582-10-04 and 1582-10-15
                for __m in __np.arange(12):
                    if __m == 9:
                        for __d in __np.arange(21):
                            if __d <= 3:
                                __days.append(__d+1)
                                __months.append(__m+1)
                                __years.append(__y)
                            else:
                                __days.append(__d+11)
                                __months.append(__m+1)
                                __years.append(__y)
                    else:
                        for __d in __np.arange(monlen(year=__y, month=__m + 1, calendar_type=calendar_type)):
                            __days.append(__d+1)
                            __months.append(__m+1)
                            __years.append(__y)
    elif calendar_type in ['365', 'noleap', '365_day' '366', 'all_leap', '366_day', '360', '360_day']:
        for __y in __np.arange(year_start, year_stop + 1):
            for __m in __np.arange(12):
                for __d in __np.arange(monlen(year=__y, month=__m + 1, calendar_type=calendar_type)):
                    __days.append(__d+1)
                    __months.append(__m+1)
                    __years.append(__y)
    else:
        raise NameError('ERROR: unknown calendar type (calendar_type)')

    if ldates:
        import cftime as __cftime
        if calendar_type in ['standard', 'gregorian']:
            return __np.array([__cftime.DatetimeGregorian(year, month, day) for year, month, day in zip(__years,__months,__days)])
        elif calendar_type in ['proleptic_gregorian']:
            return __np.array([__cftime.DatetimeProlepticGregorian(year, month, day) for year, month, day in zip(__years,__months,__days)])
        elif calendar_type in ['julian']:
            return __np.array([__cftime.DatetimeJulian(year, month, day) for year, month, day in zip(__years,__months,__days)])
        elif calendar_type in ['365', 'noleap', '365_day']:
            return __np.array([__cftime.DatetimeNoLeap(year, month, day) for year, month, day in zip(__years,__months,__days)])
        elif calendar_type in ['366', 'all_leap', '366_day']:
            return __np.array([__cftime.DatetimeAllLeap(year, month, day) for year, month, day in zip(__years,__months,__days)])
        elif calendar_type in ['360', '360_day']:
            return __np.array([__cftime.Datetime360Day(year, month, day) for year, month, day in zip(__years,__months,__days)])
        else:
            raise NameError('ERROR: unknown calendar type (calendar_type)')
    else:
        return __np.array(__years), __np.array(__months), __np.array(__days)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


def monlen(year=None, month=None, calendar_type='standard'):
    '''
    Determines the length of the given month in the a given year.

    Parameters
    ----------
    year : int
           The year.
    month : int
           The month.
    calendar_type : char, optional
           Calendar type ('standard', 'gregorian', 'proleptic_gregorian', 'julian').
    Returns
    -------
    length : int
           The length of that month in that year.
    '''

    if (year is None):
        raise NameError('ERROR: no year prescribed.')
    if (month is None):
        raise NameError('ERROR: no month prescribed.')

    if calendar_type in ['standard', 'gregorian', 'proleptic_gregorian', 'julian']:
        __monlen = [31, 28 + leapyear_check(year=year, calendar_type=calendar_type), 31, 30, 31,
                    30, 31, 31, 30, 31, 30, 31]
    elif calendar_type in ['365', 'noleap', '365_day']:
        __monlen = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    elif calendar_type in ['366', 'all_leap', '366_day']:
        __monlen = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    elif calendar_type in ['360', '360_day']:
        __monlen = [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]
    else:
        raise NameError('ERROR: unknown calendar type (calendar_type)')

    return __monlen[month-1]
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


class index_engine:
    def __init__(self, years=None, months=None, days=None, calendar=None):
        # input years/months/days will be converted

        import numpy as __np

        self.years = __np.array(years)  # input year
        self.months = __np.array(months)  # input month
        self.days = __np.array(days)   # input day
        self.calendar = calendar           # input calendar
        if self.calendar in ['360', '360_day']:
            self.get_index = self.get_index_360
        elif self.calendar in ['365', '365_day']:
            self.get_index = self.get_index_365
        elif self.calendar in ['366', '366_day']:
            self.get_index = self.get_index_366
        else:
            self.get_index = self.get_index_standard

    # 360 day calendar interpolations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    def get_index_360(self, year=None, month=None, day=None, calendar='standard'):

        import numpy as __np

        tindex = []

        if calendar in ['standard', 'gregorian', 'proleptic_gregorian', 'julian', '365', '365_day', '366', '366_day']:
            # 30 day months: new 30. = mean of 29 and 30 -------------------------------
            if month in [1, 5, 7, 8, 10, 12] and day == 30:

                tindex_left = __np.where(__np.all((self.years == year, self.months == month, self.days == 29),
                                                  axis=0))[0]
                tindex_right = __np.where(__np.all((self.years == year, self.months == month, self.days == day),
                                                   axis=0))[0]
                if __np.size(tindex_left) > 1 or __np.size(tindex_right) > 1:
                    raise NameError('ERROR: dublicated dates found')
                elif __np.size(tindex_left) == 1 and __np.size(tindex_right) == 1:
                    tindex = __np.hstack((tindex_left[0], tindex_right[0]))

            # 30 day months: 31 = 30 ---------------------------------------------------
            elif month in [1, 5, 7, 8, 10, 12] and day == 31:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == 30), axis=0))[0]

            # feb: new 28 = mean of 28 and 29 on non leap years ------------------------
            elif month == 2 and not leapyear_check(year, calendar_type=calendar) and day == 28:
                tindex_left = __np.where(__np.all((self.years == year, self.months == month, self.days == day),
                                                  axis=0))[0]
                tindex_right = __np.where(__np.all((self.years == year, self.months == month, self.days == 29),
                                                   axis=0))[0]
                if __np.size(tindex_left) > 1 or __np.size(tindex_right) > 1:
                    raise NameError('ERROR: dublicated dates found')
                elif __np.size(tindex_left) == 1 and __np.size(tindex_right) == 1:
                    tindex = __np.hstack((tindex_left[0], tindex_right[0]))

            # mar: 01.03 -> 30.02 and dd.03 -> (dd-1).03 -------------------------------
            elif month == 3:
                if day == 1:
                    tindex = __np.where(__np.all((self.years == year, self.months == 2, self.days == 30), axis=0))[0]
                else:
                    tindex = __np.where(__np.all((self.years == year, self.months == month,  self.days == (day-1)),
                                                 axis=0))[0]

            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['360', '360_day']:
            # leave every other day unchanged ------------------------------------------
            tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]
        else:
            raise NameError('ERROR: unknown calendar.')

        return tindex

    # 365 day calendar interpolations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    def get_index_365(self, year=None, month=None, day=None, calendar='standard'):

        import numpy as __np

        tindex = []

        if calendar in ['standard', 'gregorian', 'proleptic_gregorian', 'julian', '366', '366_day']:
            # feb: 28.02 = mean of 27.02 and 28.02 on leap years -----------------------
            if month == 2 and day == 28 and leapyear_check(year, calendar_type=calendar):
                tindex_left = __np.where(__np.all((self.years == year, self.months == month, self.days == 27),
                                                  axis=0))[0]
                tindex_right = __np.where(__np.all((self.years == year, self.months == month, self.days == day),
                                                   axis=0))[0]
                if __np.size(tindex_left) > 1 or __np.size(tindex_right) > 1:
                    raise NameError('ERROR: dublicated dates found')
                elif __np.size(tindex_left) == 1 and __np.size(tindex_right) == 1:
                    tindex = __np.hstack((tindex_left[0], tindex_right[0]))

            # feb: 29.02 = 28.02 -------------------------------------------------------
            # 29.02 will only appear on leap years in 365-calendar -> no check necessary
            elif month == 2 and day == 29:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == 28), axis=0))[0]
            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['360', '360_day']:
            # feb: 31.01 - 28.02 -> 01.02 - 29.02 & 01.03 - 31.03 -> 30.02 - 30.03
            if month == 2:
                if day == 1:
                    tindex = __np.where(__np.all((self.years == year, self.months == 1, self.days == 31), axis=0))[0]
                elif day == 30:
                    tindex = __np.where(__np.all((self.years == year, self.months == 3, self.days == 1), axis=0))[0]
                else:
                    tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == (day-1)),
                                                 axis=0))[0]
            elif month == 3:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == (day+1)),
                                             axis=0))[0]

            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['365', '365_day']:
            # leave every other day unchanged ------------------------------------------
            tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        else:
            raise NameError('ERROR: unknown calendar.')

        return tindex

    # 366 day calendar %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    def get_index_366(self, year=None, month=None, day=None, calendar='standard'):

        import numpy as __np

        tindex = []

        if calendar in ['standard', 'gregorian', 'proleptic_gregorian', 'julian', '365', '365_day', '366', '366_day']:
            # 30.mm -> 31.mm and mean of 29.mm and 30.mm will be the new 30.mm
            if month in [1, 3, 5, 7, 8, 10, 12] and day in [30, 31]:
                if day == 31:
                    tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == 30),
                                                 axis=0))[0]
                else:
                    tindex_left = __np.where(__np.all((self.years == year, self.months == month, self.days == 29),
                                                      axis=0))[0]
                    tindex_right = __np.where(__np.all((self.years == year, self.months == month, self.days == 30),
                                                       axis=0))[0]
                    if __np.size(tindex_left) > 1 or __np.size(tindex_right) > 1:
                        raise NameError('ERROR: dublicated dates found')
                    elif __np.size(tindex_left) == 1 and __np.size(tindex_right) == 1:
                        tindex = __np.hstack((tindex_left[0], tindex_right[0]))
            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['360', '360_day']:
            # leave every other day unchanged ------------------------------------------
            tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        else:
            raise NameError('ERROR: unknown calendar.')

        return tindex

    # standard calendar %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    def get_index_standard(self, year=None, month=None, day=None, calendar='standard'):

        import numpy as __np

        tindex = []

        if calendar in ['standard', 'gregorian', 'proleptic_gregorian', 'julian', '365', '365_day']:
            tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['366', '366_day']:
            # 29.02 for non-standard leap years (interpolate between 28.02 and 01.03)
            if month == 2 and day == 29 and not leapyear_check(year, calendar_type='standard'):
                tindex_left = __np.where(__np.all((self.years == year, self.months == 2, self.days == 28), axis=0))[0]
                tindex_right = __np.where(__np.all((self.years == year, self.months == 3, self.days == 1), axis=0))[0]
                if __np.size(tindex_left) > 1 or __np.size(tindex_right) > 1:
                    raise NameError('ERROR: dublicated dates found')
                elif __np.size(tindex_left) == 1 and __np.size(tindex_right) == 1:
                    tindex = __np.hstack((tindex_left[0], tindex_right[0]))
            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]

        elif calendar in ['360', '360_day']:
            # 31.01 -> 01.02
            if month == 2 and day == 1:
                tindex = __np.where(__np.all((self.years == year, self.months == 1, self.days == 31), axis=0))[0]
            # leap years:     01.02 - 29.02 -> 2.02 - 30.02
            # non-leap years: 01.02 - 01.03 -> 02.02 - 30.02 and 02.03 - 31.03 -> 01.03 - 30.03
            elif leapyear_check(year, calendar_type='standard') and month == 2:
                tindex = __np.where(__np.all((self.years == year, self.months == 2, self.days == (day+1)), axis=0))[0]
            elif not leapyear_check(year, calendar_type='standard') and month == 2:
                if day == 30:
                    tindex = __np.where(__np.all((self.years == year, self.months == 3, self.days == 1), axis=0))[0]
                else:
                    tindex = __np.where(__np.all((self.years == year, self.months == 2, self.days == (day+1)),
                                                 axis=0))[0]
            elif not leapyear_check(year, calendar_type='standard') and month == 3:
                tindex = __np.where(__np.all((self.years == year, self.months == 3, self.days == (day+1)), axis=0))[0]
            # leave every other day unchanged ------------------------------------------
            else:
                tindex = __np.where(__np.all((self.years == year, self.months == month, self.days == day), axis=0))[0]
        else:
            raise NameError('ERROR: unknown calendar.')

        return tindex
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



