ahordattools.py 13.8 KB
Newer Older
Robert Sebille's avatar
Robert Sebille committed
1
2
3
4
5
6
#!/usr/bin/env python3
# -*-coding:Utf-8 -*

"""Tools for timestamps of AaaaHorodatage class, Child class of
    AaaaHorodatage.

7
    Timestamp format = [0-9]+AAAAZ+/-HH:MM where [0-9]+ = the number of
8
9
    seconds from the epoch until the date entered, AAAA = the epoch of
    the timestamp, and Z+/-HH:MM the UTC offset (e.g for CEST,
10
    summertime: Z+02:00).  The timezone and epoch are embedded.
Robert Sebille's avatar
Robert Sebille committed
11
12

    Class attribute(s)
13
    - epoch: class reference year of the timestamp (only for encoding).
14
    Range: 1000 <= epoch <= 9999. Default 2000.
Robert Sebille's avatar
Robert Sebille committed
15
16
17

    Several ranges and keyboard input are managed, but not exceptions.

18
19
20
    Help and download:
    https://gitlab.adullact.net/zenjo/AaaaHorodatage/tree/master

Robert Sebille's avatar
Robert Sebille committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"""
#########################################
# GNU General Public License, version 3 #
############################################################################
#                                                                          #
#    This program is free software: you can redistribute it and/or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation, either version 3 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program.  If not, see <http://www.gnu.org/licenses/>. #
#                                                                          #
#    Robert Sebille, Belgium, 2018 -                                       #
#                                                                          #
############################################################################
# As compliant as possible with the PEP8    #
# https://www.python.org/dev/peps/pep-0008/ #
#############################################

import os
import sys
import math
49
import time
Robert Sebille's avatar
Robert Sebille committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# value to test module
# from ahordat import AaaaHorodatage
# value for production
from aaaa.ahordat import AaaaHorodatage


class AaaaHorodatageTools(AaaaHorodatage):

    # For dev start #
    # in AaaaHorodatage:
        # année de référence (attribut de classe)
    #    epoch = 1900
    # private
        # attributs
    #    def _get_ljm(self, i):
    #    ljm = property(_get_ljm)
        # methodes
    #    def _bissextile(self, an = 0):
    #    def _input_entier(self, annonce=""):
    #    def _calcul_encode(self):
    #    def _cal    def _input_entier(self, annonce=""):cul_decode(self, typ):
    # méthodes publiques
        # classe (cls)
    #    def get_epoch(cls):
    #    get_epoch = classmethod(get_epoch)
    #    def set_epoch(cls, num):
    #    set_epoch = classmethod(set_epoch)
        # instance (self)
    #    def encode(self, d=""):
    #    def decode(self, typ="string", t=0):
    # For dev end

Robert Sebille's avatar
Robert Sebille committed
82
83
    def __init__(self, ts=""):
        AaaaHorodatage.__init__(self, ts)
Robert Sebille's avatar
Robert Sebille committed
84
        # protected
85
86
87
        #douglas = open("aaaa/42", "r")
        #self._Aaaa42 = douglas.read()
        #douglas.close()
Robert Sebille's avatar
Robert Sebille committed
88

89
    def get_42(self):
90
91
92
        douglas = open("aaaa/42", "r")
        print(douglas.read())
        douglas.close()
93
94
95
96
97
98
99
100
101
102
103
104
105
106
        print("Non fatal error 42: existential error 101010.")
        print("System not halted. L'histoire")
        print("                              con")
        print("                                 ti")
        print("                                   n")
        print("                                    u")
        print("                                     e")
        print("                                       .")
        print("                                        .")
        print("                                         .\n")
        print("|101010 101010 101010 101010 101010 101010 101010 101010 101010\
 101010 101010 |\n")

    #get_42 = property(_get_42)
Robert Sebille's avatar
Robert Sebille committed
107

108
109
    # protected #
    # attributs
Robert Sebille's avatar
Robert Sebille committed
110

111
    # Special #
112
113
114
115
116
    def copy(self):
        tmp = AaaaHorodatageTools()
        tmp.ts = self.ts
        return tmp

