5 from . import serializable
7 LONG_MW_TIME_STRING = '%Y-%m-%dT%H:%M:%SZ'
9 The longhand version of MediaWiki time strings.
12 SHORT_MW_TIME_STRING = '%Y%m%d%H%M%S'
14 The shorthand version of MediaWiki time strings.
18 class Timestamp(serializable.Type):
20 An immutable type for working with MediaWiki timestamps in their various
24 time_thing : :class:`mw.Timestamp` | :py:class:`~time.time_struct` | :py:class:`~datetime.datetime` | :py:class:`str` | :py:class:`int` | :py:class:`float`
25 The timestamp type from which to construct the timestamp class.
30 You can make use of a lot of different *time things* to initialize a
31 :class:`mw.Timestamp`.
33 * If a :py:class:`~time.time_struct` or :py:class:`~datetime.datetime` are provided, a `Timestamp` will be constructed from their values.
34 * If an `int` or `float` are provided, they will be assumed to a unix timestamp in seconds since Jan. 1st, 1970 UTC.
35 * If a `str` is provided, it will be be checked against known MediaWiki timestamp formats. E.g., ``'%Y%m%d%H%M%S'`` and ``'%Y-%m-%dT%H:%M:%SZ'``.
36 * If a :class:`mw.Timestamp` is provided, the same `Timestamp` will be returned.
40 >>> import datetime, time
41 >>> from mw import Timestamp
42 >>> Timestamp(1234567890)
43 Timestamp('2009-02-13T23:31:30Z')
44 >>> Timestamp(1234567890) == Timestamp("2009-02-13T23:31:30Z")
46 >>> Timestamp(1234567890) == Timestamp("20090213233130")
48 >>> Timestamp(1234567890) == Timestamp(datetime.datetime.utcfromtimestamp(1234567890))
50 >>> Timestamp(1234567890) == Timestamp(time.strptime("2009-02-13T23:31:30Z", "%Y-%m-%dT%H:%M:%SZ"))
52 >>> Timestamp(1234567890) == Timestamp(Timestamp(1234567890))
56 You can also do math and comparisons of timestamps.::
58 >>> from mw import Timestamp
59 >>> t = Timestamp(1234567890)
61 Timestamp('2009-02-13T23:31:30Z')
64 Timestamp('2009-02-13T23:31:40Z')
67 Timestamp('2009-02-13T23:31:31Z')
76 def __new__(cls, time_thing):
77 if isinstance(time_thing, cls):
79 elif isinstance(time_thing, time.struct_time):
80 return cls.from_time_struct(time_thing)
81 elif isinstance(time_thing, datetime.datetime):
82 return cls.from_datetime(time_thing)
83 elif type(time_thing) in (int, float):
84 return cls.from_unix(time_thing)
86 return cls.from_string(time_thing)
88 def __init__(self, time_thing):
89 # Important that this does nothing in order to allow __new__ to behave
90 # as expected. User `initialize()` instead.
93 def initialize(self, time_struct):
94 self.__time = time_struct
96 def short_format(self):
98 Constructs a long, ``'%Y%m%d%H%M%S'`` formatted string common to the
99 database. This method is roughly equivalent to calling
100 ``strftime('%Y%m%d%H%M%S')``.
109 return self.strftime(SHORT_MW_TIME_STRING)
111 def long_format(self):
113 Constructs a long, ``'%Y-%m-%dT%H:%M:%SZ'`` formatted string common to the
114 API. This method is roughly equivalent to calling
115 ``strftime('%Y-%m-%dT%H:%M:%SZ')``.
124 return self.strftime(LONG_MW_TIME_STRING)
126 def strftime(self, format):
128 Constructs a formatted string.
129 See `<https://docs.python.org/3/library/time.html#time.strftime>`_ for a
130 discussion of formats descriptors.
134 The format description
139 return time.strftime(format, self.__time)
142 def strptime(cls, string, format):
144 Constructs a :class:`mw.Timestamp` from an explicitly formatted string.
145 See `<https://docs.python.org/3/library/time.html#time.strftime>`_ for a
146 discussion of formats descriptors.
150 A formatted timestamp
152 The format description
155 :class:`mw.Timestamp`
157 return cls.from_time_struct(time.strptime(string, format))
160 def from_time_struct(cls, time_struct):
162 Constructs a :class:`mw.Timestamp` from a :class:`time.time_struct`.
165 time_struct : :class:`time.time_struct`
169 :class:`mw.Timestamp`
171 instance = super().__new__(cls)
172 instance.initialize(time_struct)
176 def from_datetime(cls, dt):
178 Constructs a :class:`mw.Timestamp` from a :class:`datetime.datetime`.
181 dt : :class:`datetime.datetime``
185 :class:`mw.Timestamp`
187 time_struct = dt.timetuple()
188 return cls.from_time_struct(time_struct)
191 def from_unix(cls, seconds):
193 Constructs a :class:`mw.Timestamp` from a unix timestamp (in seconds
194 since Jan. 1st, 1970 UTC).
201 :class:`mw.Timestamp`
203 time_struct = datetime.datetime.utcfromtimestamp(seconds).timetuple()
204 return cls.from_time_struct(time_struct)
207 def from_string(cls, string):
209 Constructs a :class:`mw.Timestamp` from a MediaWiki formatted string.
210 This method is provides a convenient way to construct from common
211 MediaWiki timestamp formats. E.g., ``%Y%m%d%H%M%S`` and
212 ``%Y-%m-%dT%H:%M:%SZ``.
216 A formatted timestamp
219 :class:`mw.Timestamp`
221 if type(string) == bytes:
222 string = str(string, 'utf8')
227 return cls.strptime(string, SHORT_MW_TIME_STRING)
228 except ValueError as e:
230 return cls.strptime(string, LONG_MW_TIME_STRING)
231 except ValueError as e:
233 "{0} is not a valid Wikipedia date format".format(
238 return cls.from_time_struct(time_struct)
240 def __format__(self, format):
241 return self.strftime(format)
244 return self.short_format()
250 def deserialize(cls, time_thing):
251 return Timestamp(time_thing)
254 return "{0}({1})".format(
255 self.__class__.__name__,
256 repr(self.long_format())
263 return float(self.unix())
268 the number of seconds since Jan. 1st, 1970 UTC.
270 return int(calendar.timegm(self.__time))
272 def __sub__(self, other):
273 if isinstance(other, Timestamp):
274 return self.unix() - other.unix()
276 return self + (other * -1)
278 def __add__(self, seconds):
279 return Timestamp(self.unix() + seconds)
281 def __eq__(self, other):
283 return self.__time == other.__time
284 except AttributeError:
287 def __lt__(self, other):
289 return self.__time < other.__time
290 except AttributeError:
291 return NotImplemented
293 def __gt__(self, other):
295 return self.__time > other.__time
296 except AttributeError:
297 return NotImplemented
299 def __le__(self, other):
301 return self.__time <= other.__time
302 except AttributeError:
303 return NotImplemented
305 def __ge__(self, other):
307 return self.__time >= other.__time
308 except AttributeError:
309 return NotImplemented
311 def __ne__(self, other):
313 return not self.__time == other.__time
314 except AttributeError:
315 return NotImplemented
317 def __getnewargs__(self):
318 return (self.__time,)