summaryrefslogtreecommitdiff
path: root/test_zdump.py
blob: 0a887886c0ea4391c956137be1f1f3f89868b40c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/python2.7

import os.path, sys
sys.path.insert(0, os.path.join('build', 'dist'))

from datetime import datetime, timedelta
import re
from time import strptime
import unittest
import pytz


class ZdumpTestCase(unittest.TestCase):
    def utc_to_local_check(self, zone, utc_dt, loc_dt, loc_tzname, is_dst):
        loc_tz = pytz.timezone(zone)
        self.failUnlessEqual(
            utc_dt.astimezone(loc_tz).replace(tzinfo=None),
            loc_dt.replace(tzinfo=None))

    def local_to_utc_check(self, zone, utc_dt, loc_dt, loc_tzname, is_dst):
        self.failUnlessEqual(
            loc_dt.astimezone(pytz.utc).replace(tzinfo=None),
            utc_dt.replace(tzinfo=None))


def test_suite():
    testcases = []
    raw_data = open(
        os.path.join(os.path.dirname(__file__), 'zdump.out'), 'r').readlines()
    last_zone = None
    test_class = None
    zdump_line_re = re.compile(r'''(?x)
        ^([^\s]+) \s+ (.+) \s UT \s+ = \s+ (.+) \s ([^\s]+) \s+
                               isdst=(0|1) \s+ gmtoff=[\-\d]+ \s*$
        ''')
    for i in range(0, len(raw_data)):
        line = raw_data[i]
        m = zdump_line_re.search(line)
        if m is None:
            raise RuntimeError('Dud line %r' % (line,))
        zone, utc_string, loc_string, tzname, is_dst = m.groups()
        is_dst = bool(int(is_dst))

        if zone != last_zone:
            classname = zone.replace(
                    '+', '_plus_').replace('-', '_minus_').replace('/','_')
            test_class = type(classname, (ZdumpTestCase,), {})
            testcases.append(test_class)
            last_zone = zone
            skip_next_local = False

        utc_dt = datetime(
            *strptime(utc_string, '%a %b %d %H:%M:%S %Y')[:6])
        loc_dt = datetime(
            *strptime(loc_string, '%a %b %d %H:%M:%S %Y')[:6])

        def round_dt(loc_dt, utc_dt):
            # Urgh - utcoffset() and dst() have to be rounded to the nearest
            # minute, so we need to break our tests to match this limitation
            real_offset = loc_dt - utc_dt
            secs = real_offset.seconds + real_offset.days*86400
            fake_offset = timedelta(seconds=int((secs+30)//60)*60)
            return utc_dt + fake_offset

        loc_dt = round_dt(loc_dt, utc_dt)

        # If the naive time on the next line is less than on this
        # line, and we aren't seeing an end-of-dst transition, then
        # we can't do our local->utc tests for either this nor the
        # next line since we are in an ambiguous time period (ie.
        # we have wound back the clock but don't have differing
        # is_dst flags to resolve the ambiguity)
        skip_local = skip_next_local
        skip_next_local = False
        try:
            m = zdump_line_re.match(raw_data[i+1])
        except IndexError:
            m = None
        if m is not None:
            (next_zone, next_utc_string, next_loc_string,
                next_tzname, next_is_dst) = m.groups()
            next_is_dst = bool(int(next_is_dst))
            if next_zone == zone and next_is_dst == is_dst:
                next_utc_dt = datetime(
                    *strptime(next_utc_string, '%a %b %d %H:%M:%S %Y')[:6])
                next_loc_dt = round_dt(
                    datetime(*strptime(
                        next_loc_string, '%a %b %d %H:%M:%S %Y')[:6]),
                    next_utc_dt)
                if next_loc_dt <= loc_dt:
                    skip_local = True
                    skip_next_local = True

        loc_tz = pytz.timezone(zone)
        loc_dt = loc_tz.localize(loc_dt, is_dst)

        utc_dt = pytz.utc.localize(utc_dt)

        test_name = 'test_utc_to_local_%04d_%02d_%02d_%02d_%02d_%02d' % (
            utc_dt.year, utc_dt.month, utc_dt.day,
            utc_dt.hour, utc_dt.minute, utc_dt.second)
        def test_utc_to_local(
            self, zone=zone, utc_dt=utc_dt, loc_dt=loc_dt, tzname=tzname,
            is_dst=is_dst):
            self.utc_to_local_check(zone, utc_dt, loc_dt, tzname, is_dst)
        test_utc_to_local.__name__ = test_name
        setattr(test_class, test_name, test_utc_to_local)

        if not skip_local:
            test_name = 'test_local_to_utc_%04d_%02d_%02d_%02d_%02d_%02d' % (
                loc_dt.year, loc_dt.month, loc_dt.day,
                loc_dt.hour, loc_dt.minute, loc_dt.second)
            if is_dst:
                test_name += '_dst'
            else:
                test_name += '_nodst'
            def test_local_to_utc(
                self, zone=zone, utc_dt=utc_dt, loc_dt=loc_dt, tzname=tzname,
                is_dst=is_dst):
                self.local_to_utc_check(zone, utc_dt, loc_dt, tzname, is_dst)
            test_local_to_utc.__name__ = test_name
            setattr(test_class, test_name, test_local_to_utc)

    classname = zone.replace(
            '+', '_plus_').replace('-', '_minus_').replace('/','_')
    test_class = type(classname, (ZdumpTestCase,), {})
    testcases.append(test_class)

    suite = unittest.TestSuite()
    while testcases:
        suite.addTest(unittest.makeSuite(testcases.pop()))
    return suite

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')