Source code for cwatm.management_modules.timestep

# -------------------------------------------------------------------------
# Name:        Handling of timesteps and dates
# Purpose: Temporal management system handling dates, calendars, and time stepping.
# Supports multiple calendar systems and NetCDF temporal coordinate processing.
# Manages simulation period validation and time-based data indexing.
#
# Author:      P. Burek
# Created:     09/08/2016
# CWatM is licensed under GNU GENERAL PUBLIC LICENSE Version 3.
# -------------------------------------------------------------------------

import calendar
import datetime
import difflib  # to check the closest word in settingsfile, if an error occurs
import os
import time as xtime

import numpy as np
from netCDF4 import Dataset, num2date, date2num, date2index

from cwatm.management_modules.data_handling import *
from cwatm.management_modules.globals import *
from cwatm.management_modules.messages import *

[docs]def datenum(date): """ Convert date to integer number based on NetCDF calendar and units. Converts a datetime object to an integer representation based on the calendar system and time units defined in the NetCDF meteorological data files. This enables consistent temporal indexing across different calendar systems (standard, 360-day, noleap) used in climate data. Parameters ---------- date : datetime.datetime Date to convert to numeric representation. Returns ------- int Integer representation of the date in NetCDF time units. Adjusted for the unit conversion factor (daily, hourly, etc.). Notes ----- Uses global dateVar dictionary containing: - unit: NetCDF time units (e.g., "days since 1901-01-01") - calendar: Calendar system ('standard', '360_day', 'noleap', etc.) - unitConv: Conversion factor for different time resolutions The function applies rounding to handle NetCDF files that use 12:00 as the starting time, which results in half-day offsets. """ num = round(date2num(date, units=dateVar['unit'], calendar=dateVar['calendar'])) # changed to round because some date in netcdf have 12:00 as starting time -> results in -0.5 return num // dateVar['unitConv']
[docs]def numdate(num, add=0): """ Convert integer to date based on NetCDF calendar and units. Converts an integer representation back to a datetime object using the calendar system and time units from NetCDF meteorological files. This is the inverse operation of datenum() and maintains temporal consistency across different calendar systems. Parameters ---------- num : int or float Numeric representation of the date in NetCDF time units. add : int, optional Additional days to add to the resulting date. Default is 0. Returns ------- datetime.datetime Datetime object corresponding to the numeric representation. Notes ----- Uses global dateVar dictionary for calendar and unit information. The function handles various NetCDF time representations and calendar systems commonly used in meteorological and climate data. Used primarily for: - Converting NetCDF time indices back to calendar dates - Temporal calculations requiring date arithmetic - Output timestamp generation """ return (num2date(int(num) * dateVar['unitConv'] + add, units=dateVar['unit'], calendar=dateVar['calendar']))
[docs]def date2str(date): """ Convert datetime object to standardized date string format. Converts a datetime object to a consistent string representation used throughout CWatM for output formatting, logging, and user display. The format is day/month/year with zero padding. Parameters ---------- date : datetime.datetime Date to convert to string representation. Returns ------- str Date string in format "dd/mm/yyyy" (e.g., "27/12/2018"). Notes ----- - Uses zero-padding for single-digit days and months - Provides consistent date formatting across all CWatM outputs - Handles dates before 1900 that cause issues with strftime() - Used in progress reporting, output headers, and user interfaces """ return "%02d/%02d/%02d" % (date.day, date.month, date.year)
[docs]def ctbinding(inBinding): """ Check and retrieve configuration binding with error handling. Validates that a configuration parameter exists in the settings file and retrieves its value. Provides helpful error messages with closest matches when parameters are not found, assisting with debugging configuration issues. Parameters ---------- inBinding : str Configuration parameter name to look up in the binding dictionary. Returns ------- str Value of the configuration parameter from the binding dictionary. Raises ------ CWATMError If the parameter is not found in the binding dictionary. Error message includes closest matching parameter names for debugging. Notes ----- - Used specifically for time-related configuration parameters - Provides fuzzy matching suggestions using difflib for typo detection - Part of the configuration validation system - Essential for robust parameter handling in temporal calculations The function helps ensure that all required time-related parameters are properly configured before model execution begins. """ test = inBinding in binding if test: return binding[inBinding] else: # not tested because you have to remove eg stepstart to test this closest = difflib.get_close_matches(inBinding, list(binding.keys())) if not closest: closest = ["- no match -"] msg = "Error 118: ===== Timing in the section: [TIME-RELATED_CONSTANTS] is wrong! =====\n" msg += "No key with the name: \"" + inBinding + "\" in the settings file: \"" + settingsfile[0] + "\"\n" msg += "Closest key to the required one is: \""+ closest[0] + "\"" raise CWATMError(msg)
[docs]def timemeasure(name, loops=0, update=False, sample=1): """ Measure execution time for model subroutines and components. Records high-precision timestamps for performance profiling of CWatM components. Enables detailed analysis of computational bottlenecks and optimization of model performance. Part of the timing infrastructure used when the printtime flag is enabled. Parameters ---------- name : str Name of the subroutine or component being measured. loops : int, optional Loop counter appended to name for repeated calls. Default is 0. update : bool, optional Flag for updating measurements (currently unused). Default is False. sample : int, optional Sampling parameter (currently unused). Default is 1. Returns ------- None Appends timestamp and identifier to global timing arrays. Notes ----- Uses global arrays: - timeMes: List of high-precision timestamps (perf_counter) - timeMesString: List of corresponding subroutine identifiers When loops > 0, the identifier becomes "name_N" where N is the loop number. This allows tracking of iterative processes and repeated function calls. Timing information is processed and reported when the model completes if the printtime command-line option is specified. """ timeMes.append(xtime.perf_counter()) if loops == 0: s = name else: s = name + "_%i" % loops timeMesString.append(s) return
# ----------------------------------------------------------------------- # Calendar routines # -----------------------------------------------------------------------
[docs]def Calendar(input, errorNo=0): """ Parse and validate date strings from configuration settings. Robust date parser that handles multiple date formats commonly used in configuration files. Converts various date representations to datetime objects while providing specific error messages for different contexts (start dates, initialization dates, etc.). Parameters ---------- input : str or float Date string or numeric value from settings file. Supports formats: dd/mm/yyyy, dd-mm-yyyy, dd.mm.yyyy, or numeric. errorNo : int, optional Error context identifier: - 0: Start/end date validation (default) - 1: Initialization date validation - >1: Returns -99999 for invalid dates instead of raising error Returns ------- datetime.datetime or float or int - datetime.datetime: Successfully parsed date - float: Numeric input passed through unchanged - int: Special return value (-99999) for invalid dates when errorNo > 1 Raises ------ CWATMError If date parsing fails and errorNo <= 1, with context-specific messages. Notes ----- Date format handling: - Automatically detects 2-digit vs 4-digit years - Handles various separators (/, -, .) - Supports both European (dd/mm/yyyy) and numeric formats - Provides flexibility for international date conventions Used throughout the temporal configuration system for parsing start dates, end dates, and initialization timestamps. """ date = None try: date = float(input) except ValueError: d = input.replace('.', '/') d = d.replace('-', '/') year = d.split('/')[-1:] if len(year[0]) == 4: formatstr = "%d/%m/%Y" else: formatstr = "%d/%m/%y" if len(year[0]) == 1: d = d.replace('/', '.', 1) d = d.replace('/', '/0') d = d.replace('.', '/') print(d) try: date = datetime.datetime.strptime(d, formatstr) except: if errorNo == 0: msg = ("Error 119: Either date in StepStart is not a date or in SpinUp or StepEnd " "it is neither a number or a date!") raise CWATMError(msg) elif errorNo == 1: msg = "Error 120: First date in StepInit is neither a number or a date!" raise CWATMError(msg) elif errorNo > 1: return -99999 return date
[docs]def datetoInt(dateIn, begin, both=False): """ Convert date to integer offset from reference date. Calculates the integer number of days between a given date and a reference date, handling various calendar systems. This is fundamental for CWatM's internal time indexing system, enabling consistent temporal calculations across different calendar types. Parameters ---------- dateIn : str or float Input date string or numeric value to convert. begin : datetime.datetime Reference date for calculating the offset. both : bool, optional If True, returns both integer offset and string representation. Default is False (returns only integer). Returns ------- int or tuple If both=False: Integer day offset from reference date (1-based). If both=True: Tuple of (integer_offset, date_string). Notes ----- The function handles two input types: 1. Date strings: Parsed using Calendar() and converted to day offsets 2. Numeric values: Used directly as integer offsets The offset is 1-based, meaning the reference date itself returns 1. This aligns with CWatM's internal timestep numbering system. Critical for: - Converting configuration dates to internal timesteps - Temporal indexing in meteorological data - Simulation period calculations """ date1 = Calendar(dateIn) if type(date1) is datetime.datetime: # str1 = date1.strftime("%d/%m/%Y") # to cope with dates before 1990 str1 = date2str(date1) d1 = datenum(date1) d2 = datenum(begin) int1 = int(d1 - d2) + 1 # to be used with different days in a year e.g. 360_years else: int1 = int(date1) str1 = str(date1) if both: return int1, str1 else: return int1
[docs]def addmonths(d, x): """ Add months to a date with proper handling of month boundaries. Performs date arithmetic that correctly handles month addition across year boundaries and varying month lengths. Ensures that invalid dates (e.g., February 30) are adjusted to valid dates by using the last valid day of the target month. Parameters ---------- d : datetime.datetime Starting date for the addition operation. x : int Number of months to add (can be negative for subtraction). Returns ------- datetime.datetime New date with the specified number of months added. Notes ----- Month addition algorithm: - Calculates target month and year accounting for wraparound - Handles leap years and varying month lengths correctly - Adjusts day to last valid day if original day exceeds month length For example: - January 31 + 1 month = February 28 (or 29 in leap years) - December 15 + 2 months = February 15 - May 31 + 1 month = June 30 Used in initialization date calculations and temporal sequence generation for save state operations and periodic output scheduling. """ days_of_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] newmonth = (((d.month - 1) + x) % 12) + 1 newyear = d.year + (((d.month - 1) + x) // 12) if d.day > days_of_month[newmonth - 1]: newday = days_of_month[newmonth - 1] else: newday = d.day return datetime.datetime(newyear, newmonth, newday)
[docs]def datetosaveInit(initdates, begin, end): """ Calculate and validate initialization save dates for model state output. Processes a list of dates or date patterns to determine when the model should save its internal state for initialization purposes. Supports both explicit dates and periodic patterns (yearly, monthly, daily). This is critical for model restarts and warm start capabilities. Parameters ---------- initdates : list List of date specifications that can include: - Explicit date strings - Numeric offsets - Periodic patterns (e.g., "2y", "6m", "30d") begin : datetime.datetime Reference start date for the simulation period. end : datetime.datetime End date limiting the save date generation. Returns ------- None Populates global dateVar['intInit'] list with integer timesteps. Raises ------ CWATMError If date patterns are malformed or numeric values are invalid. Notes ----- Periodic pattern formats: - "Ny": Every N years (e.g., "2y" = every 2 years) - "Nm": Every N months (e.g., "6m" = every 6 months) - "Nd": Every N days (e.g., "30d" = every 30 days) The function generates sequences of save dates starting from the first specified date and continuing until the simulation end date. This enables flexible state saving strategies for long simulations. Global variables modified: - dateVar['intInit']: List of integer timesteps for state saving """ # datetosaveInit(initdates, dateVar['dateBegin'], dateVar['dateEnd']) # dd = datetoInt(d, dateVar['dateBegin']) # dateVar['intInit'].append(datetoInt(d, dateVar['dateBegin'])) i = 0 dateVar['intInit'] = [] dd = [] for d in initdates: i += 1 date1 = Calendar(d, i) # check if it a row of dates if date1 == -99999: if not(d[-1] in ["d", "m", "y"]): msg = "Error 121: Second value in StepInit is not a number or date nor indicating a repetition of year(y), month(m) or day(d) \n" msg += "e.g. 2y for every 2 years or 6m for every 6 month" raise CWATMError(msg) else: try: add = int(d[0:-1]) except: msg = "Error 122: Third value in StepInit is not an integer after 'y' or 'm' or 'd'" raise CWATMError(msg) # start = begin + datetime.timedelta(days=dateVar['intInit'][0]-1) d1 = datenum(begin) start = numdate(d1, dateVar['intInit'][0] - 1) j = 1 while True: if d[-1] == 'y': #date2 = start + relativedelta(years=+ add * j) date2 = start try: date2 = date2.replace(year=date2.year + add * j) except ValueError: # date2 = date2 - datetime.timedelta(days = 1) d1 = datenum(date2) date2 = numdate(d1, -1) date2 = date2.replace(year=date2.year + add * j) elif d[-1] == 'm': # date2 = start + relativedelta(months=+ add * j) date2 = addmonths(start, add * j) else: # date2 = start + datetime.timedelta(days= add * j) d1 = datenum(start) date2 = numdate(d1, add * j) if date2 > end: break else: # int1 = (date2 - begin).days + 1 d1 = datenum(date2) d2 = datenum(begin) int1 = int(d1 - d2) + 1 dateVar['intInit'].append(int1) dd.append(date2) j += 1 return if type(date1) is datetime.datetime: # int1 = (date1 - begin).days + 1 d1 = datenum(date1) d2 = datenum(begin) int1 = int(d1 - d2) + 1 else: int1 = int(date1) dateVar['intInit'].append(int1) ii = 1
# noinspection PyTypeChecker
[docs]def checkifDate(start, end, spinup, name): """ Validate temporal configuration and initialize date variables. Comprehensive validation of simulation temporal bounds, including start/end dates, spinup period, and calendar system setup. Extracts calendar information from meteorological data files and performs extensive date consistency checks. Critical for proper temporal framework initialization. Parameters ---------- start : str Configuration key for simulation start date. end : str Configuration key for simulation end date. spinup : str Configuration key for spinup end date (when outputs begin). name : str Path to meteorological file for calendar system extraction. Returns ------- None Populates global dateVar dictionary with temporal parameters. Raises ------ CWATMFileError If meteorological file cannot be found or accessed. CWATMError If date validation fails or temporal bounds are inconsistent. Notes ----- The function performs: 1. NetCDF calendar and units extraction from meteorological data 2. Unit conversion factor calculation (daily, hourly, etc.) 3. Date parsing and validation for start, end, and spinup dates 4. Temporal consistency checks (start < spinup <= end) 5. Calendar-specific day counting and period calculations Global dateVar dictionary populated with: - Date objects: dateBegin, dateStart, dateEnd - Integer offsets: intStart, intEnd, intSpin - Calendar info: calendar, unit, unitConv - Temporal markers: checked list for month/year boundaries - Period counts: diffdays, diffMonth, diffYear Essential for all subsequent temporal operations in CWatM. """ # begin = Calendar(ctbinding('CalendarDayStart')) try: name = glob.glob(os.path.normpath(name))[0] except: msg = "Error 215: Cannot find precipitation maps\n" raise CWATMFileError(name,msg, sname='PrecipitationMaps') nf1 = Dataset(name, 'r') try: dateVar['calendar'] = nf1.variables['time'].calendar dateVar['unit'] = nf1.variables['time'].units except: dateVar['calendar'] = 'standard' dateVar['unit'] = "days since 1901-01-01T00:00:00Z" nf1.close() unitconv1 = ["DAYS","HOUR","MINU","SECO"] unitconv2 = [1,24,1440,86400] unitconv3 = dateVar['unit'] [:4].upper() try: dateVar['unitConv'] = unitconv2[unitconv1.index(unitconv3)] except: dateVar['unitConv'] = 1 startdate = Calendar(ctbinding('StepStart')) if type(startdate) is datetime.datetime: begin = startdate else: msg = "Error 123: \"StepStart = " + ctbinding('StepStart') + "\"\n" msg += "StepStart has to be a valid date!" raise CWATMError(msg) # spinup date = date from which maps are written if ctbinding(spinup).lower() == "none" or ctbinding(spinup) == "0": spinup = start dateVar['intStart'], strStart = datetoInt(ctbinding(start), begin, True) dateVar['intEnd'], strEnd = datetoInt(ctbinding(end), begin, True) dateVar['intSpin'], strSpin = datetoInt(ctbinding(spinup), begin, True) # test if start and end > begin if (dateVar['intStart'] < 0) or (dateVar['intEnd'] < 0) or ((dateVar['intEnd'] - dateVar['intStart']) < 0): # strBegin = begin.strftime("%d/%m/%Y") strBegin = date2str(begin) msg = "Error 124: Start Date: " + strStart + " and/or end date: " + strEnd + " are wrong!\n or smaller than the first time step date: " + strBegin raise CWATMError(msg) if (dateVar['intSpin'] < dateVar['intStart']) or (dateVar['intSpin'] > dateVar['intEnd']): # strBegin = begin.strftime("%d/%m/%Y") strBegin = date2str(begin) msg = "Error 125: Spin Date: " + strSpin + " is wrong!\n or smaller/bigger than the first/last time step date: " + strBegin + " - " + strEnd raise CWATMError(msg) dateVar['currDate'] = begin dateVar['dateBegin'] = begin # dateVar['dateStart'] = begin + datetime.timedelta(days=dateVar['intSpin']-1) d1 = datenum(begin) startint = int(d1 + dateVar['intSpin'] - 1) dateVar['dateStart'] = numdate(startint) dateVar['diffdays'] = dateVar['intEnd'] - dateVar['intSpin'] + 1 # dateVar['dateEnd'] = dateVar['dateStart'] + datetime.timedelta(days=dateVar['diffdays']-1) dateVar['dateStart1'] = begin + datetime.timedelta(days=dateVar['intSpin'] - 1) dateVar['dateEnd1'] = dateVar['dateStart1'] + datetime.timedelta(days=dateVar['diffdays'] - 1) d1 = datenum(dateVar['dateStart']) endint = int(d1 + dateVar['diffdays']) dateVar['dateEnd'] = numdate(endint, -1) dateVar['curr'] = 0 dateVar['currwrite'] = 0 #dateVar['datelastmonth'] = datetime.datetime(year=dateVar['dateEnd'].year, month= dateVar['dateEnd'].month, day=1) - datetime.timedelta(days=1) d1 = datenum(datetime.datetime(year=dateVar['dateEnd'].year, month= dateVar['dateEnd'].month, day=1)) dateVar['datelastmonth'] = numdate(d1, -1) #dateVar['datelastyear'] = datetime.datetime(year=dateVar['dateEnd'].year, month= 1, day=1) - datetime.timedelta(days=1) d1 = datenum(datetime.datetime(year=dateVar['dateEnd'].year, month=1, day=1)) dateVar['datelastyear'] = numdate(d1, -1) dateVar['checked'] = [] # noinspection PyTypeChecker # dates = np.arange(dateVar['dateStart'], dateVar['dateEnd']+ datetime.timedelta(days=1), datetime.timedelta(days = 1)).astype(datetime.datetime) # for d in dates: # mid of month days for dint in range(startint, endint): d = numdate(dint) #dnext = numdate(dint, 1) dnext = numdate(dint,dateVar['unitConv']) # changed PB 25/09/25 -> if precitpuiation comes as second -> use the convertion anyway # if d.day == calendar.monthrange(d.year, d.month)[1]: if d.month != dnext.month: if d.month == 12: dateVar['checked'].append(2) else: dateVar['checked'].append(1) else: # mark mid of month day # if d.month == 2 and d.day==14: # dateVar['checked'].append(-1) # if d.month != 2 and d.day==15: # dateVar['checked'].append(-1) dateVar['checked'].append(0) dateVar['diffMonth'] = dateVar['checked'].count(1) + dateVar['checked'].count(2) dateVar['diffYear'] = dateVar['checked'].count(2)
[docs]def date2indexNew(date, nctime, calendar, select='nearest', name=""): """ Enhanced date-to-index conversion for NetCDF files with extended time unit support. Extends the standard NetCDF4 date2index functionality to handle monthly and yearly time units that are not supported by the original implementation. Critical for accessing climatological data with non-daily temporal resolution commonly used in hydrological modeling. Parameters ---------- date : datetime.datetime Target date to find in the NetCDF time dimension. nctime : netCDF4.Variable NetCDF time variable containing units and values. calendar : str Calendar system used in the NetCDF file. select : str, optional Selection method for nearest date matching. Default is 'nearest'. name : str, optional Dataset name for enhanced error reporting. Default is empty string. Returns ------- int Array index corresponding to the specified date in the NetCDF time dimension. Notes ----- Supported time units: - DAYS: Standard daily resolution (uses original netCDF4.date2index) - MONTHS: Monthly resolution with year-month indexing - YEARS: Annual resolution with year indexing For monthly/yearly data, the function: - Calculates temporal offset from reference date in units - Handles cases where requested date exceeds available data range - Issues warnings when using boundary data for out-of-range requests - Performs array lookup to find matching time indices Essential for accessing diverse climatological datasets with varying temporal resolutions in hydrological modeling applications. """ unit = nctime.units.split() if unit[0].upper() == "DAYS": index = date2index(date, nctime, calendar=nctime.calendar, select='nearest') elif unit[0][0:5].upper() == "MONTH": year0 = int(unit[2][0:4]) month0 = int(unit[2][6:7]) value = (date.year - year0) * 12 + (date.month - month0) if value > max(nctime[:]): value = max(nctime[:]) - 11 + (date.month - month0) msg = " - " + date.strftime('%Y-%m') + " is later then the last dataset in " + name + " -" msg += " instead last year/month dataset is used" if Flags['loud']: iiii = 1 # print(CWATMWarning(msg)) index = np.where(nctime[:] == value)[0][0] elif unit[0][0:4].upper() == "YEAR": year0 = int(unit[2][0:4]) value = date.year - year0 if value > max(nctime[:]): value = max(nctime[:]) msg = " - " + date.strftime('%Y') + " is later then the last dataset in " + name + " -" msg += " instead last year dataset is used" if Flags['loud']: iiii = 1 # print(CWATMWarning(msg)) if value < min(nctime[:]): value = min(nctime[:]) msg = " - " + date.strftime('%Y') + " is earlier then the first dataset in " + name + " -" msg += " instead first year dataset is used" if Flags['loud']: iiii = 1 # print(CWATMWarning(msg)) index = np.where(nctime[:] == value)[0][0] else: index = date2index(date, nctime, calendar=nctime.calendar, select='nearest') return index
[docs]def timestep_dynamic(self): """ Update temporal state variables during dynamic model execution. Core temporal processing function called at each timestep to advance the model's temporal state. Updates current date, calculates temporal markers (month/year boundaries), and maintains counters for output scheduling and temporal aggregation. Essential for coordinating all time-dependent model processes. Parameters ---------- self : object Model instance (parameter not used but maintains method signature). Returns ------- None Updates global dateVar dictionary with current temporal state. Notes ----- The function updates dateVar with: - currDate: Current simulation date (datetime object) - currDatestr: Current date as formatted string - doy: Day of year (1-366) - 10day, 30day: Dekadal and monthly period indices - laststep: Boolean flag for final timestep - newStart, newMonth, newYear: Boolean flags for temporal boundaries - currMonth, currYear: Cumulative month/year counters for output - daysInMonth, daysInYear: Current period lengths for averaging Temporal boundary detection: - Identifies first timestep, month start, year start - Enables conditional execution of periodic processes - Supports flexible output scheduling and aggregation Called once per timestep during the main simulation loop, providing the temporal framework for all model components. """ # print "leap:", globals.leap_flag[0] # dateVar['currDate'] = dateVar['dateBegin'] + datetime.timedelta(days=dateVar['curr']) d1 = datenum(dateVar['dateBegin']) dateVar['currDate'] = numdate(d1, dateVar['curr'] * dateVar['unitConv']) datevarInt = d1 + dateVar['curr'] # dateVar['currDatestr'] = dateVar['currDate'].strftime("%d/%m/%Y") dateVar['currDatestr'] = date2str(dateVar['currDate']) # dateVar['doy'] = int(dateVar['currDate'].strftime('%j')) # replacing this because date less than 1900 is not used firstdoy = datetime.datetime(dateVar['currDate'].year, 1, 1) # dateVar['doy'] = (dateVar['currDate'] - firstdoy).days + 1 firstdoyInt = datenum(firstdoy) dateVar['doy'] = int(datevarInt - firstdoyInt + 1) dateVar['10day'] = int((dateVar['doy'] - 1) / 10) dateVar['30day'] = int((dateVar['doy'] - 1) / 30) dateVar['laststep'] = False if (dateVar['intStart'] + dateVar['curr']) == dateVar['intEnd']: dateVar['laststep'] = True dateVar['currStart'] = dateVar['curr'] + 1 dateVar['curr'] += 1 # count currwrite only after spin time if dateVar['curr'] >= dateVar['intSpin']: dateVar['currwrite'] += 1 dateVar['currMonth'] = dateVar['checked'][:dateVar['currwrite']].count(1) + dateVar['checked'][:dateVar['currwrite']].count(2) dateVar['currYear'] = dateVar['checked'][:dateVar['currwrite']].count(2) # first timestep dateVar['newStart'] = dateVar['curr'] == 1 dateVar['newMonth'] = dateVar['currDate'].day == 1 dateVar['newYear'] = (dateVar['currDate'].day == 1) and (dateVar['currDate'].month == 1) dateVar['new10day'] = ((dateVar['doy'] - 1) / 10.0) == dateVar['10day'] dateVar['new30day'] = ((dateVar['doy'] - 1) / 30.0) == dateVar['30day'] d1month = datenum(datetime.datetime(year=dateVar['currDate'].year, month=dateVar['currDate'].month, day=1)) if dateVar['currDate'].month == 12: month = 1 year = dateVar['currDate'].year + 1 else: month = dateVar['currDate'].month + 1 year = dateVar['currDate'].year d2month = datenum(datetime.datetime(year=year, month=month, day=1)) d1year = datenum(datetime.datetime(year=dateVar['currDate'].year, month=1, day=1)) d2year = datenum(datetime.datetime(year=dateVar['currDate'].year + 1, month=1, day=1)) dateVar['daysInMonth'] = d2month - d1month dateVar['daysInYear'] = d2year - d1year return