summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxi <xi@18f92427-320e-0410-9341-c67f048884a3>2006-08-16 18:22:38 +0000
committerxi <xi@18f92427-320e-0410-9341-c67f048884a3>2006-08-16 18:22:38 +0000
commit3193c0fd9dd09c961e7f4a528ba01e0d732a36ab (patch)
tree623346d649b9437682cae29080a506dd939eaa7d
parentcc48dfaed13825e8d78ad1c3de8b51668fdeab92 (diff)
downloadpyyaml-3193c0fd9dd09c961e7f4a528ba01e0d732a36ab.tar.gz
Fix timestamp constructing and representing (close #25).
git-svn-id: http://svn.pyyaml.org/pyyaml/trunk@225 18f92427-320e-0410-9341-c67f048884a3
-rw-r--r--lib/yaml/constructor.py55
-rw-r--r--lib/yaml/representer.py25
-rw-r--r--tests/data/construct-timestamp.code2
-rw-r--r--tests/data/timestamp-bugs.code7
-rw-r--r--tests/data/timestamp-bugs.data5
-rw-r--r--tests/test_constructor.py28
-rw-r--r--tests/test_representer.py6
7 files changed, 77 insertions, 51 deletions
diff --git a/lib/yaml/constructor.py b/lib/yaml/constructor.py
index cbbcd78..d1d4f6a 100644
--- a/lib/yaml/constructor.py
+++ b/lib/yaml/constructor.py
@@ -5,11 +5,7 @@ __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
from error import *
from nodes import *
-try:
- import datetime
- datetime_available = True
-except ImportError:
- datetime_available = False
+import datetime
try:
set
@@ -304,28 +300,36 @@ class SafeConstructor(BaseConstructor):
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
- (?:\.(?P<fraction>[0-9]*))?
- (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
- (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
+ (?:(?P<fraction>\.[0-9]*))?
+ (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
+ (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
def construct_yaml_timestamp(self, node):
value = self.construct_scalar(node)
match = self.timestamp_regexp.match(node.value)
values = match.groupdict()
- for key in values:
- if values[key]:
- values[key] = int(values[key])
- else:
- values[key] = 0
- fraction = values['fraction']
- if fraction:
- while 10*fraction < 1000000:
- fraction *= 10
- values['fraction'] = fraction
- stamp = datetime.datetime(values['year'], values['month'], values['day'],
- values['hour'], values['minute'], values['second'], values['fraction'])
- diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
- return stamp-diff
+ year = int(values['year'])
+ month = int(values['month'])
+ day = int(values['day'])
+ if not values['hour']:
+ return datetime.date(year, month, day)
+ hour = int(values['hour'])
+ minute = int(values['minute'])
+ second = int(values['second'])
+ fraction = 0
+ if values['fraction']:
+ fraction = int(float(values['fraction'])*1000000)
+ delta = None
+ if values['tz_sign']:
+ tz_hour = int(values['tz_hour'])
+ tz_minute = int(values['tz_minute'] or 0)
+ delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
+ if values['tz_sign'] == '-':
+ delta = -delta
+ data = datetime.datetime(year, month, day, hour, minute, second, fraction)
+ if delta:
+ data -= delta
+ return data
def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
@@ -429,10 +433,9 @@ SafeConstructor.add_constructor(
u'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
-if datetime_available:
- SafeConstructor.add_constructor(
- u'tag:yaml.org,2002:timestamp',
- SafeConstructor.construct_yaml_timestamp)
+SafeConstructor.add_constructor(
+ u'tag:yaml.org,2002:timestamp',
+ SafeConstructor.construct_yaml_timestamp)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:omap',
diff --git a/lib/yaml/representer.py b/lib/yaml/representer.py
index 44957c4..3563a4c 100644
--- a/lib/yaml/representer.py
+++ b/lib/yaml/representer.py
@@ -5,11 +5,7 @@ __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
from error import *
from nodes import *
-try:
- import datetime
- datetime_available = True
-except ImportError:
- datetime_available = False
+import datetime
try:
set
@@ -241,17 +237,11 @@ class SafeRepresenter(BaseRepresenter):
return self.represent_mapping(u'tag:yaml.org,2002:set', value)
def represent_date(self, data):
- value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
+ value = unicode(data.isoformat())
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_datetime(self, data):
- value = u'%04d-%02d-%02d %02d:%02d:%02d' \
- % (data.year, data.month, data.day,
- data.hour, data.minute, data.second)
- if data.microsecond:
- value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
- if data.utcoffset():
- value += unicode(data.utcoffset())
+ value = unicode(data.isoformat(' '))
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_yaml_object(self, tag, data, cls, flow_style=None):
@@ -297,11 +287,10 @@ SafeRepresenter.add_representer(dict,
SafeRepresenter.add_representer(set,
SafeRepresenter.represent_set)
-if datetime_available:
- SafeRepresenter.add_representer(datetime.date,
- SafeRepresenter.represent_date)
- SafeRepresenter.add_representer(datetime.datetime,
- SafeRepresenter.represent_datetime)
+SafeRepresenter.add_representer(datetime.date,
+ SafeRepresenter.represent_date)
+SafeRepresenter.add_representer(datetime.datetime,
+ SafeRepresenter.represent_datetime)
SafeRepresenter.add_representer(None,
SafeRepresenter.represent_undefined)
diff --git a/tests/data/construct-timestamp.code b/tests/data/construct-timestamp.code
index 288022e..ffc3b2f 100644
--- a/tests/data/construct-timestamp.code
+++ b/tests/data/construct-timestamp.code
@@ -3,5 +3,5 @@
"valid iso8601": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
"space separated": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
"no time zone (Z)": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
- "date (00:00:00Z)": datetime.datetime(2002, 12, 14),
+ "date (00:00:00Z)": datetime.date(2002, 12, 14),
}
diff --git a/tests/data/timestamp-bugs.code b/tests/data/timestamp-bugs.code
new file mode 100644
index 0000000..47ac491
--- /dev/null
+++ b/tests/data/timestamp-bugs.code
@@ -0,0 +1,7 @@
+[
+ datetime.datetime(2001, 12, 15, 3, 29, 43, 100000),
+ datetime.datetime(2001, 12, 14, 16, 29, 43, 100000),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 1010),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 0, FixedOffset(60, "+1")),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 0, FixedOffset(-90, "-1:30")),
+]
diff --git a/tests/data/timestamp-bugs.data b/tests/data/timestamp-bugs.data
new file mode 100644
index 0000000..bc3223f
--- /dev/null
+++ b/tests/data/timestamp-bugs.data
@@ -0,0 +1,5 @@
+- 2001-12-14 21:59:43.10 -5:30
+- 2001-12-14 21:59:43.10 +5:30
+- 2001-12-14 21:59:43.00101
+- 2001-12-14 21:59:43+1
+- 2001-12-14 21:59:43-1:30
diff --git a/tests/test_constructor.py b/tests/test_constructor.py
index 200b112..0c873e9 100644
--- a/tests/test_constructor.py
+++ b/tests/test_constructor.py
@@ -1,9 +1,7 @@
import test_appliance
-try:
- import datetime
-except ImportError:
- pass
+
+import datetime
try:
set
except NameError:
@@ -240,6 +238,22 @@ class MyDict(dict):
def __eq__(self, other):
return type(self) is type(other) and dict(self) == dict(other)
+class FixedOffset(datetime.tzinfo):
+
+ def __init__(self, offset, name):
+ self.__offset = datetime.timedelta(minutes=offset)
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return datetime.timedelta(0)
+
+
def execute(code):
exec code
return value
@@ -257,7 +271,7 @@ class TestConstructorTypes(test_appliance.TestAppliance):
self.failUnlessEqual(type(data1), type(data2))
try:
self.failUnlessEqual(data1, data2)
- except AssertionError:
+ except (AssertionError, TypeError):
if isinstance(data1, dict):
data1 = [(repr(key), value) for key, value in data1.items()]
data1.sort()
@@ -274,6 +288,10 @@ class TestConstructorTypes(test_appliance.TestAppliance):
if (item1 != item1 or (item1 == 0.0 and item1 == 1.0)) and \
(item2 != item2 or (item2 == 0.0 and item2 == 1.0)):
continue
+ if isinstance(item1, datetime.datetime):
+ item1 = item1.utctimetuple()
+ if isinstance(item2, datetime.datetime):
+ item2 = item2.utctimetuple()
self.failUnlessEqual(item1, item2)
else:
raise
diff --git a/tests/test_representer.py b/tests/test_representer.py
index 8097733..961b477 100644
--- a/tests/test_representer.py
+++ b/tests/test_representer.py
@@ -19,7 +19,7 @@ class TestRepresenterTypes(test_appliance.TestAppliance):
self.failUnlessEqual(type(data1), type(data2))
try:
self.failUnlessEqual(data1, data2)
- except AssertionError:
+ except (AssertionError, TypeError):
if isinstance(data1, dict):
data1 = [(repr(key), value) for key, value in data1.items()]
data1.sort()
@@ -36,6 +36,10 @@ class TestRepresenterTypes(test_appliance.TestAppliance):
if (item1 != item1 or (item1 == 0.0 and item1 == 1.0)) and \
(item2 != item2 or (item2 == 0.0 and item2 == 1.0)):
continue
+ if isinstance(item1, datetime.datetime):
+ item1 = item1.utctimetuple()
+ if isinstance(item2, datetime.datetime):
+ item2 = item2.utctimetuple()
self.failUnlessEqual(item1, item2)
else:
raise