'Compute the minimum/maximum selectable date/time, as well as
the default time (=the minute-step closest to now)
@param dtformat: the user datetime format
@return: a dict {minDateTime, maxDateTime, defaultValue, yearRange}
with the min/max options as ISO-formatted strings, and the
defaultValue in user-format (all in local time), to be
passed as-is to s3.calendarwidget'

def extremes(self, dtformat=None):

    extremes = {}
    now = current.request.utcnow
    offset = S3DateTime.get_offset_value(current.session.s3.utc_offset)
    (pyears, fyears) = (80, 80)
    earliest = None
    fallback = False
    if self.minimum:
        earliest = self.minimum
        if (type(earliest) is datetime.date):
            earliest = datetime.datetime.combine(earliest, datetime.time(8, 0, 0))
    elif (self.past is not None):
        earliest = (now - datetime.timedelta(hours=self.past))
    elif (self.past_months is not None):
        earliest = (now - relativedelta(months=self.past_months))
    else:
        fallback = True
        earliest = (now - datetime.timedelta(hours=876000))
    if (earliest is not None):
        if (not fallback):
            pyears = abs((earliest.year - now.year))
        earliest = earliest.replace(microsecond=0)
        if offset:
            earliest += datetime.timedelta(seconds=offset)
        extremes['minDateTime'] = earliest.isoformat()
    latest = None
    fallback = False
    if self.maximum:
        latest = self.maximum
        if (type(latest) is datetime.date):
            latest = datetime.datetime.combine(latest, datetime.time(8, 0, 0))
    elif (self.future is not None):
        latest = (now + datetime.timedelta(hours=self.future))
    elif (self.future_months is not None):
        latest = (now + relativedelta(months=self.future_months))
    else:
        fallback = True
        latest = (now + datetime.timedelta(hours=876000))
    if (latest is not None):
        if (not fallback):
            fyears = abs((latest.year - now.year))
        latest = latest.replace(microsecond=0)
        if offset:
            latest += datetime.timedelta(seconds=offset)
        extremes['maxDateTime'] = latest.isoformat()
    if (self.timepicker and dtformat):
        if (earliest <= now <= latest):
            start = now
        elif (now < earliest):
            start = earliest
        elif (now > latest):
            start = latest
        step = (self.minute_step * 60)
        seconds = (start - start.min).seconds
        rounding = (((seconds + (step / 2)) // step) * step)
        rounded = (start + datetime.timedelta(0, (rounding - seconds), (- start.microsecond)))
        if (rounded < earliest):
            rounded = earliest
        elif (rounded > latest):
            rounded = latest
        if offset:
            rounded += datetime.timedelta(seconds=offset)
        default = rounded.strftime(dtformat)
        extremes['defaultValue'] = default
    min_year = self.min_year
    if (not min_year):
        min_year = ('-%s' % pyears)
    max_year = self.max_year
    if (not max_year):
        max_year = ('+%s' % fyears)
    extremes['yearRange'] = ('%s:%s' % (min_year, max_year))
    return extremes