Robert Sebille's avatar
Robert Sebille committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    def __eq__(self, ts_object):
        """== operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix == ts_object_posix

    def __ne__(self, ts_object):
        """!= operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix != ts_object_posix

    def __gt__(self, ts_object):
        """> operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix > ts_object_posix

    def __ge__(self, ts_object):
        """>= operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix >= ts_object_posix

    def __lt__(self, ts_object):
        """< operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix < ts_object_posix

    def __le__(self, ts_object):
        """<= operator"""
        aaaa = self.copy()
        aaaa_posix = aaaa.convert_aaaa2posix()
        ts_object_posix = ts_object.convert_aaaa2posix()
        return aaaa_posix <= ts_object_posix

Robert Sebille's avatar
Robert Sebille committed
159
    def __str__(self):
Robert Sebille's avatar
Robert Sebille committed
160
        """Return the string self.ts"""
Robert Sebille's avatar
Robert Sebille committed
161
162
        return self.get_ts()

163
    def __add__(self, seconds=0):
164
        """new_ts = ts_object + seconds.
165
        Overload of plus operator.
Robert Sebille's avatar
Robert Sebille committed
166

167
        self.ts will be updated
168

169
170
        Tips:
        - use dhms2second(d=0, h=0, m=0, s=0) method to
171
        convert days + hours + minutes + seconds in seconds
172
173
174
175
        - To add seconds to a new timestamp instance:
        new_ts_object = ts_object.copy()
        new_ts_object + seconds

176
177
178
179
180
181
182
        """
        tmp = self.ts.split("Z")
        ts_offset = tmp[1]
        tmp = int(tmp[0])
        ts_epoch = int(tmp % 10000)
        ts_seconds = int(tmp / 10000)
        ts_seconds += seconds
183
        self.ts = str(ts_seconds) + str(ts_epoch) + "Z" + ts_offset
184
        return self
Robert Sebille's avatar
Robert Sebille committed
185

186
187
188
    def __sub__(self, *nb_seconds_or_ts_object):
        """new_ts = ts_object - *nb_seconds_or_ts_object
        Overload of minus operator.
Robert Sebille's avatar
Robert Sebille committed
189

190
191
192
        if *nb_seconds_or_ts_object is ts_object, call
        self.ts.diffDays("string", *nb_seconds_or_ts_object.get_ts()),
        see this method. self.ts is NOT updated.
Robert Sebille's avatar
Robert Sebille committed
193

194
195
196
197
198
199
200
201
202
203
        if *nb_seconds_or_ts_object is a nb_seconds integer
        update and return  self.ts - *nb_seconds
        self.ts seconds - seconds must be >= 0 (epoch-01-01)

        Tips:
        - use dhms2second(d=0, h=0, m=0, s=0) utility method
        to convert days + hours + minutes + seconds in seconds
        - To add seconds to a new timestamp instance:
        new_ts_object = ts_object.copy()
        new_ts_object - seconds
Robert Sebille's avatar
Robert Sebille committed
204
205

        """
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
        if isinstance(nb_seconds_or_ts_object[0], AaaaHorodatageTools):
            return nb_seconds_or_ts_object[0].diffDays("string", self.get_ts())

        if isinstance(nb_seconds_or_ts_object[0], int):
            seconds = nb_seconds_or_ts_object[0]
            tmp = self.ts.split("Z")
            ts_offset = tmp[1]
            tmp = int(tmp[0])
            ts_epoch = int(tmp % 10000)
            ts_seconds = int(tmp / 10000)
            if ts_seconds >= seconds:
                ts_seconds -= seconds
            else:
                print("Fatal error 53: number of seconds to substract ", end="")
                print("to big in __sub__(seconds=0). Substract < ", end="")
                print(ts_epoch, "-01-01 (epoch of the timestamp)",  sep="")
                print("Program halted.")
                sys.exit(53)
            self.ts = str(ts_seconds) + str(ts_epoch) + "Z" + ts_offset
225
            return self
226

Robert Sebille's avatar
Robert Sebille committed
227
228
229
230
231
    # protected #
    # méthodes
    # méthodes publiques #
    # class (cls)
    # instance (self)
232
    def diffDays(self, typ="string", ts2=0):
Robert Sebille's avatar
Robert Sebille committed
233
        """Difference between 2 timestamps in days, hours, minutes, seconds.
