3 from datetime
import datetime
, timedelta
, tzinfo
5 from stgit
.exception
import StgException
6 from stgit
.lib
.git
.base
import Immutable
7 from stgit
.run
import Run
, RunException
10 class DateException(StgException
):
11 """Exception raised when a date+time string could not be parsed."""
13 def __init__(self
, string
, type):
14 super().__init
__('"%s" is not a valid %s' % (string
, type))
17 class TimeZone(tzinfo
):
18 """A simple time zone class for static offsets from UTC. (We have to
19 define our own since Python's standard library doesn't define any
20 time zone classes.)"""
22 def __init__(self
, tzstring
):
23 m
= re
.match(r
'^([+-])(\d{2}):?(\d{2})$', tzstring
)
25 raise DateException(tzstring
, 'time zone')
26 sign
= int(m
.group(1) + '1')
28 self
._offset
= timedelta(
29 hours
=sign
* int(m
.group(2)), minutes
=sign
* int(m
.group(3))
32 raise DateException(tzstring
, 'time zone')
35 def utcoffset(self
, dt
):
48 def system_date(datestring
):
49 m
= re
.match(r
"^(.+)([+-]\d\d:?\d\d)$", datestring
)
51 # Time zone included; we parse it ourselves, since "date"
52 # would convert it to the local time zone.
55 t
= Run("date", "+%Y-%m-%d-%H-%M-%S", "-d", ds
).output_one_line()
59 # Time zone not included; we ask "date" to provide it for us.
61 d
= Run("date", "+%Y-%m-%d-%H-%M-%S_%z", "-d", datestring
).output_one_line()
65 year
, month
, day
, hour
, minute
, second
= [int(x
) for x
in t
.split("-")]
67 return datetime(year
, month
, day
, hour
, minute
, second
, tzinfo
=TimeZone(z
))
69 raise DateException(datestring
, "date")
72 def git_date(datestring
=''):
75 Run('git', 'var', 'GIT_AUTHOR_IDENT')
78 'GIT_AUTHOR_NAME': 'XXX',
79 'GIT_AUTHOR_EMAIL': 'XXX',
80 'GIT_AUTHOR_DATE': datestring
,
87 _
, _
, timestamp
, offset
= ident
.split()
88 return datetime
.fromtimestamp(int(timestamp
), TimeZone(offset
))
91 class Date(Immutable
):
92 """Represents a timestamp used in git commits."""
94 def __init__(self
, datestring
):
95 # Try git-formatted date.
96 m
= re
.match(r
'^(\d+)\s+([+-]\d\d:?\d\d)$', datestring
)
99 self
._time
= datetime
.fromtimestamp(
100 int(m
.group(1)), TimeZone(m
.group(2))
103 raise DateException(datestring
, 'date')
106 # Try iso-formatted date.
108 r
'^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s+'
109 r
'([+-]\d\d:?\d\d)$',
114 self
._time
= datetime(
115 *[int(m
.group(i
+ 1)) for i
in range(6)],
116 **{'tzinfo': TimeZone(m
.group(7))},
119 raise DateException(datestring
, 'date')
122 if datestring
== 'now':
123 self
._time
= git_date()
127 # Try parsing with `git var`.
128 gd
= git_date(datestring
)
133 # Try parsing with the system's "date" command.
134 sd
= system_date(datestring
)
139 raise DateException(datestring
, 'date')
142 return self
.isoformat()
145 """Human-friendly ISO 8601 format."""
147 self
._time
.replace(tzinfo
=None).isoformat(str(' ')),
151 def rfc2822_format(self
):
152 """Format date in RFC-2822 format, as used in email."""
153 return email
.utils
.format_datetime(self
._time
)
156 def maybe(cls
, datestring
):
157 """Return a new object initialized with the argument if it contains a
158 value (otherwise, just return the argument)."""
159 return cls(datestring
) if datestring
is not None else None