Skip to content

Commit

Permalink
Serialize incoming zoned date/datetime instances into strings of the …
Browse files Browse the repository at this point in the history
…appropriate format before relaying to taskwarrior.
  • Loading branch information
coddingtonbear committed Feb 2, 2014
1 parent 9b394d5 commit 0516cc1
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 8 deletions.
71 changes: 70 additions & 1 deletion taskw/test/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import datetime
import random

import dateutil.tz
from nose.tools import eq_
import pytz

from taskw.utils import decode_task, encode_task
from taskw.utils import (
decode_task, encode_task, encode_task_experimental, DATE_FORMAT
)

TASK = {'description': "task 2 http://www.google.com/",
'entry': "1325011643",
Expand Down Expand Up @@ -88,3 +93,67 @@ def test_ordering(self):
task1 = dict(shuffled(TASK.items()))
task2 = dict(shuffled(TASK.items()))
eq_(encode_task(task1), encode_task(task2))

def test_encodes_dates(self):
arbitrary_date = datetime.date(2014, 3, 2)
task = {
'arbitrary_field': arbitrary_date
}

actual_encoded_task = encode_task_experimental(task)
expected_encoded_task = encode_task_experimental(
{
'arbitrary_field': arbitrary_date.strftime(DATE_FORMAT)
}
)

eq_(
actual_encoded_task,
expected_encoded_task,
)

def test_encodes_naive_datetimes(self):
arbitrary_naive_datetime = datetime.datetime.now()
task = {
'arbitrary_field': arbitrary_naive_datetime
}

actual_encoded_task = encode_task_experimental(task)
expected_encoded_task = encode_task_experimental(
{
'arbitrary_field': (
arbitrary_naive_datetime
.replace(tzinfo=dateutil.tz.tzlocal())
.astimezone(pytz.utc).strftime(DATE_FORMAT)
)
}
)

eq_(
actual_encoded_task,
expected_encoded_task,
)

def test_encodes_zoned_datetimes(self):
arbitrary_timezone = pytz.timezone('America/Los_Angeles')
arbitrary_zoned_datetime = datetime.datetime.now().replace(
tzinfo=arbitrary_timezone
)
task = {
'arbitrary_field': arbitrary_zoned_datetime
}

actual_encoded_task = encode_task_experimental(task)
expected_encoded_task = encode_task_experimental(
{
'arbitrary_field': (
arbitrary_zoned_datetime
.astimezone(pytz.utc).strftime(DATE_FORMAT)
)
}
)

eq_(
actual_encoded_task,
expected_encoded_task,
)
26 changes: 19 additions & 7 deletions taskw/utils.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
""" Various utilties """

import datetime
import re
from operator import itemgetter
import six
import datetime

try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict

import dateutil.tz
import pytz
import six

DATE_FORMAT = '%Y%m%dT%H%M%SZ'


encode_replacements = OrderedDict([
('\\', '\\\\'),
('\"', '&dquot;'),
Expand Down Expand Up @@ -48,12 +54,18 @@ def encode_task_experimental(task):
if 'tags' in task:
task['tags'] = ','.join(task['tags'])
for k in task:
for unsafe, safe in six.iteritems(encode_replacements_experimental):
if isinstance(task[k], basestring):
task[k] = task[k].replace(unsafe, safe)

if isinstance(task[k], datetime.datetime):
task[k] = task[k].strftime("%Y%m%dT%M%H%SZ")
if not task[k].tzinfo:
# Dates not having timezone information should be
# assumed to be in local time
task[k] = task[k].replace(tzinfo=dateutil.tz.tzlocal())
# All times should be converted to UTC before serializing
task[k] = task[k].astimezone(pytz.utc).strftime(DATE_FORMAT)
elif isinstance(task[k], datetime.date):
task[k] = task[k].strftime(DATE_FORMAT)
elif isinstance(task[k], six.string_types):
for unsafe, safe in six.iteritems(encode_replacements_experimental):
task[k] = task[k].replace(unsafe, safe)

# Then, format it as a string
return "%s\n" % " ".join([
Expand Down

0 comments on commit 0516cc1

Please sign in to comment.