#!/d/Bin/Python/python.exe
#
#
# $Date: 2005/05/19 05:33:10 $
#
"""
	Simple wrapper around python's time functionalities to generate coordinates
	along an axes for date values.
"""	
import time, datetime

class dateError(Exception) :
	""" Exception raised if the date is not between epoch and the current time"""
	def __init__(self) :
		pass
	def __str__(self) :
		return "Date must be between epoch and maxday"

class coordDates :
	"""
	Class to convert dates on a set of coordinates. The class is initialized with the
	maximum value of the coordinate (corresponding to 'today')

	The initialization also generates a self.years dictionary. This dictionary stores
	the coordinates for the first of January of each year between epoch and now. The dictionary is
	keyed with the year (as a string).	
	"""
	def __init__(self,rect,ep="1994-01-01",format="%Y-%m-%d") :
		"""Initialization of coord date. Default epoch is 01 January, 1994, default format is %Y-%m-%d
		
		@param rect: size of the whole range (ie, line)
		@type rect: number (float or integer)
		@param ep: start of 'epoch', ie, the starting date
		@type ep: either a datetime.date or a string for the date
		@param format: if ep is a string, this defines the format of the string using the convention for the datetime.date objects
		"""
		self._rectSize = rect
					
		self._epoch = self._getDateObject(ep,format)
		self._epochYear = self._epoch.year
		
		self._today = datetime.date.today()
		self._generateYears()
		
	def setMaxDay(self,date,format="%Y-%m-%d") :
		"""By default, the maximum day is set to 'today'. This can be overriden by this method.
		Beware: this method B{must} be invoked right after intialization.
		
		@param date: start of 'epoch', ie, the starting date
		@type date: either a datetime.date or a string for the date
		@param format: if date is a string, this defines the format of the string using the convention for the datetime.date objects
		"""
		self._today = self._getDateObject(date,format)
		self._generateYears(True)
		
	def days(self,date,format="%Y-%m-%d") :
		"""Return the number of days relative to local epoch. The date must be between epoch and now.
		
		@param date: start of 'epoch', ie, the starting date
		@type date: either a datetime.date or a string for the date
		@param format: if date is a string, this defines the format of the string using the convention for the datetime.date objects
		@raises dateError: if the date is not between epoch and the current time.
	    """
		dateD = self._getDateObject(date,format)
		if self._epoch <= dateD and dateD <= self._today :
			diff = dateD - self._epoch
			return diff.days
		else :
			raise dateError
			
	def coordinate(self,date,format="%Y-%m-%d") :
		"""Return the coordinates relative to local epoch, local epoch being at the point zero. 
		The date must be between epoch and now. Default format of the date is %Y-%m-%d
		
		@param date: start of 'epoch', ie, the starting date
		@type date: either a datetime.date or a string for the date
		@param format: if date is a string, this defines the format of the string using the convention for the datetime.date objects
		@returns: coordinate value
		@rtype: float
		@raises dateError: if the date is not between epoch and the current time (the exception is actually raised by 
		L{days<coordDates.days>} invoked by this method).
		"""
		d = self.days(date,format)
		return (d * self._rectSize)/self._allDays

	def _generateYears(self,adjust=False) :
		"""
		Fill in the year dictionary.
		"""
		diff = self._today - self._epoch
		self._allDays = diff.days
		y0 = self._epoch.year
		if not(self._epoch.month == 1 and self._epoch.day == 1) :
			y0 += 1
		yL = self._today.year
		self.years = {}
		for y in range(y0,yL+1) :
			c = self.coordinate(("%d-01-01" % y))
			self.years[y] = c
					
	def _getDateObject(self,date,format) :
		"""
		Get the date, if necessary by converting a string.
		@param date: start of 'epoch', ie, the starting date
		@type date: either a datetime.date or a string for the date
		@param format: if date is a string, this defines the format of the string using the convention for the datetime.date objects
		@return: date
		@rtype: datetime.date
		"""
		if type(date) == type("a") :
			tl = time.strptime(date,format)
			return datetime.date(tl.tm_year,tl.tm_mon,tl.tm_mday)
		else :
			return date
			

# The debugging idiom...
if __name__ == '__main__' :
	print "----------"
	coord = coordDates(640)
	years = coord.years.keys()
	years.sort()
	for y in years :
		print "%s: %s" % (y,coord.years[y])

	
