]> code.communitydata.science - rises_declines_wikia_code.git/blob - mediawiki_dump_tools/Mediawiki-Utilities/mw/types/timestamp.py
Initial commit
[rises_declines_wikia_code.git] / mediawiki_dump_tools / Mediawiki-Utilities / mw / types / timestamp.py
1 import calendar
2 import datetime
3 import time
4
5 from . import serializable
6
7 LONG_MW_TIME_STRING = '%Y-%m-%dT%H:%M:%SZ'
8 """
9 The longhand version of MediaWiki time strings.
10 """
11
12 SHORT_MW_TIME_STRING = '%Y%m%d%H%M%S'
13 """
14 The shorthand version of MediaWiki time strings.
15 """
16
17
18 class Timestamp(serializable.Type):
19     """
20     An immutable type for working with MediaWiki timestamps in their various
21     forms.
22
23     :Parameters:
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.
26
27     :Returns:
28         :class:`mw.Timestamp`
29
30     You can make use of a lot of different *time things* to initialize a
31     :class:`mw.Timestamp`.
32
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.
37
38     For example::
39
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")
45         True
46         >>> Timestamp(1234567890) == Timestamp("20090213233130")
47         True
48         >>> Timestamp(1234567890) == Timestamp(datetime.datetime.utcfromtimestamp(1234567890))
49         True
50         >>> Timestamp(1234567890) == Timestamp(time.strptime("2009-02-13T23:31:30Z", "%Y-%m-%dT%H:%M:%SZ"))
51         True
52         >>> Timestamp(1234567890) == Timestamp(Timestamp(1234567890))
53         True
54
55
56     You can also do math and comparisons of timestamps.::
57
58         >>> from mw import Timestamp
59         >>> t = Timestamp(1234567890)
60         >>> t
61         Timestamp('2009-02-13T23:31:30Z')
62         >>> t2 = t + 10
63         >>> t2
64         Timestamp('2009-02-13T23:31:40Z')
65         >>> t += 1
66         >>> t
67         Timestamp('2009-02-13T23:31:31Z')
68         >>> t2 - t
69         9
70         >>> t < t2
71         True
72
73
74     """
75
76     def __new__(cls, time_thing):
77         if isinstance(time_thing, cls):
78             return time_thing
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)
85         else:
86             return cls.from_string(time_thing)
87
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.
91         pass
92
93     def initialize(self, time_struct):
94         self.__time = time_struct
95
96     def short_format(self):
97         """
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')``.
101
102         :Parameters:
103             format : str
104                 The string format
105
106         :Returns:
107             A formatted string
108         """
109         return self.strftime(SHORT_MW_TIME_STRING)
110
111     def long_format(self):
112         """
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')``.
116
117         :Parameters:
118             format : str
119                 The string format
120
121         :Returns:
122             A formatted string
123         """
124         return self.strftime(LONG_MW_TIME_STRING)
125
126     def strftime(self, format):
127         """
128         Constructs a formatted string.
129         See `<https://docs.python.org/3/library/time.html#time.strftime>`_ for a
130         discussion of formats descriptors.
131
132         :Parameters:
133             format : str
134                 The format description
135
136         :Returns:
137             A formatted string
138         """
139         return time.strftime(format, self.__time)
140
141     @classmethod
142     def strptime(cls, string, format):
143         """
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.
147
148         :Parameters:
149             string : str
150                 A formatted timestamp
151             format : str
152                 The format description
153
154         :Returns:
155             :class:`mw.Timestamp`
156         """
157         return cls.from_time_struct(time.strptime(string, format))
158
159     @classmethod
160     def from_time_struct(cls, time_struct):
161         """
162         Constructs a :class:`mw.Timestamp` from a :class:`time.time_struct`.
163
164         :Parameters:
165             time_struct : :class:`time.time_struct`
166                 A time structure
167
168         :Returns:
169             :class:`mw.Timestamp`
170         """
171         instance = super().__new__(cls)
172         instance.initialize(time_struct)
173         return instance
174
175     @classmethod
176     def from_datetime(cls, dt):
177         """
178         Constructs a :class:`mw.Timestamp` from a :class:`datetime.datetime`.
179
180         :Parameters:
181             dt : :class:`datetime.datetime``
182                 A datetime.
183
184         :Returns:
185             :class:`mw.Timestamp`
186         """
187         time_struct = dt.timetuple()
188         return cls.from_time_struct(time_struct)
189
190     @classmethod
191     def from_unix(cls, seconds):
192         """
193         Constructs a :class:`mw.Timestamp` from a unix timestamp (in seconds
194         since Jan. 1st, 1970 UTC).
195
196         :Parameters:
197             seconds : int
198                 A unix timestamp
199
200         :Returns:
201             :class:`mw.Timestamp`
202         """
203         time_struct = datetime.datetime.utcfromtimestamp(seconds).timetuple()
204         return cls.from_time_struct(time_struct)
205
206     @classmethod
207     def from_string(cls, string):
208         """
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``.
213
214         :Parameters:
215             string : str
216                 A formatted timestamp
217
218         :Returns:
219             :class:`mw.Timestamp`
220         """
221         if type(string) == bytes:
222             string = str(string, 'utf8')
223         else:
224             string = str(string)
225
226         try:
227             return cls.strptime(string, SHORT_MW_TIME_STRING)
228         except ValueError as e:
229             try:
230                 return cls.strptime(string, LONG_MW_TIME_STRING)
231             except ValueError as e:
232                 raise ValueError(
233                     "{0} is not a valid Wikipedia date format".format(
234                         repr(string)
235                     )
236                 )
237
238         return cls.from_time_struct(time_struct)
239
240     def __format__(self, format):
241         return self.strftime(format)
242
243     def __str__(self):
244         return self.short_format()
245
246     def serialize(self):
247         return self.unix()
248
249     @classmethod
250     def deserialize(cls, time_thing):
251         return Timestamp(time_thing)
252
253     def __repr__(self):
254         return "{0}({1})".format(
255             self.__class__.__name__,
256             repr(self.long_format())
257         )
258
259     def __int__(self):
260         return self.unix()
261
262     def __float__(self):
263         return float(self.unix())
264
265     def unix(self):
266         """
267         :Returns:
268             the number of seconds since Jan. 1st, 1970 UTC.
269         """
270         return int(calendar.timegm(self.__time))
271
272     def __sub__(self, other):
273         if isinstance(other, Timestamp):
274             return self.unix() - other.unix()
275         else:
276             return self + (other * -1)
277
278     def __add__(self, seconds):
279         return Timestamp(self.unix() + seconds)
280
281     def __eq__(self, other):
282         try:
283             return self.__time == other.__time
284         except AttributeError:
285             return False
286
287     def __lt__(self, other):
288         try:
289             return self.__time < other.__time
290         except AttributeError:
291             return NotImplemented
292
293     def __gt__(self, other):
294         try:
295             return self.__time > other.__time
296         except AttributeError:
297             return NotImplemented
298
299     def __le__(self, other):
300         try:
301             return self.__time <= other.__time
302         except AttributeError:
303             return NotImplemented
304
305     def __ge__(self, other):
306         try:
307             return self.__time >= other.__time
308         except AttributeError:
309             return NotImplemented
310
311     def __ne__(self, other):
312         try:
313             return not self.__time == other.__time
314         except AttributeError:
315             return NotImplemented
316         
317     def __getnewargs__(self):
318         return (self.__time,)

Community Data Science Collective || Want to submit a patch?