Commit b4f0083d authored by Robert Sebille's avatar Robert Sebille
Browse files

Initial commit

parents
<?xml version="1.0" encoding="UTF-8"?>
<!-- Komodo Project File - DO NOT EDIT -->
<project id="23aed076-9535-43af-b9cf-309ae21299dc" kpf_version="5" name="AaaaHorodatage.komodoproject">
<preference-set idref="23aed076-9535-43af-b9cf-309ae21299dc" id="project" preftype="project">
<long id="prefs_version">1</long>
</preference-set>
</project>
List errors
AaaaHorodatage
2 : utc_seconds not in range -43200 >= utc_seconds <= +50400 in _convert_utc_seconds2string
3 : range error for year, month, day, hour, minute or second input in encode
AaaaHorodatageTools
51 : offset UTC timestamp 1 does not match offset UTC timestamp 2 in diffTs
others
This diff is collapsed.
This program is under GNU General Public License, version 3, see LICENCE text file.
This program needs python 3
+ modules datetime, aaaa.ahordat, aaaa.ahordattools
Modules:
from datetime import *
import aaaa.ahordat
import aaaa.ahordattools
Help:
In a python3 shell open in the same directory as this README file
help(aaaa.ahordat)
help(aaaa.ahordattools)
Tests:
in the same directory as this README file
python3 main.py
#!/usr/bin/env python3
# -*-coding:Utf-8 -*
"""Encoding and decoding module for timestamps of AaaaHorodatage class.
Timestamp format = SAAAAZ+/-HH:MM where S = the number of seconds
from the epoch until the date entered, AAAA = the year of the timestamp,
and Z+/-HH:MM the UTC offset (e.g for CEST, summertime: Z+02:00).
The timezone is embedded therefore.
Class attribute(s)
- epoch: class reference year of the timestamp.
Range: 0 <= epoch <= year <= 10000.
Several ranges and keyboard input are managed, but not exceptions.
"""
#########################################
# 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
from datetime import *
class AaaaHorodatage:
# année de référence (attribut de classe)
epoch = 1900
def __init__(self):
# AaaaHorodatage.epoch
self.utc_server_offset =\
math.ceil(datetime.timestamp(datetime.now()) -
datetime.timestamp(datetime.utcnow()))
# self.utc_server_offset = 8100
# private
# Liste nombre de jours par mois, moins décembre et année non bissextile
self._ljm = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
# protected #
# attributs
def _get_ljm(self, i):
return self._ljm[i]
ljm = property(_get_ljm)
# méthodes
def _utc_seconds2string(self, utc_seconds=0):
if -43200 >= utc_seconds <= +50400:
print("Fatal error: utc_seconds not in range -43200 >= utc_seconds\
<= +50400 in _utc_seconds2string")
sys.exit(2)
chaineoffset = "+"
if utc_seconds < 0:
chaineoffset = "-"
abs_utc_seconds = math.fabs(utc_seconds)
heures = int(abs_utc_seconds / (60 * 60))
minutes = int((abs_utc_seconds % 3600) / 60)
if heures < 10:
chaineoffset += "0"
chaineoffset += str(heures) + ":"
if minutes < 10:
chaineoffset += "0"
chaineoffset += str(minutes)
return chaineoffset
def _utc_string2seconds(self, utc_string="+00:00"):
tmp = utc_string.split(":")
retour = int(tmp[0]) * 3600
if retour > 0:
retour += int(tmp[1]) * 60
else:
retour -= int(tmp[1]) * 60
return retour
def _bissextile(self, an=0):
b = 0
if an % 400 == 0 or (an % 4 == 0 and not(an % 100 == 0)):
b = 1
return b
def _input_entier(self, annonce=""):
"""Vérifie si une entrée est entière"""
self.ok = False
while not self.ok:
try:
self.entier = int(input(annonce))
self.ok = True
return self.entier
except ValueError:
print("I want an integer, please: ", end="")
self.ok = False
def _calcul_encode(self, utc):
"""Encode the timestamp."""
# ts = horodatage
self.ts = self.secondes
self.ts += self.minutes * 60
self.ts += self.heures * 60 * 60
# calcul du nombre de secondes de l'année en cours et ajout à ts
self.i = 0
self.tmp = 0
while self.i < (self.mois - 1):
self.tmp += self._get_ljm(self.i)
if self.i == 1:
self.tmp += self._bissextile(self.annee)
# print (self.i, " ljm ", self._get_ljm(self.i), " tmp ", self.tmp)
self.i += 1
self.tmp += self.jour - 1
# print (" tmp ", self.tmp)
self.ts += self.tmp * 24 * 60 * 60
# calcul des secondes depuis epoch et ajout à ts
self.i = self.get_epoch()
self.tmp = 0
while self.i < (self.annee):
self.tmp += 365 + self._bissextile(self.i)
# print (self.i, " tmp ", self.tmp, " j ", (365 + self.bissextile(i)))
self.i += 1
self.ts += self.tmp * 24 * 60 * 60
# On sauve l'année dans les 4 derniers chiffres du ts
self.ts = (self.ts * 10000) + self.annee
# On ajoute l'offset UTC (local ou Z) sous forme de chaine
self.ts = str(self.ts) + "Z" + self.get_utc_server_offset("string")
if self.utc:
self.ts = self.ts_local2utc(self.ts)
# print (annee, " bissextile = ", self.bis)
# print (self.ts, " est l'horodatage pour le ", self.jour, "/", \
# self.mois, "/", self.annee, \
# " à ", self.heures, ":", self.minutes, ":", self.secondes, sep = "")
return self.ts
def _calcul_decode(self, typ):
"""Decode the timestamp."""
# Largage du timestamp
self.utc = self.ts.split("Z")
self.ts = int(self.utc[0])
self.utc = str(self.utc[1])
# Récupération de l'année
self.annee = self.ts % 10000
# Récupération du ts en seconde
self.ts = int(self.ts / 10000)
# On retire les secondes de epoch à annee - 1
# (on en n'a pas besoin pour calculer mois et jour)
self.i = self.get_epoch()
self.tmp = 0
while self.i < (self.annee):
self.tmp += 365 + self._bissextile(self.i)
self.i += 1
self.ts -= self.tmp * 24 * 60 * 60
# On retrouve secondes, minutes et heures
self.secondes = self.ts % 60
self.minutes = int((self.ts / 60) % 60)
self.heures = int((self.ts / (60 * 60)) % 24)
# on a retiré les secondes des années précédentes depuis epoch,
# donc on récupère le n° du jour dans l'année (-1)
self.jour = int((self.ts / (24 * 60 * 60)))
# on sépare mois et jour
self.i = 0
self.tmp = self._get_ljm(0)
self.mois = 1
while self.jour >= self.tmp:
self.jour -= self.tmp
self.i += 1
self.tmp = self._get_ljm(self.i)
if self.i == 1:
self.tmp += self._bissextile(self.annee)
self.mois += 1
# print("i " + str(self.i) + " j " + str(self.jour) +\
# " t " + str(self.tmp))
self.jour += 1
# on gère le type de retour demandé
if typ == "string":
if self.mois < 10:
str_mois = "0" + str(self.mois)
else:
str_mois = str(self.mois)
if self.jour < 10:
str_jour = "0" + str(self.jour)
else:
str_jour = str(self.jour)
if self.heures < 10:
str_heures = "0" + str(self.heures)
else:
str_heures = str(self.heures)
if self.minutes < 10:
str_minutes = "0" + str(self.minutes)
else:
str_minutes = str(self.minutes)
if self.secondes < 10:
str_secondes = "0" + str(self.secondes)
else:
str_secondes = str(self.secondes)
retour = str(self.annee) + "-" + str_mois + "-" + str_jour + "T" +\
str_heures + ":" + str_minutes + ":" +\
str_secondes + self.utc
if typ == "list":
retour = list()
retour.append(self.annee)
retour.append(self.mois)
retour.append(self.jour)
retour.append(self.heures)
retour.append(self.minutes)
retour.append(self.secondes)
retour.append(self.utc)
if typ == "dict" or typ == "dictionary":
retour = dict()
retour["year"] = self.annee
retour["month"] = self.mois
retour["day"] = self.jour
retour["hour"] = self.heures
retour["minutes"] = self.minutes
retour["seconds"] = self.secondes
retour["UTC"] = self.utc
return retour
# méthodes publiques #
# classe (cls)
def get_epoch(cls):
"""Return the current epoch of the AaaaHorodatage class."""
return cls.epoch
get_epoch = classmethod(get_epoch)
def set_epoch(cls, num):
"""Set the epoch of the AaaaHorodatage class.
Range: 0 <= epoch <= 9999.
Please, be consistent with yourself! e.g., you must encode
and decode the same timestamp with the same epoch;
or the results will be incoherent. It's why this method is a
class method.
"""
if num >= 0 and num < 10000:
cls.epoch = int(num)
else:
print("Error: ", cls.__class__.__name__, ".set_epoch(0 ... 9999)\
= ", str(num), ". ", cls.__class__.__name__,
".epoch now set to ", cls.epoch,
sep="")
set_epoch = classmethod(set_epoch)
# instance (self)
def encode(self, d="", utc=False):
"""Request input of seconds, minutes, hour, year, month
and day and return an AaaaHorodatage class timestamp.
d =
- "" or none: keyboard input.
- list or tuple input, (fixed order): year, month, day,
hour, minutes, seconds.
- dict (any order, e.g.):
seconds: minutes: hour: year: month: day:
Ranges: seconds, minutes: 0-59. hour: 0-23.
year: epoch <= year <= 10000. month: 1-12.
day: 1-28/29/30/31, exact for the month.
utc: True, UTC timezone (default) | False, local timezone
"""
self.annee = -1
self.mois = -1
self.jour = -1
self.heures = -1
self.minutes = -1
self.secondes = -1
self.utc = utc
# list or tupples
if isinstance(d, list) or isinstance(d, tuple):
self.annee = d[0]
self.mois = d[1]
self.jour = d[2]
self.heures = d[3]
self.minutes = d[4]
self.secondes = d[5]
# dict
if isinstance(d, dict):
self.annee = d["year"]
self.mois = d["month"]
self.jour = d["day"]
self.heures = d["hour"]
self.minutes = d["minutes"]
self.secondes = d["seconds"]
# on vérifie les plages des entrées listes, tuples, dictionnaires
if isinstance(d, list) or isinstance(d, tuple) or isinstance(d, dict):
self.erreur = ""
if self.annee < self.get_epoch() or self.annee > 9999:
self.erreur = "Fatal error, year = " + str(self.annee) + \
". It must be [" + \
str(self.get_epoch()) +\
"-9999, integer] in encode."
if self.mois < 1 or self.mois > 12:
self.erreur = "Fatal error, month = " + str(self.mois) + \
". It must be [1-12, integer] in encode."
self.nb_jours = self._get_ljm(self.mois - 1)
if self.mois == 2:
self.nb_jours += self._bissextile(self.annee)
if self.jour < 1 or self.jour > self.nb_jours:
self.erreur = "Fatal error, day = " + str(self.jour) + \
". For " + str(self.mois) + "/" + \
str(self.annee) + \
", day must be [1-" + \
str(self.nb_jours) + ", integer] in encode."
if self.heures < 0 or self.heures > 23:
self.erreur = "Fatal error, hour = " + str(self.heures) + \
". It must be between [0-23, integer] in encode."
if self.minutes < 0 or self.minutes > 59:
self.erreur = "Fatal error, minutes = " + str(self.minutes) + \
". It must be between [0-59, integer] in encode."
if self.secondes < 0 or self.secondes > 59:
self.erreur = "Fatal error, seconds = " + str(self.secondes) + \
". It must be between [0-59, integer] in encode."
if self.erreur != "":
print(self.erreur)
sys.exit(3)
# keyboard
if d == "":
print("Coding a timestamp from the keyboard:")
self.annonce = "Year [" + str(self.get_epoch()) +\
"-9999, integer]: "
while self.annee < self.get_epoch() or self.annee > 9999:
print(self.annonce, end="")
self.annee = self._input_entier()
# self.annee = int(input(self.annonce))
while (self.mois < 1 or self.mois > 12):
print("Month [1-12, integer]: ", end="")
self.mois = self._input_entier()
self.nb_jours = self._get_ljm(self.mois - 1)
if self.mois == 2:
self.nb_jours += self._bissextile(self.annee)
while self.jour < 1 or self.jour > self.nb_jours:
print("Day [1-" + str(self.nb_jours) + ", integer]: ", end="")
self.jour = self._input_entier()
while self.heures < 0 or self.heures > 23:
print("Hour [0-23, integer]: ", end="")
self.heures = self._input_entier()
while self.minutes < 0 or self.minutes > 59:
print("Minutes [0-59, integer]: ", end="")
self.minutes = self._input_entier()
while self.secondes < 0 or self.secondes > 59:
print("Seconds [0-59, integer]: ", end="")
self.secondes = self._input_entier()
return self._calcul_encode(utc)
def decode(self, typ="string", t=""):
"""Request an AaaaHorodatage class timestamp input and
return year, month, day, hour, minutes, seconds and
offset UTC.
Return typ =
- "string": return a string (YYYY-MM-DDTHH:MM:SS+HH:MM). Default,
compliant ISO 8601.
- "list": return a list (fixed order).
fixed order: year, month and day, hour, minutes, seconds, UTC.
- "dict" or "dictionary": return a dict (any order).
t = timestamp; none or "" = keyboard input.
"""
self.ts = t
# if keyboard
if t == "":
self.ts = input("Decode timestamp : ")
# retour
return self._calcul_decode(typ)
def utc_seconds2string(self, utc_seconds=0):
"""Return a string, timezone offset in the form +/-HH:MM
utc_seconds = number of seconds, integer.
-43200 >= utc_seconds <= +50400"""
return self._utc_seconds2string(utc_seconds)
def utc_string2seconds(self, utc_string="+00:00"):
"""Return a number of seconds (integer)
utc_string: timezone offset in the form +/-HH:MM"""
return self._utc_string2seconds(utc_string)
def get_utc_server_offset(self, typ=""):
"""Get the offset UTC for the server time.
Return:
- Number of seconds (default)
- String (+/-HH:MM) if typ = "string"
"""
retour = self.utc_server_offset
if typ == "string":
retour = self._utc_seconds2string(self.utc_server_offset)
return retour
def ts_local2utc(self, ts):
"""Convert a local Aaaa timestamp to an UTC."""
ts_tmp_list = self.decode("list", ts)
# ts_tmp_list[3] = heure, ts_tmp_list[4] = minutes
tmp = ts.split("Z")
ts_tmp = int(tmp[0])
an_tmp = int(ts_tmp % 10000)
ts_tmp = int(ts_tmp / 10000)
utc_tmp = self._utc_string2seconds(tmp[1])
# test pour 31/12 ou 1/1
ts_secondes_extremes = (ts_tmp_list[3] * 3600) + (ts_tmp_list[3] * 60)
if utc_tmp < 0 and (ts_secondes_extremes + int(math.fabs(utc_tmp))) >\
86400:
an_tmp += 1
if utc_tmp > 0 and (ts_secondes_extremes - int(math.fabs(utc_tmp))) < 0:
an_tmp -= 1
ts_tmp -= utc_tmp
return str(ts_tmp) + str(an_tmp) + "Z+00:00"
if __name__ == "__main__":
# Pour tester le module ...
s = 12
m = 2
h = 12
A = 2000
M = 2
J = 29
epo = 1800
print("(NB: If you enter " + str(J) + "/" + str(M) + "/" + str(A) + " " +
str(h) + ":" + str(m) + ":" + str(s) +
", you will obtain 4 same results)")
print("Keyboard, tuple, list end dict")
AaaaHorodatage.set_epoch(epo)
print(AaaaHorodatage.get_epoch())
k = AaaaHorodatage()
print("\tUTC offset (string): " + k.get_utc_server_offset("string"))
my_k = k.encode()
print("horodatage keyboard: ", my_k)
t = AaaaHorodatage()
print("horodatage tuple : ", t.encode((A, M, J, h, m, s)))
l = AaaaHorodatage()
print("horodatage list : ", l.encode([A, M, J, h, m, s]))
dic0 = {"seconds": s, "minutes": m, "hour": h, "year": A, "month": M,
"day": J}
d = AaaaHorodatage()
print("horodatage dict : ", d.encode(dic0))
print("1: ", k.decode())
print("2: ", k.decode("dictionary"))
print("3: ", l.decode("dictionary", my_k))
#!/usr/bin/env python3
# -*-coding:Utf-8 -*
"""Tools for timestamps of AaaaHorodatage class, Child class of
AaaaHorodatage.
Timestamp format = SAAAAZ+/-HH:MM where S = the number of seconds
from the epoch until the date entered, AAAA = the year of the timestamp,
and Z+/-HH:MM the UTC offset (e.g for CEST, summertime: Z+02:00)
The timezone is embedded therefore.
Class attribute(s)
- epoch: class reference year of the timestamp.
Range: 0 <= epoch <= year <= 10000.
Several ranges and keyboard input are managed, but not exceptions.
"""
#########################################
# 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
# 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