def s3_datetime(name="date", **attr): """ Return a standard datetime field @param name: the field name @keyword default: the field default, use "now" for current datetime @keyword represent: the field representation method, use "date" for S3DateTime.date_represent (default is S3DateTime.datetime_represent) @keyword widget: the form widget, can use "date" to configure an S3DateWidget (default is S3DateTimeWidget) @keyword past: limit selection to x hours before now @keyword future: limit selection to x hours after now @keyword min: earliest selectable datetime.datetime (overrides past) @keyword max: latest selectable datetime.datetime (overrides future) @ToDo: Different default field name in case we need to start supporting Oracle, where 'date' is a reserved word """ now = current.request.utcnow limits = {} for keyword in ("past", "future", "min", "max"): if keyword in attr: limits[keyword] = attr[keyword] del attr[keyword] # Default and label if "default" in attr and attr["default"] == "now": attr["default"] = now if "label" not in attr: attr["label"] = current.T("Date") # Representation option if "represent" not in attr: attr["represent"] = lambda dt: S3DateTime.datetime_represent(dt, utc=True) elif attr["represent"] == "date": attr["represent"] = lambda dt: S3DateTime.date_represent(dt, utc=True) # Helper functions to convert min/max dates into past/future hours def past_hours(earliest): diff = now - earliest return divmod(diff.days * 86400 + diff.seconds, 3600)[0] def future_hours(latest): diff = latest - now return divmod(diff.days * 86400 + diff.seconds, 3600)[0] requires = None widget = attr.get("widget") if widget == "date": # Helper function to convert past/future hours into # earliest/latest datetime, retaining day of month and # time of day def limit(delta): current_month = now.month years, hours = divmod(-delta, 8760) months = divmod(hours, 744)[0] if months > current_month: years += 1 month = divmod((current_month - months) + 12, 12)[1] year = now.year - years return now.replace(month=month, year=year) # Compute limits earliest = limits.get("min") if not earliest: past = limits.get("past") if past is not None: earliest = limit(-past) else: past = past_hours(earliest) latest = attr.get("max") if not latest: future = attr.get("future") if future is not None: latest = limit(future) else: future = future_hours(latest) # Widget widget_opts = {} if past is not None: widget_opts["past"] = int(round(past / 744.0, 0)) if future is not None: widget_opts["future"] = int(round(future / 744.0, 0)) attr["widget"] = S3DateWidget(**widget_opts) # Validator if "requires" not in attr: dateformat = current.deployment_settings.get_L10n_date_format() if past is None and future is None: requires = IS_UTC_DATE() elif past is None: requires = IS_UTC_DATE(maximum=latest.date()) elif future is None: requires = IS_UTC_DATE(minimum=earliest.date()) else: attr["widget"] = S3DateWidget(past=past, future=future) requires = IS_UTC_DATE(maximum=latest.date(), minimum=earliest.date()) elif widget is None or widget == "datetime": # Widget attr["widget"] = S3DateTimeWidget(**limits) # Validator if "requires" not in attr: earliest = limits.get("min") if not earliest: past = limits.get("past") if past is not None: earliest = now - datetime.timedelta(hours=past) latest = limits.get("max") if not latest: future = limits.get("future") if future is not None: latest = now + datetime.timedelta(hours=future) if earliest and latest: requires = IS_UTC_DATETIME(minimum=earliest, maximum=latest) elif earliest: requires = IS_UTC_DATETIME(minimum=earliest) elif latest: requires = IS_UTC_DATETIME(maximum=latest) else: requires = IS_UTC_DATETIME() if "requires" not in attr and requires is not None: empty = attr.pop("empty", None) if empty is False: attr["requires"] = requires else: attr["requires"] = IS_EMPTY_OR(requires) return S3ReusableField(name, "datetime", **attr)()
def s3_date(name="date", **attr): """ Return a standard Date field Additional options to normal S3ReusableField: default == "now" (in addition to usual meanings) past = x months future = x months start_field = "selector" for start field default_interval = x months from start date default_explicit = Bool for explicit default start_field and default_interval should be given together @ToDo: Different default field name in case we need to start supporting Oracle, where 'date' is a reserved word """ if "past" in attr: past = attr["past"] del attr["past"] else: past = None if "future" in attr: future = attr["future"] del attr["future"] else: future = None T = current.T now = current.request.utcnow.date() if "default" in attr and attr["default"] == "now": attr["default"] = now if "label" not in attr: attr["label"] = T("Date") if "represent" not in attr: attr["represent"] = lambda d: S3DateTime.date_represent(d, utc=True) if "requires" not in attr: if past is None and future is None: requires = IS_UTC_DATE() else: current_month = now.month if past is None: future_month = now.month + future if future_month <= 12: maximum = now.replace(month=future_month) else: current_year = now.year years = int(future_month / 12) future_year = current_year + years future_month = future_month - (years * 12) if future_month: maximum = now.replace(year=future_year, month=future_month) else: maximum = now.replace(year=future_year) requires = IS_UTC_DATE(maximum=maximum) elif future is None: if past < current_month: minimum = now.replace(month=current_month - past) else: current_year = now.year past_years = int(past / 12) past_months = past - (past_years * 12) past_month = current_month - past_months if past_month: minimum = now.replace(year=current_year - past_years, month=past_month) else: minimum = now.replace(year=current_year - past_years) requires = IS_UTC_DATE(minimum=minimum) else: future_month = now.month + future if future_month <= 12: maximum = now.replace(month=future_month) else: current_year = now.year years = int(future_month / 12) future_year = now.year + years future_month = future_month - (years * 12) if future_month: maximum = now.replace(year=future_year, month=future_month) else: maximum = now.replace(year=future_year) if past < current_month: minimum = now.replace(month=current_month - past) else: current_year = now.year past_years = int(past / 12) past_months = past - (past_years * 12) past_month = current_month - past_months if past_month: minimum = now.replace(year=current_year - past_years, month=past_month) else: minimum = now.replace(year=current_year - past_years) requires = IS_UTC_DATE(minimum=minimum, maximum=maximum) if "empty" in attr: if attr["empty"] is False: attr["requires"] = requires else: attr["requires"] = IS_EMPTY_OR(requires) del attr["empty"] else: # Default attr["requires"] = IS_EMPTY_OR(requires) if "widget" not in attr: # Widget Options widget_option = {} if "start_field" in attr: widget_option["start_field"] = attr["start_field"] del attr["start_field"] if "default_interval" in attr: widget_option["default_interval"] = attr["default_interval"] del attr["default_interval"] if "default_explicit" in attr: widget_option["default_explicit"] = attr["default_explicit"] del attr["default_explicit"] if future is not None: widget_option["future"] = future if past is not None: widget_option["past"] = past attr["widget"] = S3DateWidget(**widget_option) f = S3ReusableField(name, "date", **attr) return f()