## $Id$ ## ## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """ A datewidget with customizable date format. """ from datetime import datetime from zope.formlib.interfaces import ConversionError, IDisplayWidget from zope.formlib.textwidgets import ( DatetimeWidget, DatetimeDisplayWidget, escape) from zope.formlib.widget import renderElement, CustomWidgetFactory from zope.interface import implements #: A dictionary of supported datetime formats. #: #: The following formats are supported: #: #: ``iso`` #: ISO format ``YYYY-MM-DD HH:MM:SS`` #: ``le`` #: little endian with slashes: ``DD/MM/YYYY HH:MM:SS`` #: ``de`` #: german date format: ``DD.MM.YYYY HH:MM:SS`` #: ``us`` #: middle endian format common in the U.S.: ``MM/DD/YYYY HH:MM:SS`` #: DATETIME_FORMATS = { 'iso': ('datetimepicker', '%Y-%m-%d %H:%M:%S'), 'le': ('datetimepicker-le', '%d/%m/%Y %H:%M:%S'), 'de': ('datetimepicker-de', '%d.%m.%Y %H:%M:%S'), 'us': ('datetimepicker-us', '%m/%d/%Y %H:%M:%S'), } #: a dict containing as key and suitable CSS tags as values. FORMATS_BY_VALUE = dict( [(val[1], val[0]) for key, val in DATETIME_FORMATS.items()]) class FormattedDatetimeWidget(DatetimeWidget): """A datetime widget that supports different (and _explicit_) date formats. If the widget is bound to a schema field with respective attributes, it reads its `date_format` attribute (see waeup.kofa.schema.FormattedDatetime for an example) and sets a CSS tag according to the values. The widget also accepts ISO format as a fallback, even if a different format was set. This should help with imports. This is an input widget. """ date_format = '%Y-%m-%d %H:%M:%S' show_time = True def __init__(self, context, request, *args, **kw): # try to grab date_format and show_time from bound schema field. date_format = getattr(context, 'date_format', self.date_format) if date_format is not None: self.date_format = date_format self.show_time = getattr(context, 'show_time', self.show_time) # add css class determined by date_format css_cls = FORMATS_BY_VALUE.get(self.date_format, '') self.cssClass = ' '.join([self.cssClass, css_cls]).strip() return super(FormattedDatetimeWidget, self).__init__( context, request, *args, **kw) def _toFieldValue(self, input): # In import files we can use the hash symbol at the end of a # datetime string to avoid annoying automatic datetime transformation # by Excel or Calc input = input.strip('#') if input == self._missing: return self.context.missing_value else: try: value = datetime.strptime(input, self.date_format) except (ValueError, IndexError), v: try: # Try ISO format as fallback. # This is needed for instance during imports. value = datetime.strptime( input, FormattedDatetimeWidget.date_format) except (ValueError, IndexError), v: raise ConversionError("Invalid datetime data", v) return value def _toFormValue(self, value): if value: value = value.strftime(self.date_format) return value class FormattedDatetimeDisplayWidget(DatetimeDisplayWidget): """A datetime widget that supports different (and _explicit_) date formats. This is a display widget. """ date_format = '%Y-%m-%d %H:%M:%S' show_time = False implements(IDisplayWidget) def __init__(self, context, request, *args, **kw): # try to grab date_format and show_time from bound schema field. date_format = getattr(context, 'date_format', self.date_format) if date_format is not None: self.date_format = date_format self.show_time = getattr(context, 'show_time', self.show_time) return super(FormattedDatetimeDisplayWidget, self).__init__( context, request, *args, **kw) def __call__(self): if self._renderedValueSet(): content = self._data else: content = self.context.default if content == self.context.missing_value: return "" content = content.strftime(self.date_format) return renderElement("span", contents=escape(content), cssClass=self.cssClass) class DatetimeLEWidget(FormattedDatetimeWidget): date_format = '%d/%m/%Y %H:%M:%S' class DatetimeDEWidget(FormattedDatetimeWidget): date_format = '%d.%m.%Y %H:%M:%S' class DatetimeUSWidget(FormattedDatetimeWidget): date_format = '%m/%d/%Y %H:%M:%S' class DatetimeLEDisplayWidget(FormattedDatetimeDisplayWidget): date_format = '%d/%m/%Y %H:%M:%S' class DatetimeDEDisplayWidget(FormattedDatetimeDisplayWidget): date_format = '%d.%m.%Y %H:%M:%S' class DatetimeUSDisplayWidget(FormattedDatetimeDisplayWidget): date_format = '%m/%d/%Y %H:%M:%S' def FriendlyDatetimeDisplayWidget(format): """Get a friendly datetime display widget for `format`. This widget is suitable for display forms. Valid `format` values are the keys of `DATE_FORMATS` dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S). This widget is not rendered with a specialized CSS tag for enabling JavaScript datepickers. `css_class` is ignored which means there is no difference between e.g. ``le`` and ``le-year``.` """ css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le']) datetime_format = date_format + ' %H:%M:%S' return CustomWidgetFactory( FormattedDateDisplayWidget, date_format=datetime_format)