source: main/waeup.kofa/trunk/src/waeup/kofa/widgets/datewidget.py @ 9002

Last change on this file since 9002 was 8193, checked in by uli, 13 years ago

Display local datetime when using FormattedDateDisplayWidget? (or
derived versions like FriendlyDateWidget?) with datetimes.

  • Property svn:keywords set to Id
File size: 8.8 KB
RevLine 
[7196]1## $Id: datewidget.py 8193 2012-04-17 11:35:01Z uli $
[6050]2##
[6867]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
[6050]4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
[7196]8##
[6050]9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
[7196]13##
[6050]14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19A datewidget with customizable date format.
20"""
[8193]21import pytz
[6050]22from datetime import datetime
[8193]23from zope.component import queryUtility
[6056]24from zope.formlib.interfaces import ConversionError, IDisplayWidget
[8147]25from zope.formlib.textwidgets import DateWidget, DateDisplayWidget, escape
[6056]26from zope.formlib.widget import renderElement, CustomWidgetFactory
27from zope.interface import implements
[8193]28from waeup.kofa.interfaces import IKofaUtils
29from waeup.kofa.utils.helpers import to_timezone
[6050]30
[8193]31
[6056]32#: A dictionary of supported date formats.
[6057]33#:
34#: The following formats are supported:
35#:
36#: ``iso``
37#:    ISO format ``YYYY-MM-DD``
38#: ``le``
39#:    little endian with slashes: ``DD/MM/YYYY``
40#: ``de``
41#:    german date format: ``DD.MM.YYYY``
42#: ``us``
43#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
44#:
45#: Furthermore we support for input widgets an additional year
46#: marker. Input date widgets with this marker provide also a year
47#: selector, handy for dates of birth etc.
48#:
49#: The year-supporting formats are similar to the basic versions above:
50#:
51#: ``iso-year``
52#:    ISO format ``YYYY-MM-DD``
53#: ``le-year``
54#:    little endian with slashes: ``DD/MM/YYYY``
55#: ``de-year``
56#:    german date format: ``DD.MM.YYYY``
57#: ``us-year``
58#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
[8147]59#:
[6057]60#: For date display widgets there is naturally no difference between a
61#: year and non-year setting (you can for instance use 'le' or 'le-year'
62#: with the same output).
[6056]63DATE_FORMATS = {
[6060]64    'iso': ('datepicker', '%Y-%m-%d'),
65    'le':  ('datepicker-le', '%d/%m/%Y'),
66    'de':  ('datepicker-de', '%d.%m.%Y'),
67    'us':  ('datepicker-us', '%m/%d/%Y'),
68    'iso-year': ('datepicker-year', '%Y-%m-%d'),
69    'le-year':  ('datepicker-le-year', '%d/%m/%Y'),
70    'de-year':  ('datepicker-de-year', '%d.%m.%Y'),
71    'us-year':  ('datepicker-us-year', '%m/%d/%Y'),
[6056]72    }
[8147]73
74#: a dict containing tuples (<FORMAT>, <SHOW_YEAR>) as keys and
75#: suitable CSS tags as values.
76FORMATS_BY_VALUE = dict(
77    [((val[1], 'year' in key), val[0]) for key, val in DATE_FORMATS.items()])
78
79class FormattedDateWidget(DateWidget):
80    """A date widget that supports different (and _explicit_) date formats.
81
82    If the widget is bound to a schema field with respective
83    attributes, it reads its `show_year` and `date_format` attributes
84    (see waeup.kofa.schema.FormattedDate for an example) and sets a
85    CSS tag according to these values.
86
87    The widget also accepts ISO format as a fallback, even if a
88    different format was set. This should help with imports.
89
90    This is an input widget.
91    """
92    date_format = '%Y-%m-%d'
93    show_year = False
94
95    def __init__(self, context, request, *args, **kw):
96        # try to grab date_format and show_year from bound schema field.
[8152]97        date_format = getattr(context, 'date_format', self.date_format)
98        if date_format is not None:
99            self.date_format = date_format
[8147]100        self.show_year = getattr(context, 'show_year', self.show_year)
[8150]101        # add css class determined by date_format and show_year
102        css_cls = FORMATS_BY_VALUE.get((self.date_format, self.show_year), '')
103        self.cssClass = ' '.join([self.cssClass, css_cls]).strip()
[8147]104        return super(FormattedDateWidget, self).__init__(
105            context, request, *args, **kw)
106
107    def _toFieldValue(self, input):
108        # In import files we can use the hash symbol at the end of a
109        # date string to avoid annoying automatic date transformation
110        # by Excel or Calc
111        input = input.strip('#')
112        if input == self._missing:
113            return self.context.missing_value
114        else:
115            try:
116                value = datetime.strptime(input, self.date_format)
117            except (ValueError, IndexError), v:
118                try:
119                    # Try ISO format as fallback.
120                    # This is needed for instance during imports.
121                    value = datetime.strptime(
122                        input, FormattedDateWidget.date_format)
123                except (ValueError, IndexError), v:
124                    raise ConversionError("Invalid datetime data", v)
125        return value.date()
126
127    def _toFormValue(self, value):
128        if value:
129            value = value.strftime(self.date_format)
130        return value
131
[8150]132
[8147]133class FormattedDateDisplayWidget(DateDisplayWidget):
134    """A date widget that supports different (and _explicit_) date formats.
135
136    This is a display widget.
[8193]137
138    It can also be used for displaying datetimes. If used to display a
139    datetime (not a date), the widget returns local datetime with
140    timezone set according to KofaUtils.
[8147]141    """
142    date_format = '%Y-%m-%d'
[8150]143    show_year = False
144
[8147]145    implements(IDisplayWidget)
146
[8150]147    def __init__(self, context, request, *args, **kw):
148        # try to grab date_format and show_year from bound schema field.
[8152]149        date_format = getattr(context, 'date_format', self.date_format)
150        if date_format is not None:
151            self.date_format = date_format
[8150]152        self.show_year = getattr(context, 'show_year', self.show_year)
153        return super(FormattedDateDisplayWidget, self).__init__(
154            context, request, *args, **kw)
155
[8147]156    def __call__(self):
157        if self._renderedValueSet():
158            content = self._data
159        else:
160            content = self.context.default
161        if content == self.context.missing_value:
162            return ""
[8193]163        if isinstance(content, datetime):
164            # shift value to local timezone
165            tz = pytz.utc
166            utils = queryUtility(IKofaUtils)
167            if utils is not None:
168                tz = utils.tzinfo
169            content = to_timezone(content, tz)
[8147]170        content = content.strftime(self.date_format)
171        return renderElement("span", contents=escape(content),
172                             cssClass=self.cssClass)
173
[8152]174class DateLEWidget(FormattedDateWidget):
175    date_format = '%d/%m/%Y'
176
177class DateDEWidget(FormattedDateWidget):
178    date_format = '%d.%m.%Y'
179
180class DateUSWidget(FormattedDateWidget):
181    date_format = '%m/%d/%Y'
182
183class DateLEDisplayWidget(FormattedDateDisplayWidget):
184    date_format = '%d/%m/%Y'
185
186class DateDEDisplayWidget(FormattedDateDisplayWidget):
187    date_format = '%d.%m.%Y'
188
189class DateUSDisplayWidget(FormattedDateDisplayWidget):
190    date_format = '%m/%d/%Y'
191
[6056]192def FriendlyDateWidget(format):
193    """Get a friendly date input widget for `format`.
[6057]194
195    This widget is suitable for edit and add forms.
[8147]196
[7599]197    Valid `format` values are the keys of `DATE_FORMATS`
[6057]198    dict. Default is ``le`` (little endian; DD/MM/YYYY).
199
200    Friendly date widgets are rendered with a specialized CSS tag for
201    enabling JavaScript datepickers.
[6056]202    """
203    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
204    return CustomWidgetFactory(
205        FormattedDateWidget,
206        cssClass=css_class,
207        date_format=date_format)
208
209def FriendlyDateDisplayWidget(format):
210    """Get a friendly date display widget for `format`.
[6057]211
212    This widget is suitable for display forms.
[8147]213
[7599]214    Valid `format` values are the keys of `DATE_FORMATS`
[6057]215    dict. Default is ``le`` (little endian; DD/MM/YYYY).
216
[6060]217    This widget is not rendered with a specialized CSS tag for
[8147]218    enabling JavaScript datepickers. `css_class` is ignored which means
[6060]219    there is nor difference between e.g. ``le`` and ``le-year``.`
[6056]220    """
221    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
222    return CustomWidgetFactory(
223        FormattedDateDisplayWidget,
224        date_format=date_format)
[6867]225
226def FriendlyDatetimeDisplayWidget(format):
227    """Get a friendly datetime display widget for `format`.
228
229    This widget is suitable for display forms.
230
[7599]231    Valid `format` values are the keys of `DATE_FORMATS`
[6868]232    dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S).
[6867]233
234    This widget is not rendered with a specialized CSS tag for
235    enabling JavaScript datepickers. `css_class` is ignored which means
[8147]236    there is no difference between e.g. ``le`` and ``le-year``.`
[6867]237    """
238    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
239    datetime_format = date_format + ' %H:%M:%S'
240    return CustomWidgetFactory(
[6868]241        FormattedDateDisplayWidget,
242        date_format=datetime_format)
Note: See TracBrowser for help on using the repository browser.