1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
49
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
82
83
84
85
|
# ==============================================================================
# Copyright (C) 2019 - Philip Paquette
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
# ==============================================================================
""" Time functions
- Contains generic time functions (e.g. to calculate deadlines)
"""
import calendar
import datetime
import math
import pytz
def str_to_seconds(offset_str):
""" Converts a time in format 00W00D00H00M00S in number of seconds
:param offset_str: The string to convert (e.g. '20D')
:return: Its equivalent in seconds = 1728000
"""
mult = {'W': 7 * 24 * 60 * 60, 'D': 24 * 60 * 60, 'H': 60 * 60, 'M': 60, 'S': 1, ' ': 1}
buffer = current_sum = 0
offset_str = str(offset_str)
# Adding digits to buffer, when a character is found,
# multiply it with buffer and increase the current_sum
for char in offset_str:
if char.isdigit():
buffer = buffer * 10 + int(char)
elif char.upper() in mult:
current_sum += buffer * mult[char.upper()]
buffer = 0
else:
buffer = 0
current_sum += buffer
return current_sum
def trunc_time(timestamp, trunc_interval, time_zone='GMT'):
""" Truncates time at a specific interval (e.g. 20M) (i.e. Rounds to the next :20, :40, :60)
:param timestamp: The unix epoch to truncate (e.g. 1498746120)
:param trunc_interval: The truncation interval (e.g. 60*60 or '1H')
:param time_zone: The time to use for conversion (defaults to GMT otherwise)
:return: A timestamp truncated to the nearest (future) interval
"""
midnight_ts = calendar.timegm(datetime.datetime.combine(datetime.date.today(), datetime.time.min).utctimetuple())
midnight_offset = (timestamp - midnight_ts) % (24*3600)
dtime = datetime.datetime.fromtimestamp(timestamp, pytz.timezone(time_zone))
tz_offset = dtime.utcoffset().total_seconds()
interval = str_to_seconds(trunc_interval)
trunc_offset = math.ceil((midnight_offset + tz_offset) / interval) * interval
trunc_ts = timestamp - midnight_offset + trunc_offset - tz_offset
return int(trunc_ts)
def next_time_at(timestamp, time_at, time_zone='GMT'):
""" Returns the next timestamp at a specific 'hh:mm'
:param timestamp: The unix timestamp to convert
:param time_at: The next 'hh:mm' to have the time rounded to, or 0 to skip
:param time_zone: The time to use for conversion (defaults to GMT otherwise)
:return: A timestamp truncated to the nearest (future) hh:mm
"""
if not time_at:
return timestamp
midnight_ts = calendar.timegm(datetime.datetime.combine(datetime.date.today(), datetime.time.min).utctimetuple())
midnight_offset = (timestamp - midnight_ts) % (24*3600)
dtime = datetime.datetime.fromtimestamp(timestamp, pytz.timezone(time_zone))
tz_offset = dtime.utcoffset().total_seconds()
trunc_interval = '%dH%dM' % (int(time_at.split(':')[0]), int(time_at.split(':')[1])) if ':' in time_at else time_at
interval = str_to_seconds(trunc_interval)
at_offset = (-midnight_offset + interval - tz_offset) % (24 * 3600)
at_ts = timestamp + at_offset
return int(at_ts)
|