Skip to content

Commit

Permalink
Merge 9a6004f into 3dcbafa
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannes Raggam committed Mar 31, 2014
2 parents 3dcbafa + 9a6004f commit ab9a955
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 45 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -4,6 +4,12 @@ Changelog
1.6.0 (unreleased)
------------------

- Add default_timezone widget attribute to DatetimeWidget. If used and set to a
valid Olson DB/pytz timezone identifier or to an callback returning such, the
datetime object returned by the widget will be localized to that timezone.
This changes the timezone related behavior from version 1.4.0.
[thet]

- fix related items widget using getSource when it should use getVocabulary
[davisagli]

Expand Down
41 changes: 19 additions & 22 deletions plone/app/widgets/dx.py
Expand Up @@ -2,6 +2,7 @@

from AccessControl import getSecurityManager
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import safe_callable
from datetime import date
from datetime import datetime

Expand Down Expand Up @@ -195,27 +196,15 @@ def toFieldValue(self, value):
else:
value += ['00', '00']

# Eventually set timezone
old_val = getattr(self.widget.context, self.field.getName(), None)
if getattr(old_val, 'tzinfo', False):
# Only set when field was previously set and only if it's value
# is a timezone aware datetime object.
# effective date for example is timezone naive, at the moment.
#
# We only ask, if there is a timezone information at all but try to
# use the 'timezone' attribute on the widget's context, since it
# represents the timezone, the event and it's form input are
# related to.
timezone = getattr(self.widget.context, 'timezone', None)
if timezone:
# plone.app.event stores it's timezone on the context.
timezone = pytz.timezone(timezone)
else:
# This better should use the tzinfo object from old_val. But we
# have to find, how to correctly localize the value with it.
timezone = pytz.utc
return timezone.localize(datetime(*map(int, value)))
return datetime(*map(int, value))
# TODO: respect the selected zone from the widget and just fall back
# to default_zone
default_zone = self.widget.default_timezone
zone = default_zone() if safe_callable(default_zone) else default_zone
ret = datetime(*map(int, value))
if zone:
tzinfo = pytz.timezone(zone)
ret = tzinfo.localize(ret)
return ret


class SelectWidgetConverterBase(object):
Expand Down Expand Up @@ -511,7 +500,13 @@ def render(self):


class DatetimeWidget(DateWidget, HTMLInputWidget):
"""Datetime widget for z3c.form."""
"""Datetime widget for z3c.form.
:param default_timezone: A Olson DB/pytz timezone identifier or a callback
returning such an identifier.
:type default_timezone: String or callback
"""

_converter = DatetimeWidgetConverter
_formater = 'dateTime'
Expand All @@ -520,6 +515,8 @@ class DatetimeWidget(DateWidget, HTMLInputWidget):

pattern_options = DateWidget.pattern_options.copy()

default_timezone = None

def _base_args(self):
"""Method which will calculate _base class arguments.
Expand Down
61 changes: 38 additions & 23 deletions plone/app/widgets/tests/test_dx.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-

from datetime import date
from datetime import datetime
from mock import Mock
Expand Down Expand Up @@ -36,6 +35,7 @@

import mock
import json
import pytz

try:
import unittest2 as unittest
Expand Down Expand Up @@ -271,56 +271,71 @@ def test_data_converter(self):
'21-10-30 15:40',
)

def test_data_converter_timezone(self):
def test_data_converter__no_timezone(self):
"""When no timezone is set, don't apply one.
"""
from plone.app.widgets.dx import DatetimeWidgetConverter
context = Mock()

# Test for previously set datetime, without tzinfo and no timezone on
# context.
# Should not apply a timezone to the field value.
dt = datetime(2013, 11, 13, 10, 20)
setattr(context, self.field.getName(), dt)
self.widget.context = context
self.widget.default_timezone = None

converter = DatetimeWidgetConverter(self.field, self.widget)
self.assertEqual(
converter.toFieldValue('2013-11-13 10:20'),
datetime(2013, 11, 13, 10, 20),
)

# Test for previously set datetime, with tzinfo but no timezone on
# context.
# Should apply UTZ zone to field value, to be able to be compared with
# the timezone aware datetime from the context.
import pytz
nl = pytz.timezone('Europe/Amsterdam')
dt = nl.localize(datetime(2013, 11, 13, 10, 20))
# cleanup
self.widget.context = None
self.widget.default_timezone = None

def test_data_converter__timezone_id(self):
"""When a (pytz) timezone id is set, use that.
"""
from plone.app.widgets.dx import DatetimeWidgetConverter
context = Mock()

dt = datetime(2013, 11, 13, 10, 20)
setattr(context, self.field.getName(), dt)
context.timezone = None
self.widget.context = context
self.widget.default_timezone = 'Europe/Vienna'
tz = pytz.timezone('Europe/Vienna')

converter = DatetimeWidgetConverter(self.field, self.widget)
self.assertEqual(
converter.toFieldValue('2013-11-13 10:20'),
pytz.utc.localize(datetime(2013, 11, 13, 10, 20)),
tz.localize(datetime(2013, 11, 13, 10, 20)),
)

# Test for previously set datetime, with tzinfo and timezone one
# context.
# Should apply the zone based on "timezone" value to field value, to be
# able to be CORRECTLY compared with the timezone aware datetime from
# the context.
nl = pytz.timezone('Europe/Amsterdam')
dt = nl.localize(datetime(2013, 11, 13, 10, 20))
# cleanup
self.widget.context = None
self.widget.default_timezone = None

def test_data_converter__timezone_callback(self):
"""When a timezone callback is set, returning a (pytz) timezone id,
use that.
"""
from plone.app.widgets.dx import DatetimeWidgetConverter
context = Mock()

dt = datetime(2013, 11, 13, 10, 20)
setattr(context, self.field.getName(), dt)
context.timezone = "Europe/Amsterdam"
self.widget.context = context
self.widget.default_timezone = lambda: 'Europe/Vienna'
tz = pytz.timezone('Europe/Vienna')

converter = DatetimeWidgetConverter(self.field, self.widget)
self.assertEqual(
converter.toFieldValue('2013-11-13 10:20'),
nl.localize(datetime(2013, 11, 13, 10, 20)),
tz.localize(datetime(2013, 11, 13, 10, 20)),
)

# cleanup
self.widget.context = None
self.widget.default_timezone = None

def test_fieldwidget(self):
from plone.app.widgets.dx import DatetimeWidget
Expand Down

0 comments on commit ab9a955

Please sign in to comment.