from eon_delta import eon_delta from eon_class import * from time import struct_time class eon_date( eon ): ''' #+ # NAME: # eon_date.init # PURPOSE: # Create eon_date object # CALLING SEQUENCE: # 1. tt = eon_date('time-string' [,'time_format',julian=True]) # 2. tt = eon_date('time-string' , 'regex'[, julian=True]) # 3. tt = eon_date(year=YYYY,month=MONTH,day=DAY, hour=HOUR,minute=MINUTE,second=SECOND,chipsec=CHIPSEC[,tz=TZ,julian=True]) # 4. tt = eon_date(year=YYYY,doy=DOY, hour=HOUR,minute=MINUTE,second=SECOND,chipsec=CHIPSEC[,tz=TZ,julian=True]) # 5. tt = eon_date(jd=JD) # 6. tt = eon_date(njd=NJD) # 7. tt = eon_date(mjd=MJD) # 8. tt = eon_date(posix=POSIX) # 9. tt = eon_date(jepoch=JEPOCH) # 10 = eon_date(bepoch=BEPOCH) # 11 = eon_date(carrington=CARRINGTON) [NOT IMPLEMENTED YET] # 12 = eon_date(now=TRUE) # 13 = eon_date(tictay=TICDAY,tictac=TICTAC,exponent=EXPONENT) # PROCEDURE: # For 1. and 2. the time zone can be coded into 'time-string' (with TZ in 'format' and 'regex') # The timezone specified as keyword tz is only used if not specified in 'time-string' # For 3. and 4. keyword tz is always used, if present. # julian=TRUE forces use of Julian calendar for input date (1,2,3,4) #- ''' attr = { 'year' : 'atom: year, -MM-DD' , 'doy' : 'atom: day of year, -DOY' , 'month' : 'atom: month, YYYY--DD' , 'cmonth' : 'atom: month, YYYY--DD' , 'imonth' : 'atom: month, YYYY--DD' , 'day' : 'atom: day of month, YYYY-MM-
', 'hour' : 'atom: hour, :mm:ss.ddd' , 'minute' : 'atom: minute, hh::ss.ddd' , 'second' : 'atom: second, hh:mm:.ddd' , 'chipsec' : 'atom: fraction of second in units of 10^-exponent, hh:mm:ss.', 'njd' : 'days since 2000/01/01 12:00 UT' , 'mjd' : 'modified Julian day' , 'jd' : 'Julian day' , 'bepoch' : 'Besselian epoch' , 'jepoch' : 'Julian epoch' , 'dow' : 'day of week' , 'posix' : 'posix time' , 'posix2' : 'posix time' , } # imonth, cmonth, posix2 tz = eon_delta(tz='UTC') # set/get with eon_util.eon_tz julian = False # set/get with eon_util.eon_julian def __init__ (self, ctime = None, # ctime needs to stay first format = None, regex = None, ticday = None, tictac = None, exponent= g_EXPONENT, year = None, doy = None, month = None, day = None, hour = None, minute = None, second = None, chipsec = None, tz = None, julian = False, jd = None, mjd = None, njd = None, posix = None, jepoch = None, bepoch = None, now = False, ): ''' #+ # NAME: # eon_date.__init__ # PURPOSE: # Creates eon_date object # CALLING SEQUENCE: # tt = eon_date() # INPUTS: # OPTIONAL INPUTS: # exponent defines units in which time of day is specified as # 10^(-exponent). # Default: g_EXPONENT (defined in eon_helper.py) # OUTPUTS: # tt eon_date object # tt.exponent integer # set from optional input 'exponent' # (i.e. defaults to g_EXPONENT) # PROCEDURE: # The __init__ procedure MUST set tt.ticday and tt.tictac: # tt.ticday number of whole days since 2000 January 1, 0 UTC # tt.tictac time of days in units of 10^(-tt.exponent) # tt.exponent defines units of tt.tictac # As a side effect a number of attributes may get set, but this # is not really necessary. #- ''' if g_EON_DEBUG: print self.__class__, 'in eon_date', whoami() self.exponent = exponent tictacs_per_second = 10**exponent tictacs_per_day = tictacs_per_second*g_SECONDS_PER_DAY if ticday != None: # Input is primary attributes 'ticday' and 'tictac' if tictac == None: tictac = 0 self.ticday = ticday self.tictac = tictac if isinteger(tictac) else long(round(tictac)) elif posix != None: # Input is posix time tic = disassemble_number(posix, exponent) ticday,tictac = divmod( tic[0], g_SECONDS_PER_DAY ) self.ticday = -g_JD2000_POSIX +long(ticday) self.tictac = long(round(tictac*tictacs_per_second+tic[1])) elif njd != None: # Input is 'new' Julian day (days since 2000/01/01 12:00:00) ticday,tictac = divmod( best_guess_for_number(njd) ,1) self.ticday = -g_NJD_NJD +long(ticday) self.tictac = tictacs_per_day/2 +long(round(tictac*tictacs_per_day)) elif mjd != None: # Modified Julian days ticday,tictac = divmod( best_guess_for_number(mjd) ,1) self.ticday = -g_NJD_MJD +long(ticday) self.tictac = tictacs_per_day/2 +long(round(tictac*tictacs_per_day)) elif jd != None: # Julian days ticday,tictac = divmod( best_guess_for_number(jd) ,1) self.ticday = -g_NJD_JD +long(ticday) self.tictac = tictacs_per_day/2 +long(round(tictac*tictacs_per_day)) elif jepoch != None: ticday,tictac = divmod(( best_guess_for_number(jepoch)-2000)*g_JULIAN_YEAR,1) self.ticday = 0 +long(ticday) self.tictac = tictacs_per_day/2 +long(round(tictac*tictacs_per_day)) elif bepoch != None: ticday,tictac = divmod(-g_JD2000_B1900+(best_guess_for_number(bepoch)-1900)*g_BESSEL_YEAR,1) self.ticday = 0 +long(ticday) self.tictac = tictacs_per_day/2 +long(round(tictac*tictacs_per_day)) else: # Often a single unnamed argument containing a string with a time is specified. # This argument will end up in 'ctime'. This is run through split_date_format # (if format is set), split_time_regex (if regex is set) or split_date_free # to set the time # Input is (a subset of) a list of individual time elements (year, doy, etc.) # 0:YYYY,1:MM,2:DD,3:hh,4:mm,5:ss,6:dow,7:doy if ctime != None: year,doy,month,day,hour,minute,second,chipsec,tz_override = \ (ctime.tm_year,ctime.tm_yday,None,None,ctime.tm_hour,ctime.tm_min,ctime.tm_sec,None,None) if isinstance(ctime,struct_time) else \ split_date_regex (ctime,regex ,exponent) if regex != None else \ split_date_format(ctime,format,exponent) if format != None else \ split_date_free (ctime, exponent) if tz_override != None: tz = tz_override # At this point a complete set of time elements is specified. # Use these to set the primary attributes self.ticday and self.tictac if isstring(year): # 'year' could be returned as a string from split_date_regex # Currently only string 'posix' is returned if year == 'posix': # doy = seconds, month = chipsec (both integers) second = doy chipsec = month day,second = divmod(second,g_SECONDS_PER_DAY) self.ticday = -g_JD2000_POSIX+day self.tictac = second*tictacs_per_second+chipsec else: raise EonError("%s, option not implemented, '%s'"%(whoami(),year)) else: year,doy,month,cmonth,day,hour,minute,second,chipsec = fill_date_fields(year,doy,month,day,hour,minute,second,chipsec,tz,exponent=exponent,julian=julian,now=now) if not isinteger(chipsec): chipsec = long(round(chipsec)) self.ticday = days_since_2000(year,doy,julian=julian)+julian*g_GREG2JULIAN self.tictac = ((hour*g_MINUTES_PER_HOUR+minute)*g_SECONDS_PER_MINUTE+second)*10**exponent+chipsec self.normalize() return def get (self, format = None , regex = None , attr = None , ): ''' #+ # NAME: # eon_date.get # PURPOSE: # Retrieve attribute from eon_date object # CALLING SEQUENCE: # attr = tt.get( format=format, regex=regex, attr=attr ) # INPUTS # tt eon_date object # OPTIONAL INPUTS: # Specify one of these to define the attribute required # format string defining the format for the attribute # The string can contain a number of identifiers which are # replaced by the corresponding time properties. # YYYY 4-digit year # YY 2-digit year: # YYYY-1900 if YYYY less than 2000 # YYYY-2000 if YYYY if greater/equal 2000 # DOY day of year as 3-char string between 001 and 366 # MM month as 2-char string between 01 and 12 # MON month as 3-char uppercase string: JAN, FEB, .., DEC # Mon month as 3-char string: Jan, Feb, .., Dec # Month full name of month: January, February, .., December # DD day of month as 2-char string between 01 and 31 # hh hour of the day as 2-char string between 01 and 23 # mm minute in the the hour as 2-char string between # 01 and 59 # ss, ss.d#, ss.d, ss.dd, ss.ddd, etc. # integer part of second in the minute as 2-char # string between 01 and 59, with optional the fraction # indicated by .d#, .d, etc. (see below) # posix, posix.d#, posix.d, posix.dd, posix.ddd, etc. # integer part of posix (unix) time with optional the # fraction indicated by .d#, .d, etc. (see below) # # For ss and posix the fraction of the second is specified as: # The fractional second is added with the indicated # number digits: # .d# # digits matches the internal time precision # .d one digit # .dd two digits, etc. # # DOW day of week as 3-char uppercase string: MON, TUE, .. # weekday full day of week: Monday, Tuesday, .. # njd 'new' Julian day: days since 2000 Jan 1, 12:00:00 UTC # mjd modified Julian day # jd Julian day # jepoch Julian epoch # bepoch Besselian epoch # # regex not yet implemented # # attr single attribute of time: # year # doy # month # day # dow # hour # minute # second # chipsec # njd # mjd # jd # jepoch # bepoch # posix # # julian True/False, default: False # False: return attributes for Gregorian date # True : return attributes for Julian date ''' if format != None: if g_EON_DEBUG: print '%s in "%s" with format "%s"'%(self.__class__, whoami(), format) dformat = translate_format( format ) if g_EON_DEBUG: print '%s in "%s" with format "%s"'%(self.__class__, whoami(), dformat) result = dformat if 'njd' in result: result = result.replace('njd' , str(self.njd )) if 'mjd' in result: result = result.replace('mjd' , str(self.mjd )) if 'jd' in result: result = result.replace('jd' , str(self.jd )) if 'jepoch' in result: result = result.replace('jepoch', str(self.jepoch)) if 'bepoch' in result: result = result.replace('bepoch', str(self.bepoch)) if 'posix' in result: posix2 = self.posix2 result = fill_fraction(result, '%d'%posix2[0], posix2[1], self.exponent, spec='posix') if '%S' in result: # ss (seconds) posix2 = self.posix2 result = fill_fraction(result, '%02d'%divmod(posix2[0],60)[1], posix2[1], self.exponent, spec='%S') if 'TZ' in result: result = result.replace('TZ', eon_date.tz.get('hm')) # The remaining quantities are timezone dependent # (part = year, doy, month, day, hour, minute, second, dow) while '%Y' in result: result = result.replace('%Y','%04d'%self.year,1) if 'YY' in result: year = self.year year = (year-1900)*(year < 2000)+(year-2000)*(year >= 2000) result = result.replace('YY','%02d'%year,1) if '%j' in result: # DOY result = result.replace('%j','%03d'%self.doy,1) if '%m' in result: # MM (2-digit month integer) result = result.replace('%m','%02d'%self.month[0],1) if '%B' in result: # Month (full name of month) result = result.replace('%B','%s'%self.month[1],1) if '%b' in result: # Mon (3-char month) result = result.replace('%b','%s'%self.month[1][0:3],1) if 'MON' in result: result = result.replace('MON','%3s'%self.month[1][0:3].upper(),1) if 'mon' in result: result = result.replace('mon','%3s'%self.month[1][0:3].lower(),1) if '%d' in result: # DD result = result.replace('%d','%02d'%self.day,1) if '%H' in result: # hh (hour 00..24) result = result.replace('%H','%02d'%self.hour,1) if '%M' in result: # MM result = result.replace('%M','%02d'%self.minute,1) if '%a' in result: # Dow (3-char day of week) dow = self.dow[1] result = result.replace('%a', dow[0].upper()+dow[1:3].lower()) if '%A' in result: # Weekday (full weekday) dow = self.dow[1] result = result.replace('%A', dow[0].upper()+dow[1:].lower()) if 'DOW' in result: result = result.replace('DOW', self.dow[1][0:3].upper()) if 'dow' in result: result = result.replace('DOW', self.dow[1][0:3].lower()) if 'WEEKDAY' in result: result = result.replace('WEEKDAY', self.dow[1].upper()) if 'weekday' in result: result = result.replace('weekday', self.dow[1].lower()) if result == format: raise EonError("%s, unrecognized format, '%s'"%(whoami(),format)) elif regex != None: if g_EON_DEBUG: print '%s in "%s" with regex "%s"'%(self.__class__, whoami(), regex) pass else: if g_EON_DEBUG: print '%s in "%s" with attr "%s"'%(self.__class__, whoami(), attr) if isinstance(attr,list): result = [ self.__getattr__( a ) for a in attr ] else: result = self.__getattr__( attr ) return result def __getattr__(self, name): if g_EON_DEBUG: print '%s in %s for "%s"'%(self.__class__, whoami(), name) # This intercepts attributes that already have been calculated. # This includes the principal attributes self.ticday, self.tictac # and self.exponent, and timezone-independent quantities that # have been referenced already. if name in self.__dict__: result = self.__dict__[name] # First list all quantities that are independent of timezone. # These are stored as attributes of self whenever they are # referenced the first time. More subsequence references they # are pulled out of self.__dict__ (see above). elif name == 'njd': tictacs_per_day = g_SECONDS_PER_DAY*10**self.exponent chipsec = self.tictac-tictacs_per_day/2 self.njd = g_NJD_NJD+self.ticday+(0 if chipsec == 0 else float(chipsec)/tictacs_per_day) result = self.__dict__[name] elif name == 'mjd': tictacs_per_day = g_SECONDS_PER_DAY*10**self.exponent chipsec = self.tictac-tictacs_per_day/2 self.mjd = g_NJD_MJD+self.ticday+(0 if chipsec == 0 else float(chipsec)/tictacs_per_day) result = self.__dict__[name] elif name == 'jd': tictacs_per_day = g_SECONDS_PER_DAY*10**self.exponent chipsec = self.tictac-tictacs_per_day/2 self.jd = g_NJD_JD+self.ticday+float(self.tictac-tictacs_per_day/2)/tictacs_per_day result = self.__dict__[name] elif name == 'jepoch': tictacs_per_day = g_SECONDS_PER_DAY*10**self.exponent chipsec = self.tictac-tictacs_per_day/2 self.jepoch = 2000+(self.ticday+(0 if chipsec == 0 else float(chipsec)/tictacs_per_day))/g_JULIAN_YEAR result = self.__dict__[name] elif name == 'bepoch': tictacs_per_day = g_SECONDS_PER_DAY*10**self.exponent chipsec = self.tictac-tictacs_per_day/2 self.bepoch = 1900+(g_JD2000_B1900+(self.ticday+(0 if chipsec == 0 else float(chipsec)/tictacs_per_day)))/g_BESSEL_YEAR result = self.__dict__[name] elif name == 'posix2': # Integer number of seconds in posix time tictacs_per_second = 10**self.exponent iposix, chipsec = divmod(self.tictac, tictacs_per_second) # Integer seconds and remaining tictacs iposix += (g_JD2000_POSIX+self.ticday)*g_SECONDS_PER_DAY self.posix2 = (iposix,chipsec) result = self.__dict__[name] elif name == 'posix': # NEVER USED? Posix time as float (will loose some precision) tictacs_per_second = 10**self.exponent iposix, chipsec = divmod(self.tictac, tictacs_per_second) # Integer seconds and remaining tictacs rposix = 0 if chipsec == 0 else float(chipsec)/tictacs_per_second # Remaining fraction of a second iposix += (g_JD2000_POSIX+self.ticday)*g_SECONDS_PER_DAY # Integer number of seconds in posix time self.posix = iposix+rposix result = self.__dict__[name] elif name in 'second': tictacs_per_second = 10**self.exponent iposix, chipsec = divmod(self.tictac, tictacs_per_second) # Integer seconds and remaining tictacs self.second = divmod(iposix,60)[1] result = self.__dict__[name] elif name in 'chipsec': tictacs_per_second = 10**self.exponent iposix, chipsec = divmod(self.tictac, tictacs_per_second) # Integer seconds and remaining tictacs self.chipsec = chipsec result = self.__dict__[name] # The remaining attributes are timezone-dependent quantities that # need to be calculated from scratch every time they are referenced. # The timezone is stored in self.__dict__['tz'] in __init__. # The first call to adjust_for_timezone adjust self.ticday and # self.tictac to account for the timezone. The second call resets # self.ticday and self.tictac back to their original (UTC) values, # and removes self.__dict__['tz'] from the dictionary. elif name == 'year': year, doy = year_doy((self+eon_date.tz).ticday-g_GREG2JULIAN*eon_date.julian,julian=eon_date.julian) result = year elif name == 'doy': year, doy = year_doy((self+eon_date.tz).ticday-g_GREG2JULIAN*eon_date.julian,julian=eon_date.julian) result = doy elif name in ['month','imonth','cmonth','day']: year, doy = year_doy((self+eon_date.tz).ticday-g_GREG2JULIAN*eon_date.julian,julian=eon_date.julian) year, month, cmonth, day = doy2month_day( year, doy, julian=eon_date.julian ) result = (month,cmonth) if name == 'month' else month if name == 'imonth' else cmonth if name == 'cmonth' else day elif name == 'dow': dow = weekday( (self+eon_date.tz).ticday ) result = dow elif name in ['hour','minute']: tt = self+eon_date.tz day, hour, minute, second, chipsec = fix_dhms(0, 0, 0, 0, tt.tictac, exponent=tt.exponent) result = hour if name == 'hour' else minute else: raise EonError( "%s, unknown eon_date attr '%s'"%(whoami(),name)) return result def __add__( self, tt ): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) if not isinstance(tt,eon_delta): # Can only add delta to date raise TypeError, "unsupported operand type for +: '%s' and '%s'"%(self.type(),tt.__class__) return super(eon_date,self).__add__( tt ) def __neg__(self): # Date cannot be negated if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) raise TypeError, "unsupported operand type for -: '%s'"%self.type() def __mul__(self, multiplier): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) raise TypeError, "unsupported operand type for *: '%s'"%self.type() def __div__(self, divider): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) raise TypeError, "unsupported operand type for /: '%s'"%self.type() def __divmod__(self, divider): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) raise TypeError, "unsupported operand type for divmod: '%s'"%self.type() def __abs__(self): # Date cannot be negated if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) raise TypeError, "unsupported operand type for abs: '%s'"%self.type() def __deepcopy__(self, memo=None): return super(eon_date,self).new( eon_date ) def new( self, ticday=None, tictac=None, exponent=None, tt=None ): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) return super(eon_date,self).new( eon_delta if isinstance(tt,eon_date) else eon_date, ticday, tictac, exponent ) def bot(self,dt,t0=None): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) if t0 == None: t0 = eon_date(exponent=self.exponent) return t0+(self-t0).bot(dt) def eot(self,dt,t0=None): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) if t0 == None: t0 = eon_date(exponent=self.exponent) return t0+(self-t0).eot(dt) def round(self,dt,t0=None): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) if t0 == None: t0 = eon_date(exponent=self.exponent) return t0+(self-t0).round(dt) def edge_of_period(self,case_type,end_of_period=False): if g_EON_DEBUG: print "method '%s:%s'"%(__name__,whoami()) cases = ['year','quarter','month','week','day','monday','tuesday','wednesday','thursday','friday','saturday','sunday','hour','minute'] if case_type in cases: case_type += "+0" import re for case in cases: m = re.search('^%s([-,+]\d+)'%case,case_type) if m: break else: raise EonError( "%s, t.bop(case), invalid case '%s'"%(whoami(),case_type)) uu = self n = long(m.group(1)) if end_of_period and uu != uu.bop(case): n += 1 if case == 'year': tt = eon_date(year=uu.get(attr='year')+n,month=1, day=1 , exponent=self.exponent) elif case == 'quarter': tt = eon_date(year=uu.get(attr='year'),month=(uu.get(attr='month')[0]-1)/3*3+1+n*3, day=1 , exponent=self.exponent) elif case == 'month': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0]+n, day=1 , exponent=self.exponent) elif case == 'week': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]-1+n*7) , exponent=self.exponent) elif case == 'day': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')+n , exponent=self.exponent) elif case == 'monday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+6)%7+n*7 , exponent=self.exponent) elif case == 'tuesday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+5)%7+n*7 , exponent=self.exponent) elif case == 'wednesday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+4)%7+n*7 , exponent=self.exponent) elif case == 'thursday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+3)%7+n*7 , exponent=self.exponent) elif case == 'friday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+2)%7+n*7 , exponent=self.exponent) elif case == 'saturday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+1)%7+n*7 , exponent=self.exponent) elif case == 'sunday': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day')-(uu.get(attr='dow')[0]+0)%7+n*7 , exponent=self.exponent) elif case == 'hour': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day'), hour=uu.get(attr='hour')+n , exponent=self.exponent) elif case == 'minute': tt = eon_date(year=uu.get(attr='year'),month= uu.get(attr='month')[0], day=uu.get(attr='day'), hour=uu.get(attr='hour'), minute=uu.get(attr='minute')+n, exponent=self.exponent) return tt def bop(self,case_type): return self.edge_of_period(case_type,False) def eop(self,case_type): return self.edge_of_period(case_type,True) def deadline(self,case_type): tt = self.new() if case_type[0] in ['+','-']: # Process in reverse order?? for fragment in case_type.split(','): if fragment[0] == '+': tt = tt.eop(fragment[1:]) elif fragment[0] == '-': tt = tt.bop(fragment[1:]) else: raise EonError( "%s, t.deadline(case_type), missing sign in '%s'"%(whoami(),case_type)) else: t1 = tt.deadline('+'+case_type) t2 = tt.deadline('-'+case_type) tt = t1 if t2 < tt else t2 return tt def range(self,tn,dt): if isinteger( tn ): n = tn if n < 0: raise EonError( "%s, t.range(dt,n), n must be >= 0, n='%s'"%(whoami(),n)) elif isinstance(tn,eon_date): n,f = divmod(tn-self,dt) if f != f.zero(): n += 1 return [ self+i*dt for i in range(n) ]