~bzr-pqm/bzr/bzr.dev

5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2007, 2008, 2009, 2011 Canonical Ltd
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
16
6379.6.3 by Jelmer Vernooij
Use absolute_import.
17
from __future__ import absolute_import
18
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
19
import calendar
20
import time
6280.2.1 by Matt Giuca
bzrlib.timestamp: More robust handling of time stamp string. (LP: #892657)
21
import re
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
22
2425.6.2 by Martin Pool
Make timestamps use existing format_date; document that function more
23
from bzrlib import osutils
24
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
25
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
26
def format_highres_date(t, offset=0):
27
    """Format a date, such that it includes higher precision in the
28
    seconds field.
29
30
    :param t:   The local time in fractional seconds since the epoch
31
    :type t: float
32
    :param offset:  The timezone offset in integer seconds
33
    :type offset: int
34
35
    Example: format_highres_date(time.time(), -time.timezone)
36
    this will return a date stamp for right now,
37
    formatted for the local timezone.
38
39
    >>> from bzrlib.osutils import format_date
40
    >>> format_date(1120153132.350850105, 0)
41
    'Thu 2005-06-30 17:38:52 +0000'
42
    >>> format_highres_date(1120153132.350850105, 0)
43
    'Thu 2005-06-30 17:38:52.350850105 +0000'
44
    >>> format_date(1120153132.350850105, -5*3600)
45
    'Thu 2005-06-30 12:38:52 -0500'
46
    >>> format_highres_date(1120153132.350850105, -5*3600)
47
    'Thu 2005-06-30 12:38:52.350850105 -0500'
48
    >>> format_highres_date(1120153132.350850105, 7200)
49
    'Thu 2005-06-30 19:38:52.350850105 +0200'
50
    >>> format_highres_date(1152428738.867522, 19800)
51
    'Sun 2006-07-09 12:35:38.867522001 +0530'
52
    """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
53
    if not isinstance(t, float):
54
        raise ValueError(t)
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
55
56
    # This has to be formatted for "original" date, so that the
57
    # revision XML entry will be reproduced faithfully.
58
    if offset is None:
59
        offset = 0
60
    tt = time.gmtime(t + offset)
61
3512.3.1 by Martin von Gagern
Hand-selected minimalistic set of changes from my setlocale branch.
62
    return (osutils.weekdays[tt[6]] +
63
            time.strftime(" %Y-%m-%d %H:%M:%S", tt)
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
64
            # Get the high-res seconds, but ignore the 0
65
            + ('%.9f' % (t - int(t)))[1:]
66
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
67
68
69
def unpack_highres_date(date):
70
    """This takes the high-resolution date stamp, and
71
    converts it back into the tuple (timestamp, timezone)
72
    Where timestamp is in real UTC since epoch seconds, and timezone is an
73
    integer number of seconds offset.
74
75
    :param date: A date formated by format_highres_date
76
    :type date: string
77
78
    """
3512.3.1 by Martin von Gagern
Hand-selected minimalistic set of changes from my setlocale branch.
79
    # Weekday parsing is locale sensitive, so drop the weekday
80
    space_loc = date.find(' ')
81
    if space_loc == -1 or date[:space_loc] not in osutils.weekdays:
82
        raise ValueError(
83
            'Date string does not contain a day of week: %r' % date)
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
84
    # Up until the first period is a datestamp that is generated
85
    # as normal from time.strftime, so use time.strptime to
86
    # parse it
87
    dot_loc = date.find('.')
88
    if dot_loc == -1:
89
        raise ValueError(
90
            'Date string does not contain high-precision seconds: %r' % date)
3512.3.1 by Martin von Gagern
Hand-selected minimalistic set of changes from my setlocale branch.
91
    base_time = time.strptime(date[space_loc:dot_loc], " %Y-%m-%d %H:%M:%S")
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
92
    fract_seconds, offset = date[dot_loc:].split()
93
    fract_seconds = float(fract_seconds)
94
95
    offset = int(offset)
96
97
    hours = int(offset / 100)
98
    minutes = (offset % 100)
99
    seconds_offset = (hours * 3600) + (minutes * 60)
100
101
    # time.mktime returns localtime, but calendar.timegm returns UTC time
102
    timestamp = calendar.timegm(base_time)
103
    timestamp -= seconds_offset
104
    # Add back in the fractional seconds
105
    timestamp += fract_seconds
106
    return (timestamp, seconds_offset)
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
107
108
109
def format_patch_date(secs, offset=0):
110
    """Format a POSIX timestamp and optional offset as a patch-style date.
111
112
    Inverse of parse_patch_date.
113
    """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
114
    if offset % 60 != 0:
115
        raise ValueError(
116
        "can't represent timezone %s offset by fractional minutes" % offset)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
117
    # so that we don't need to do calculations on pre-epoch times,
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
118
    # which doesn't work with win32 python gmtime, we always
119
    # give the epoch in utc
120
    if secs == 0:
121
        offset = 0
122
    if secs + offset < 0:
123
        from warnings import warn
124
        warn("gmtime of negative time (%s, %s) may not work on Windows" %
125
                (secs, offset))
2425.6.2 by Martin Pool
Make timestamps use existing format_date; document that function more
126
    return osutils.format_date(secs, offset=offset,
127
            date_fmt='%Y-%m-%d %H:%M:%S')
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
128
129
6280.2.1 by Matt Giuca
bzrlib.timestamp: More robust handling of time stamp string. (LP: #892657)
130
# Format for patch dates: %Y-%m-%d %H:%M:%S [+-]%H%M
131
# Groups: 1 = %Y-%m-%d %H:%M:%S; 2 = [+-]%H; 3 = %M
132
RE_PATCHDATE = re.compile("(\d+-\d+-\d+\s+\d+:\d+:\d+)\s*([+-]\d\d)(\d\d)$")
6280.2.3 by Matt Giuca
bzrlib.timestamp: Better error message if the string is missing a timezone offset.
133
RE_PATCHDATE_NOOFFSET = re.compile("\d+-\d+-\d+\s+\d+:\d+:\d+$")
6280.2.1 by Matt Giuca
bzrlib.timestamp: More robust handling of time stamp string. (LP: #892657)
134
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
135
def parse_patch_date(date_str):
136
    """Parse a patch-style date into a POSIX timestamp and offset.
137
138
    Inverse of format_patch_date.
139
    """
6280.2.1 by Matt Giuca
bzrlib.timestamp: More robust handling of time stamp string. (LP: #892657)
140
    match = RE_PATCHDATE.match(date_str)
141
    if match is None:
6280.2.3 by Matt Giuca
bzrlib.timestamp: Better error message if the string is missing a timezone offset.
142
        if RE_PATCHDATE_NOOFFSET.match(date_str) is not None:
143
            raise ValueError("time data %r is missing a timezone offset"
144
                % date_str)
145
        else:
146
            raise ValueError("time data %r does not match format " % date_str
147
                + "'%Y-%m-%d %H:%M:%S %z'")
6280.2.1 by Matt Giuca
bzrlib.timestamp: More robust handling of time stamp string. (LP: #892657)
148
    secs_str = match.group(1)
6280.2.2 by Matt Giuca
bzrlib.timestamp: Now checks offset hour and minute to ensure they are within correct range.
149
    offset_hours, offset_mins = int(match.group(2)), int(match.group(3))
150
    if abs(offset_hours) >= 24 or offset_mins >= 60:
151
        raise ValueError("invalid timezone %r" %
152
            (match.group(2) + match.group(3)))
153
    offset = offset_hours * 3600 + offset_mins * 60
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
154
    tm_time = time.strptime(secs_str, '%Y-%m-%d %H:%M:%S')
155
    # adjust seconds according to offset before converting to POSIX
156
    # timestamp, to avoid edge problems
157
    tm_time = tm_time[:5] + (tm_time[5] - offset,) + tm_time[6:]
158
    secs = calendar.timegm(tm_time)
159
    return secs, offset