Source code for pytesmo.timedate.dekad

# Copyright (c) 2014, Vienna University of Technology (TU Wien), Department
# of Geodesy and Geoinformation (GEO).
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the Vienna University of Technology - Department of
#   Geodesy and Geoinformation nor the names of its contributors may be used to
#   endorse or promote products derived from this software without specific
#   prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
# DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Author: Thomas Mistelbauer thomas.mistelbauer@geo.tuwien.ac.at
# Creation date: 2014-08-05

"""
This module provides functions for date manipulation on a dekadal basis.

A dekad is defined as days 1-10, 11-20 and 21-last day of a month.

Or in numbered dekads:

1: day 1-10
2: day 11-20
3: day 21-last
"""

import calendar
import pandas as pd
import math
from datetime import datetime


[docs]def dekad_index(begin, end=None): """Creates a pandas datetime index on a decadal basis. Parameters ---------- begin : datetime Datetime index start date. end : datetime, optional Datetime index end date, set to current date if None. Returns ------- dtindex : pandas.DatetimeIndex Dekadal datetime index. """ if end is None: end = datetime.now() mon_begin = datetime(begin.year, begin.month, 1) mon_end = datetime(end.year, end.month, 1) daterange = pd.date_range(mon_begin, mon_end, freq='MS') dates = [] for i, dat in enumerate(daterange): lday = calendar.monthrange(dat.year, dat.month)[1] if i == 0 and begin.day > 1: if begin.day < 11: if daterange.size == 1: if end.day < 11: dekads = [10] elif end.day >= 11 and end.day < 21: dekads = [10, 20] else: dekads = [10, 20, lday] else: dekads = [10, 20, lday] elif begin.day >= 11 and begin.day < 21: if daterange.size == 1: if end.day < 21: dekads = [20] else: dekads = [20, lday] else: dekads = [20, lday] else: dekads = [lday] elif i == (len(daterange) - 1) and end.day < 21: if end.day < 11: dekads = [10] else: dekads = [10, 20] else: dekads = [10, 20, lday] for j in dekads: dates.append(pd.datetime(dat.year, dat.month, j)) dtindex = pd.DatetimeIndex(dates) return dtindex
[docs]def check_dekad(date): """Checks the dekad of a date and returns the dekad date. Parameters ---------- date : datetime Date to check. Returns ------- new_date : datetime Date of the dekad. """ if date.day < 11: dekad = 10 elif date.day > 10 and date.day < 21: dekad = 20 else: dekad = calendar.monthrange(date.year, date.month)[1] new_date = datetime(date.year, date.month, dekad) return new_date
[docs]def dekad_startdate_from_date(dt_in): """ dekadal startdate that a date falls in Parameters ---------- run_dt: datetime.datetime Returns ------- startdate: datetime.datetime startdate of dekad """ if dt_in.day <= 10: startdate = datetime(dt_in.year, dt_in.month, 1, 0, 0, 0) if dt_in.day >= 11 and dt_in.day <= 20: startdate = datetime(dt_in.year, dt_in.month, 11, 0, 0, 0) if dt_in.day >= 21: startdate = datetime(dt_in.year, dt_in.month, 21, 0, 0, 0) return startdate
[docs]def check_dekad_enddate(dt): """ Check if a date is a dekad enddate """ return check_dekad(dt) == dt
[docs]def check_dekad_startdate(dt): """ Check if a date is a dekad startdate """ if dt.day in [1, 11, 21]: return True else: return False
[docs]def group_into_dekads(dates, use_dekad_startdate=False): """ Group a list of dates into dekads. Parameters ---------- dates: list of datetime.datetime use_dekad_startdates: boolean, optional If set the dekad reference dates will be the startdates of the dekad Returns ------- groups: dict keys: dekad reference dates values: list of dates belonging to dekad """ groups = {} for dt in dates: dekad_date = check_dekad(dt) if use_dekad_startdate: dekad_date = dekad_startdate_from_date(dekad_date) if dekad_date not in groups: groups[dekad_date] = [] groups[dekad_date].append(dt) return groups
[docs]def dekad2day(year, month, dekad): """Gets the day of a dekad. Parameters ---------- year : int Year of the date. month : int Month of the date. dekad : int Dekad of the date. Returns ------- day : int Day value for the dekad. """ if dekad == 1: day = 10 elif dekad == 2: day = 20 elif dekad == 3: day = calendar.monthrange(year, month)[1] return day
[docs]def runningdekad2date(year, rdekad): """Gets the date of the running dekad of a spacifc year. Parameters ---------- year : int Year of the date. rdekad : int Running dekad of the date. Returns ------- datetime.datetime Date value for the running dekad. """ month = int(math.ceil(rdekad / 3.)) dekad = rdekad - month * 3 + 3 day = dekad2day(year, month, dekad) return datetime(year, month, day)
[docs]def day2dekad(day): """Returns the dekad of a day. Parameters ---------- day : int Day of the date. Returns ------- dekad : int Number of the dekad in a month. """ if day < 11: dekad = 1 elif day > 10 and day < 21: dekad = 2 else: dekad = 3 return dekad
[docs]def get_dekad_period(dates): """Checks number of the dekad in the current year for dates given as list. Parameters ---------- dates : list of datetime Dates to check. Returns ------- period : list of int List of dekad periods. """ period = [] for dat in dates: d = check_dekad(dat) dekad = day2dekad(d.day) per = dekad + ((d.month - 1) * 3) period.append(per) return period