def _DateFactory(date=None, dtfunc=DateTime.localtime, convfunc=DateTime.utc2local):

    # shortcut for most common case, to minimize
    # time spent in this function...
    if date is None: return dtfunc()

    # else if an int or float, it's seconds since epoch.
    if type(date) in (types.IntType, types.FloatType):
	return dtfunc(date)
    
    # else if a DateTime, it's in the "wrong" (opposite)
    # timezone (local if we want UTC, and vice versa).
    # Use convfunc on it.
    elif isDateTime(date): return convfunc(date)

    # else we've got an ISO string on our hands. If a time
    # value is in string, it's assumed to be in GMT.
    # One may pass an offset a la +-0000 for conversion
    # of time value into GMT.

    elif type(date) == types.StringType:
	return _parseDate(date)

    # else date is garbage; ignore and return current date
    else: return dtfunc()
def DateTruncate(date, unit='dd'):
    """
    Given a DateTime object, and a unit to truncate the
    date object to, a string that is one of:

    'mm', 'dd', 'yy', 'yyyy', 'hh', 'mi'
    
    it returns a new date object with the original
    date object's values "truncated" to the unit
    specified:
    
    trunc(Date('1999-03-24', 'mm') == Date('1999-03-01')
    
    unit is case-insensitive.
    """
    if not isDateTime(date):
	raise TypeError, "date %s is not a DateTime object" % date

    unit = string.lower(unit)

    # we handle 'ss' in this code...
    if unit != 'ss' and not _trunc_helpers.has_key(unit):
	raise ValueError, "Cannot truncate date to unit %s" % repr(unit)

    if unit == 'ss':
	return DateTime.DateTime(
		   date.year, date.month, date.day,
		   date.hour, date.minute, int(date.second)
	       )
    else:
        return date + _trunc_helpers[unit]
def _DateFactory(date=None, dtfunc=DateTime.localtime, convfunc=DateTime.utc2local):

    # shortcut for most common case, to minimize
    # time spent in this function...
    if date is None: return dtfunc()

    # else if an int or float, it's seconds since epoch.
    if type(date) in (types.IntType, types.FloatType):
	return dtfunc(date)
    
    # else if a DateTime, it's in the "wrong" (opposite)
    # timezone (local if we want UTC, and vice versa).
    # Use convfunc on it.
    elif isDateTime(date): return convfunc(date)

    # else we've got an ISO string on our hands. If a time
    # value is in string, it's assumed to be in GMT.
    # One may pass an offset a la +-0000 for conversion
    # of time value into GMT.

    elif type(date) == types.StringType:
	return _parseDate(date)

    # else date is garbage; ignore and return current date
    else: return dtfunc()
def DateTruncate(date, unit='dd'):
    """
    Given a DateTime object, and a unit to truncate the
    date object to, a string that is one of:

    'mm', 'dd', 'yy', 'yyyy', 'hh', 'mi'
    
    it returns a new date object with the original
    date object's values "truncated" to the unit
    specified:
    
    trunc(Date('1999-03-24', 'mm') == Date('1999-03-01')
    
    unit is case-insensitive.
    """
    if not isDateTime(date):
	raise TypeError, "date %s is not a DateTime object" % date

    unit = string.lower(unit)

    # we handle 'ss' in this code...
    if unit != 'ss' and not _trunc_helpers.has_key(unit):
	raise ValueError, "Cannot truncate date to unit %s" % repr(unit)

    if unit == 'ss':
	return DateTime.DateTime(
		   date.year, date.month, date.day,
		   date.hour, date.minute, int(date.second)
	       )
    else:
        return date + _trunc_helpers[unit]
def DateRound(date, unit='dd'):
    """
    Just like DateTruncate, but instead 
    "rounds" the date to the date unit specified.
    
    For compatibility with Oracle, month-rounding is handled
    in this abritrary manner:
    
    If the day is greater than the 15th, date rounds up.
    otherwise, date rounds down.
    
    Year-rounding is also Oracle-like: if month is less
    than 7, year rounds down.
    
    As with DateTruncate, the value of
    unit is case-sensitive.
    """
    if not isDateTime(date):
	raise TypeError, "Date %s is not a DateTime object" % date

    unit = string.lower(unit)

    # we handle 'ss' in this code...
    if not _trunc_helpers.has_key(unit):
	raise ValueError, "Cannot round date to unit %s" % repr(unit)

    if unit == 'ss':
	newsecs = int(date.second + .5)
	return DateTime.DateTime(
		   date.year, date.month, date.day,
		   date.hour, date.minute, newsecs
	       )

    else:
	bounder = _round_bounds[unit]
	if getattr(date, bounder[0]) < bounder[1]:
	    return date + _trunc_helpers[unit]
	else:
	    return date + _round_helpers[unit]
def DateRound(date, unit='dd'):
    """
    Just like DateTruncate, but instead 
    "rounds" the date to the date unit specified.
    
    For compatibility with Oracle, month-rounding is handled
    in this abritrary manner:
    
    If the day is greater than the 15th, date rounds up.
    otherwise, date rounds down.
    
    Year-rounding is also Oracle-like: if month is less
    than 7, year rounds down.
    
    As with DateTruncate, the value of
    unit is case-sensitive.
    """
    if not isDateTime(date):
	raise TypeError, "Date %s is not a DateTime object" % date

    unit = string.lower(unit)

    # we handle 'ss' in this code...
    if not _trunc_helpers.has_key(unit):
	raise ValueError, "Cannot round date to unit %s" % repr(unit)

    if unit == 'ss':
	newsecs = int(date.second + .5)
	return DateTime.DateTime(
		   date.year, date.month, date.day,
		   date.hour, date.minute, newsecs
	       )

    else:
	bounder = _round_bounds[unit]
	if getattr(date, bounder[0]) < bounder[1]:
	    return date + _trunc_helpers[unit]
	else:
	    return date + _round_helpers[unit]