## $Id: datewidget.py 7599 2012-02-07 11:30:00Z henrik $
##
## Copyright (C) 2011 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.i18n import _
from zope.formlib.interfaces import ConversionError, IDisplayWidget
from zope.formlib.textwidgets import (
    DateWidget, DateDisplayWidget, escape, DatetimeDisplayWidget)
from zope.formlib.widget import renderElement, CustomWidgetFactory
from zope.interface import implements

class FormattedDateWidget(DateWidget):
    """A date widget that supports different (and _explicit_) date formats.

    This is an input widget.
    """
    date_format = '%Y-%m-%d'

    def _toFieldValue(self, input):
        if input == self._missing:
            return self.context.missing_value
        else:
            try:
                # In import files we can use the hash symbol at the end of a
                # date string to avoid annoying automatic date transformation
                # by Excel or Calc
                input = input.strip('#') 
                value = datetime.strptime(input, self.date_format)
            except (ValueError, IndexError), v:
                raise ConversionError(_("Invalid datetime data"), v)
        return value.date()

    def _toFormValue(self, value):
        if value:
            value = value.strftime(self.date_format)
        return value

class FormattedDateDisplayWidget(DateDisplayWidget):
    """A date widget that supports different (and _explicit_) date formats.

    This is a display widget.
    """
    date_format = '%Y-%m-%d'
    implements(IDisplayWidget)
    
    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)

#: A dictionary of supported date formats.
#:
#: The following formats are supported:
#:
#: ``iso``
#:    ISO format ``YYYY-MM-DD``
#: ``le``
#:    little endian with slashes: ``DD/MM/YYYY``
#: ``de``
#:    german date format: ``DD.MM.YYYY``
#: ``us``
#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
#:
#: Furthermore we support for input widgets an additional year
#: marker. Input date widgets with this marker provide also a year
#: selector, handy for dates of birth etc.
#:
#: The year-supporting formats are similar to the basic versions above:
#:
#: ``iso-year``
#:    ISO format ``YYYY-MM-DD``
#: ``le-year``
#:    little endian with slashes: ``DD/MM/YYYY``
#: ``de-year``
#:    german date format: ``DD.MM.YYYY``
#: ``us-year``
#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
#:    
#: For date display widgets there is naturally no difference between a
#: year and non-year setting (you can for instance use 'le' or 'le-year'
#: with the same output).
DATE_FORMATS = {
    'iso': ('datepicker', '%Y-%m-%d'),
    'le':  ('datepicker-le', '%d/%m/%Y'),
    'de':  ('datepicker-de', '%d.%m.%Y'),
    'us':  ('datepicker-us', '%m/%d/%Y'),
    'iso-year': ('datepicker-year', '%Y-%m-%d'),
    'le-year':  ('datepicker-le-year', '%d/%m/%Y'),
    'de-year':  ('datepicker-de-year', '%d.%m.%Y'),
    'us-year':  ('datepicker-us-year', '%m/%d/%Y'),
    }
def FriendlyDateWidget(format):
    """Get a friendly date input widget for `format`.

    This widget is suitable for edit and add forms.
    
    Valid `format` values are the keys of `DATE_FORMATS`
    dict. Default is ``le`` (little endian; DD/MM/YYYY).

    Friendly date widgets are rendered with a specialized CSS tag for
    enabling JavaScript datepickers.
    """
    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
    return CustomWidgetFactory(
        FormattedDateWidget,
        cssClass=css_class,
        date_format=date_format)

def FriendlyDateDisplayWidget(format):
    """Get a friendly date 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).

    This widget is not rendered with a specialized CSS tag for
    enabling JavaScript datepickers. `css_class` is ignored which means 
    there is nor difference between e.g. ``le`` and ``le-year``.`
    """
    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
    return CustomWidgetFactory(
        FormattedDateDisplayWidget,
        date_format=date_format)

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 nor 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)