234
            ts2 - self.ts1.
Robert Sebille's avatar
Robert Sebille committed
235

236
            Usage: ts1_object.diffDays(<typ>, <ts2>)
237
238
            Offset UTC timestamp 1 must match offset UTC timestamp 2
            and epoch timestamp 1 must match epoch timestamp 2.
Robert Sebille's avatar
Robert Sebille committed
239
240

            Return typ =
241
            - "seconds": return the number of seconds.
Robert Sebille's avatar
Robert Sebille committed
242
243
244
245
246
            - "string": return a string (+/-DDTHH:MM:SS). Default.
            - "list": return a list (fixed order).
            fixed order: sens_difference, day, hour, minutes, seconds.
            - "dict" or "dictionary": return a dict (any order).
            ts2 = 0 or none: keyboard input.
247
            ts2 eg: ts2_object.get_ts()
Robert Sebille's avatar
Robert Sebille committed
248
249
250

        """
        # if keyboard
251
252
        if ts2 == 0:
            ts2 = input("Decode timestamp 2: ")
Robert Sebille's avatar
Robert Sebille committed
253
254
255
        # if arg
        # to do

256
        # Récupération des timestamp et test des offset utc
257
        ts1_split = self.ts.split("Z")
258
        ts2_split = ts2.split("Z")
Robert Sebille's avatar
Robert Sebille committed
259
        if ts1_split[1] != ts2_split[1]:
260
261
            print("Fatal error 51: offset UTC1 " + ts1_split[1] +
                  " does not match offset UTC2 " + ts2_split[1] +
Robert Sebille's avatar
Robert Sebille committed
262
263
                  "\nProgram halted.")
            print("Consider to standardize the 2 timestamps to Z (+00:00)")
Robert Sebille's avatar
Robert Sebille committed
264
265
            print(" using <instance>.tz2utc(timestamp1)")
            print(" using <instance>.tz2utc(timestamp2)")
Robert Sebille's avatar
Robert Sebille committed
266
            sys.exit(51)
267
        # recup ts1, ts2
268
269
270
271
272
273
        ts1 = int(ts1_split[0])
        ts2 = int(ts2_split[0])
        epoch1 = ts1 % 10000
        epoch2 = ts2 % 10000
        ts1 = int(ts1 / 10000)
        ts2 = int(ts2 / 10000)
274
275
        # test des epoch
        if epoch1 != epoch2:
276
277
            print("Fatal error 52: epoch1 " + str(epoch1) +
                  " does not match epoch2 " + str(epoch2) +
278
279
280
281
                  "\nProgram halted.")
            print("Consider to standardize the 2 epochs")
            print(" using <instance>.convert2epoch(timestamp1, epoch)")
            print(" using <instance>.convert2epoch(timestamp2, epoch)")
282
            sys.exit(52)
283
        # ts2 > ts1 => sens diff = +, sinon -
284
285
286
287
        sens_diff = "+"
        if ts1 > ts2:
            ts1, ts2 = ts2, ts1
            sens_diff = "-"
288

289
290
291
292
293
        dts = ts2 - ts1
        secondes = dts % 60
        minutes = int((dts / 60) % 60)
        heures = int((dts / (60 * 60)) % 24)
        jours = int(dts / (60 * 60 * 24))
Robert Sebille's avatar
Robert Sebille committed
294

295
296
297
298
299
300
        if typ == "seconds":
            if sens_diff == "-":
                retour = -dts
            else:
                retour = dts

Robert Sebille's avatar
Robert Sebille committed
301
        if typ == "string":
302
303
            retour = (sens_diff + str(jours) + "T" + str(heures) + ":" +
                      str(minutes) + ":" + str(secondes))
Robert Sebille's avatar
Robert Sebille committed
304
        if typ == "list":
305
306
307
308
309
310
            retour = list()
            retour.append(sens_diff)
            retour.append(jours)
            retour.append(heures)
            retour.append(minutes)
            retour.append(secondes)
Robert Sebille's avatar
Robert Sebille committed
311
312

        if typ == "dict" or typ == "dictionary":
313
314
315
316
317
318
319
320
            retour = dict()
            retour["sensdiff"] = sens_diff
            retour["days"] = jours
            retour["hours"] = heures
            retour["minutes"] = minutes
            retour["seconds"] = secondes

        return retour
Robert Sebille's avatar
Robert Sebille committed
321

322
    def convert2epoch(self, epoch):
323
324
325
        """Convert an embedded epoch to an other

        1000 <= epoch <= 9999
