~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/timestamp.py

  • Committer: Jelmer Vernooij
  • Date: 2011-11-30 20:02:16 UTC
  • mto: This revision was merged to the branch mainline in revision 6333.
  • Revision ID: jelmer@samba.org-20111130200216-aoju21pdl20d1gkd
Consistently pass tree path when exporting.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007, 2008, 2009, 2011 Canonical Ltd
 
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
import calendar
 
18
import time
 
19
import re
 
20
 
 
21
from bzrlib import osutils
 
22
 
 
23
 
 
24
def format_highres_date(t, offset=0):
 
25
    """Format a date, such that it includes higher precision in the
 
26
    seconds field.
 
27
 
 
28
    :param t:   The local time in fractional seconds since the epoch
 
29
    :type t: float
 
30
    :param offset:  The timezone offset in integer seconds
 
31
    :type offset: int
 
32
 
 
33
    Example: format_highres_date(time.time(), -time.timezone)
 
34
    this will return a date stamp for right now,
 
35
    formatted for the local timezone.
 
36
 
 
37
    >>> from bzrlib.osutils import format_date
 
38
    >>> format_date(1120153132.350850105, 0)
 
39
    'Thu 2005-06-30 17:38:52 +0000'
 
40
    >>> format_highres_date(1120153132.350850105, 0)
 
41
    'Thu 2005-06-30 17:38:52.350850105 +0000'
 
42
    >>> format_date(1120153132.350850105, -5*3600)
 
43
    'Thu 2005-06-30 12:38:52 -0500'
 
44
    >>> format_highres_date(1120153132.350850105, -5*3600)
 
45
    'Thu 2005-06-30 12:38:52.350850105 -0500'
 
46
    >>> format_highres_date(1120153132.350850105, 7200)
 
47
    'Thu 2005-06-30 19:38:52.350850105 +0200'
 
48
    >>> format_highres_date(1152428738.867522, 19800)
 
49
    'Sun 2006-07-09 12:35:38.867522001 +0530'
 
50
    """
 
51
    if not isinstance(t, float):
 
52
        raise ValueError(t)
 
53
 
 
54
    # This has to be formatted for "original" date, so that the
 
55
    # revision XML entry will be reproduced faithfully.
 
56
    if offset is None:
 
57
        offset = 0
 
58
    tt = time.gmtime(t + offset)
 
59
 
 
60
    return (osutils.weekdays[tt[6]] +
 
61
            time.strftime(" %Y-%m-%d %H:%M:%S", tt)
 
62
            # Get the high-res seconds, but ignore the 0
 
63
            + ('%.9f' % (t - int(t)))[1:]
 
64
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
 
65
 
 
66
 
 
67
def unpack_highres_date(date):
 
68
    """This takes the high-resolution date stamp, and
 
69
    converts it back into the tuple (timestamp, timezone)
 
70
    Where timestamp is in real UTC since epoch seconds, and timezone is an
 
71
    integer number of seconds offset.
 
72
 
 
73
    :param date: A date formated by format_highres_date
 
74
    :type date: string
 
75
 
 
76
    """
 
77
    # Weekday parsing is locale sensitive, so drop the weekday
 
78
    space_loc = date.find(' ')
 
79
    if space_loc == -1 or date[:space_loc] not in osutils.weekdays:
 
80
        raise ValueError(
 
81
            'Date string does not contain a day of week: %r' % date)
 
82
    # Up until the first period is a datestamp that is generated
 
83
    # as normal from time.strftime, so use time.strptime to
 
84
    # parse it
 
85
    dot_loc = date.find('.')
 
86
    if dot_loc == -1:
 
87
        raise ValueError(
 
88
            'Date string does not contain high-precision seconds: %r' % date)
 
89
    base_time = time.strptime(date[space_loc:dot_loc], " %Y-%m-%d %H:%M:%S")
 
90
    fract_seconds, offset = date[dot_loc:].split()
 
91
    fract_seconds = float(fract_seconds)
 
92
 
 
93
    offset = int(offset)
 
94
 
 
95
    hours = int(offset / 100)
 
96
    minutes = (offset % 100)
 
97
    seconds_offset = (hours * 3600) + (minutes * 60)
 
98
 
 
99
    # time.mktime returns localtime, but calendar.timegm returns UTC time
 
100
    timestamp = calendar.timegm(base_time)
 
101
    timestamp -= seconds_offset
 
102
    # Add back in the fractional seconds
 
103
    timestamp += fract_seconds
 
104
    return (timestamp, seconds_offset)
 
105
 
 
106
 
 
107
def format_patch_date(secs, offset=0):
 
108
    """Format a POSIX timestamp and optional offset as a patch-style date.
 
109
 
 
110
    Inverse of parse_patch_date.
 
111
    """
 
112
    if offset % 60 != 0:
 
113
        raise ValueError(
 
114
        "can't represent timezone %s offset by fractional minutes" % offset)
 
115
    # so that we don't need to do calculations on pre-epoch times,
 
116
    # which doesn't work with win32 python gmtime, we always
 
117
    # give the epoch in utc
 
118
    if secs == 0:
 
119
        offset = 0
 
120
    if secs + offset < 0:
 
121
        from warnings import warn
 
122
        warn("gmtime of negative time (%s, %s) may not work on Windows" %
 
123
                (secs, offset))
 
124
    return osutils.format_date(secs, offset=offset,
 
125
            date_fmt='%Y-%m-%d %H:%M:%S')
 
126
 
 
127
 
 
128
# Format for patch dates: %Y-%m-%d %H:%M:%S [+-]%H%M
 
129
# Groups: 1 = %Y-%m-%d %H:%M:%S; 2 = [+-]%H; 3 = %M
 
130
RE_PATCHDATE = re.compile("(\d+-\d+-\d+\s+\d+:\d+:\d+)\s*([+-]\d\d)(\d\d)$")
 
131
RE_PATCHDATE_NOOFFSET = re.compile("\d+-\d+-\d+\s+\d+:\d+:\d+$")
 
132
 
 
133
def parse_patch_date(date_str):
 
134
    """Parse a patch-style date into a POSIX timestamp and offset.
 
135
 
 
136
    Inverse of format_patch_date.
 
137
    """
 
138
    match = RE_PATCHDATE.match(date_str)
 
139
    if match is None:
 
140
        if RE_PATCHDATE_NOOFFSET.match(date_str) is not None:
 
141
            raise ValueError("time data %r is missing a timezone offset"
 
142
                % date_str)
 
143
        else:
 
144
            raise ValueError("time data %r does not match format " % date_str
 
145
                + "'%Y-%m-%d %H:%M:%S %z'")
 
146
    secs_str = match.group(1)
 
147
    offset_hours, offset_mins = int(match.group(2)), int(match.group(3))
 
148
    if abs(offset_hours) >= 24 or offset_mins >= 60:
 
149
        raise ValueError("invalid timezone %r" %
 
150
            (match.group(2) + match.group(3)))
 
151
    offset = offset_hours * 3600 + offset_mins * 60
 
152
    tm_time = time.strptime(secs_str, '%Y-%m-%d %H:%M:%S')
 
153
    # adjust seconds according to offset before converting to POSIX
 
154
    # timestamp, to avoid edge problems
 
155
    tm_time = tm_time[:5] + (tm_time[5] - offset,) + tm_time[6:]
 
156
    secs = calendar.timegm(tm_time)
 
157
    return secs, offset