import datetime, calendar """ utilities for working with dates using datetime module (Python 2.3 or later) Jeff Whitaker """ hrsgregstart = 13865688 # hrs from 00010101 to 15821015 in Julian calendar. # times in many datasets use mixed Gregorian/Julian calendar, datetime # module uses a proleptic Gregorian calendar. So, I use datetime to compute # hours since start of Greg. calendar (15821015) and add this constant to # get hours since 1-Jan-0001 in the mixed Gregorian/Julian calendar. gregstart = datetime.datetime(1582,10,15) # datetime.datetime instance day1 = datetime.datetime(1,1,1) # datetime.datetime instance def dateto_hrs_since_day1CE(curdate,mixedcal=True): """given datetime.datetime instance, compute hours since 1-Jan-0001""" if mixedcal: if curdate < gregstart: msg = 'date must be after start of gregorian calendar (15821015)!' raise ValueError, msg difftime = curdate-gregstart hrsdiff = 24*difftime.days + difftime.seconds/3600 return hrsdiff+hrsgregstart else: difftime = curdate-day1 return 24.*(difftime.days+1)+difftime.seconds/3600. def hrs_since_day1CE_todate(hrs,mixedcal=True): """return datetime.datetime instance given hours since 1-Jan-0001""" if hrs < 0.0: msg = "hrs must be positive!" raise ValueError, msg delta = datetime.timedelta(hours=1) if mixedcal: hrs_sincegreg = hrs - hrsgregstart curdate = gregstart + hrs_sincegreg*delta else: curdate = hrs*delta return curdate def dateshift(analdate,fcsthr): """ verifdate = incdate(analdate, fcsthr) compute verification date given analysis date string (yyyymmddhh) and fcst hour. """ yyyy,mm,dd,hh = splitdate(analdate) analdate = datetime.datetime(yyyy,mm,dd,hh) verifdate = analdate + fcsthr*datetime.timedelta(hours=1) verifdate = makedate(verifdate.year,verifdate.month,verifdate.day,verifdate.hour) return verifdate def splitdate(yyyymmddhh): """ yyyy,mm,dd,hh = splitdate(yyyymmddhh) give an date string (yyyymmddhh) return integers yyyy,mm,dd,hh. """ yyyy = int(yyyymmddhh[0:4]) mm = int(yyyymmddhh[4:6]) dd = int(yyyymmddhh[6:8]) hh = int(yyyymmddhh[8:10]) return yyyy,mm,dd,hh def makedate(yyyy,mm,dd,hh): """ yyyymmddhh = makedate(yyyy,mm,dd,hh) return a date string of the form yyyymmddhh given integers yyyy,mm,dd,hh. """ return '%0.4i'%(yyyy)+'%0.2i'%(mm)+'%0.2i'%(dd)+'%0.2i'%(hh) def hrstodate(hrs,mixedcal=True): """ yyyymmddhh = hrstodate(hrs) return a date string of the form yyyymmddhh given hrs since day 1 CE. """ date = hrs_since_day1CE_todate(hrs,mixedcal=mixedcal) return makedate(date.year,date.month,date.day,date.hour) def datetohrs(yyyymmddhh,mixedcal=True): """ hrs = hrstodate(yyyymmddhh) return hrs since day 1 CE given a date string of the form yyyymmddhh. """ yyyy,mm,dd,hh = splitdate(yyyymmddhh) return dateto_hrs_since_day1CE(datetime.datetime(yyyy,mm,dd,hh),mixedcal=mixedcal) def daterange(date1,date2,hrinc): """ date_list = daterange(date1,date2,hrinc) return of list of date strings of the form yyyymmddhh given a starting date, ending date and an increment in hours. """ date = date1 delta = datetime.timedelta(hours=1) yyyy,mm,dd,hh = splitdate(date) d = datetime.datetime(yyyy,mm,dd,hh) n = 0 dates = [date] while date < date2: d = d + hrinc*delta date = makedate(d.year,d.month,d.day,d.hour) dates.append(date) n = n + 1 return dates def dayofyear(yyyy,mm,dd): """ return integer day of year given yyyy,mm,dd """ d = datetime.datetime(yyyy,mm,dd) d0 = datetime.datetime(yyyy,1,1) return (d-d0).days def getyrmon(day_of_year,yyyy=2001): d1 = datetime.datetime(yyyy,1,1) if calendar.isleap(d1.year) and day_of_year > 366: raise ValueError, 'not that many days in the year' if not calendar.isleap(d1.year) and day_of_year > 365: raise ValueError, 'not that many days in the year' d2 = d1 + (day_of_year-1)*datetime.timedelta(days=1) return d2.month,d2.day def daysinmonth(yyyy,mm): """ return number of days in month given yyyy,mm """ return calendar.monthrange(yyyy,mm)[1] if __name__ == "__main__": print dayofyear(2000,2,29) print daysinmonth(2000,2) print datetohrs('0001010100',mixedcal=False) print datetohrs('2001010100',mixedcal=False) print datetohrs('2001010100',mixedcal=True)