Robert Sebille's avatar
Robert Sebille committed
326
        self.ts is updated
327
328

        """
329
330
331
        ts_offset = self.ts.split("Z")
        ts_offset = self.offset_string2seconds(ts_offset[1])
        save_offset = self.get_utc_local_offset()
332
        save_epoch = self.get_epoch()
333
        ts = self.decode("list", self.ts)
334
        self.set_epoch(epoch)
335
        self.set_utc_local_offset(ts_offset)
336
337
        ts = self.encode(ts)
        self.set_epoch(save_epoch)
338
        self.set_utc_local_offset(save_offset)
Robert Sebille's avatar
Robert Sebille committed
339
340
        self.ts = ts
        return self.ts
Robert Sebille's avatar
Robert Sebille committed
341

342
343
344
345
    def dhms2seconds(self, d=0, h=0, m=0, s=0):
        """Convert days + hours + minutes + seconds in seconds"""
        return d * self._get_nb_sec_par_jour() + h * 3600 + m * 60 + s

346
347
    def convert_aaaa2posix(self):
        """Return a Posix timestamp (UTC) from an Aaaa timestamp"""
348
349
        aaaa_ts = self.copy()
        aaaa_ts.convert2epoch(1970)
350
        aaaa_ts.tz2utc()
351
352
        #self.ts = save_local_ts
        tmp = aaaa_ts.get_ts().split("Z")
Robert Sebille's avatar
Robert Sebille committed
353
354
355
356
357
        ts_sec = int(tmp[0])
        ts_sec = int(ts_sec / 10000)

        return ts_sec

358
    def convert_posix2aaaa(self, posix_ts):
Robert Sebille's avatar
Robert Sebille committed
359
        """Return an Aaaa timestamp from a Posix timestamp
360
        self.ts is updated
Robert Sebille's avatar
Robert Sebille committed
361
362
363
364

        posix_ts will be considered at UTC time.

        """
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
        self.ts = str(posix_ts) + "1970Z+00:00"
        return self.ts

    def set_ts_same_epoch_offset(self, ts_object):
        """Convert ts_object to same epoch and UTC offset
        than self.ts

        ts_object.ts is updated
        """
        ts_epoch = self.get_ts_epoch()
        tmp = self.get_ts()
        tmp = tmp.split("Z")
        ts_offset = tmp[1]
        ts_object.tz2utc()
        ts_object.utc2tz(ts_offset)
        ts_object.convert2epoch(ts_epoch)
Robert Sebille's avatar
Robert Sebille committed
381

382
        return ts_object
Robert Sebille's avatar
Robert Sebille committed
383

Robert Sebille's avatar
Robert Sebille committed
384
385
386
if __name__ == "__main__":
    # at top, # from ahordat import AaaaHorodatage # to test module

387
388
389
390
391
392
393
394
    h = AaaaHorodatageTools()
    h.encode((2001, 1, 1, 10, 10, 10))
    print(h.decode())
    h2 = h + h.dhms2seconds(1, 2, 10, 20)
    print(h2.get_ts())
    print(h.decode(), " - ", h2.decode())

    sys.exit()
395
396
397
398
399
400
401
    k = AaaaHorodatageTools()
    print(k.get_utc_local_offset())
    print(k.get_utc_local_offset("string"))
    myKCEST = k.encode([2001, 2, 20, 20, 15, 30])
    print(myKCEST)
    print(k.decode('string', myKCEST), "\n---")
    print(k.convert2epoch(myKCEST, 1900))