File: //proc/self/root/lib64/python3.9/site-packages/borg/helpers/time.py
import os
import time
from datetime import datetime, timezone
from ..constants import ISO_FORMAT, ISO_FORMAT_NO_USECS
def to_localtime(ts):
"""Convert datetime object from UTC to local time zone"""
return datetime(*time.localtime((ts - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds())[:6])
def utcnow():
"""Returns a naive datetime instance representing the time in the UTC timezone"""
return datetime.now(timezone.utc).replace(tzinfo=None)
def parse_timestamp(timestamp, tzinfo=timezone.utc):
"""Parse a ISO 8601 timestamp string"""
fmt = ISO_FORMAT if '.' in timestamp else ISO_FORMAT_NO_USECS
dt = datetime.strptime(timestamp, fmt)
if tzinfo is not None:
dt = dt.replace(tzinfo=tzinfo)
return dt
def timestamp(s):
"""Convert a --timestamp=s argument to a datetime object"""
try:
# is it pointing to a file / directory?
ts = safe_s(os.stat(s).st_mtime)
return datetime.fromtimestamp(ts, tz=timezone.utc)
except OSError:
# didn't work, try parsing as timestamp. UTC, no TZ, no microsecs support.
for format in ('%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S+00:00',
'%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S',
'%Y-%m-%dT%H:%M', '%Y-%m-%d %H:%M',
'%Y-%m-%d', '%Y-%j',
):
try:
return datetime.strptime(s, format).replace(tzinfo=timezone.utc)
except ValueError:
continue
raise ValueError
# Not too rarely, we get crappy timestamps from the fs, that overflow some computations.
# As they are crap anyway (valid filesystem timestamps always refer to the past up to
# the present, but never to the future), nothing is lost if we just clamp them to the
# maximum value we can support.
# As long as people are using borg on 32bit platforms to access borg archives, we must
# keep this value True. But we can expect that we can stop supporting 32bit platforms
# well before coming close to the year 2038, so this will never be a practical problem.
SUPPORT_32BIT_PLATFORMS = True # set this to False before y2038.
if SUPPORT_32BIT_PLATFORMS:
# second timestamps will fit into a signed int32 (platform time_t limit).
# nanosecond timestamps thus will naturally fit into a signed int64.
# subtract last 48h to avoid any issues that could be caused by tz calculations.
# this is in the year 2038, so it is also less than y9999 (which is a datetime internal limit).
# msgpack can pack up to uint64.
MAX_S = 2**31-1 - 48*3600
MAX_NS = MAX_S * 1000000000
else:
# nanosecond timestamps will fit into a signed int64.
# subtract last 48h to avoid any issues that could be caused by tz calculations.
# this is in the year 2262, so it is also less than y9999 (which is a datetime internal limit).
# round down to 1e9 multiple, so MAX_NS corresponds precisely to a integer MAX_S.
# msgpack can pack up to uint64.
MAX_NS = (2**63-1 - 48*3600*1000000000) // 1000000000 * 1000000000
MAX_S = MAX_NS // 1000000000
def safe_s(ts):
if 0 <= ts <= MAX_S:
return ts
elif ts < 0:
return 0
else:
return MAX_S
def safe_ns(ts):
if 0 <= ts <= MAX_NS:
return ts
elif ts < 0:
return 0
else:
return MAX_NS
def safe_timestamp(item_timestamp_ns):
t_ns = safe_ns(item_timestamp_ns)
return datetime.fromtimestamp(t_ns / 1e9)
def format_time(ts: datetime, format_spec=''):
"""
Convert *ts* to a human-friendly format with textual weekday.
"""
return ts.strftime('%a, %Y-%m-%d %H:%M:%S' if format_spec == '' else format_spec)
def isoformat_time(ts: datetime):
"""
Format *ts* according to ISO 8601.
"""
# note: first make all datetime objects tz aware before adding %z here.
return ts.strftime(ISO_FORMAT)
def format_timedelta(td):
"""Format timedelta in a human friendly format
"""
ts = td.total_seconds()
s = ts % 60
m = int(ts / 60) % 60
h = int(ts / 3600) % 24
txt = '%.2f seconds' % s
if m:
txt = '%d minutes %s' % (m, txt)
if h:
txt = '%d hours %s' % (h, txt)
if td.days:
txt = '%d days %s' % (td.days, txt)
return txt
class OutputTimestamp:
def __init__(self, ts: datetime):
if ts.tzinfo == timezone.utc:
ts = to_localtime(ts)
self.ts = ts
def __format__(self, format_spec):
return format_time(self.ts, format_spec=format_spec)
def __str__(self):
return f'{self}'
def isoformat(self):
return isoformat_time(self.ts)
to_json = isoformat