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.
20 We have to define our own since Python's standard library doesn't define any time
25 def __init__(self
, tzstring
):
26 m
= re
.match(r
'^([+-])(\d{2}):?(\d{2})$', tzstring
)
28 raise DateException(tzstring
, 'time zone')
29 sign
= int(m
.group(1) + '1')
31 self
._offset
= timedelta(
32 hours
=sign
* int(m
.group(2)), minutes
=sign
* int(m
.group(3))
35 raise DateException(tzstring
, 'time zone')
38 def utcoffset(self
, dt
):
51 def system_date(datestring
):
52 m
= re
.match(r
"^(.+)([+-]\d\d:?\d\d)$", datestring
)
54 # Time zone included; we parse it ourselves, since "date"
55 # would convert it to the local time zone.
58 t
= Run("date", "+%Y-%m-%d-%H-%M-%S", "-d", ds
).output_one_line()
62 # Time zone not included; we ask "date" to provide it for us.
64 d
= Run("date", "+%Y-%m-%d-%H-%M-%S_%z", "-d", datestring
).output_one_line()
68 year
, month
, day
, hour
, minute
, second
= [int(x
) for x
in t
.split("-")]
70 return datetime(year
, month
, day
, hour
, minute
, second
, tzinfo
=TimeZone(z
))
72 raise DateException(datestring
, "date")
75 def git_date(datestring
=''):
78 Run('git', 'var', 'GIT_AUTHOR_IDENT')
81 'GIT_AUTHOR_NAME': 'XXX',
82 'GIT_AUTHOR_EMAIL': 'XXX',
83 'GIT_AUTHOR_DATE': datestring
,
90 _
, _
, timestamp
, offset
= ident
.split()
91 return datetime
.fromtimestamp(int(timestamp
), TimeZone(offset
))
94 class Date(Immutable
):
95 """Represents a timestamp used in Git commits."""
97 def __init__(self
, datestring
):
98 # Try git-formatted date.
99 m
= re
.match(r
'^(\d+)\s+([+-]\d\d:?\d\d)$', datestring
)
102 self
._time
= datetime
.fromtimestamp(
103 int(m
.group(1)), TimeZone(m
.group(2))
106 raise DateException(datestring
, 'date')
109 # Try iso-formatted date.
111 r
'^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s+'
112 r
'([+-]\d\d:?\d\d)$',
117 self
._time
= datetime(
118 *[int(m
.group(i
+ 1)) for i
in range(6)],
119 **{'tzinfo': TimeZone(m
.group(7))},
122 raise DateException(datestring
, 'date')
125 if datestring
== 'now':
126 self
._time
= git_date()
130 # Try parsing with `git var`.
131 gd
= git_date(datestring
)
136 # Try parsing with the system's "date" command.
137 sd
= system_date(datestring
)
142 raise DateException(datestring
, 'date')
145 return self
.isoformat()
148 """Human-friendly ISO 8601 format."""
150 self
._time
.replace(tzinfo
=None).isoformat(str(' ')),
154 def rfc2822_format(self
):
155 """Format date in RFC-2822 format, as used in email."""
156 return email
.utils
.format_datetime(self
._time
)
159 def maybe(cls
, datestring
):
160 """Create new :class:`Date` from non-None argument.
162 Returns None if the argument is None.
165 return cls(datestring
) if datestring
is not None else None