diff --git a/toot/commands.py b/toot/commands.py index 2b42ea0..f74e3e6 100644 --- a/toot/commands.py +++ b/toot/commands.py @@ -11,8 +11,8 @@ from toot.exceptions import ApiError, ConsoleError from toot.output import (print_lists, print_out, print_instance, print_account, print_acct_list, print_search_results, print_status, print_timeline, print_notifications, print_tag_list, print_list_accounts, print_user_list) -from toot.tui.utils import parse_datetime from toot.utils import args_get_instance, delete_tmp_status_file, editor_input, multiline_input, EOF_KEY +from toot.utils.datetime import parse_datetime def get_timeline_generator(app, user, args): diff --git a/toot/tui/entities.py b/toot/tui/entities.py index a30bcb6..165ca77 100644 --- a/toot/tui/entities.py +++ b/toot/tui/entities.py @@ -1,6 +1,6 @@ from collections import namedtuple -from .utils import parse_datetime +from toot.utils.datetime import parse_datetime Author = namedtuple("Author", ["account", "display_name", "username"]) diff --git a/toot/tui/poll.py b/toot/tui/poll.py index 81756af..7c3afbc 100644 --- a/toot/tui/poll.py +++ b/toot/tui/poll.py @@ -3,7 +3,9 @@ import urwid from toot import api from toot.exceptions import ApiError from toot.utils import format_content -from .utils import highlight_hashtags, parse_datetime +from toot.utils.datetime import parse_datetime + +from .utils import highlight_hashtags from .widgets import Button, CheckBox, RadioButton diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index fb255c8..58bc7f6 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -5,14 +5,15 @@ import webbrowser from typing import List, Optional +from toot.tui import app +from toot.utils import format_content +from toot.utils.datetime import parse_datetime, time_ago +from toot.utils.language import language_name + from .entities import Status from .scroll import Scrollable, ScrollBar -from .utils import highlight_hashtags, parse_datetime, highlight_keys +from .utils import highlight_hashtags, highlight_keys from .widgets import SelectableText, SelectableColumns -from toot.tui import app -from toot.tui.utils import time_ago -from toot.utils import format_content -from toot.utils.language import language_name logger = logging.getLogger("toot") diff --git a/toot/tui/utils.py b/toot/tui/utils.py index bb156e6..3c9bf37 100644 --- a/toot/tui/utils.py +++ b/toot/tui/utils.py @@ -1,62 +1,14 @@ import base64 -import math -import os import re import shutil import subprocess -from typing import List import urwid -from datetime import datetime, timezone from functools import reduce from html.parser import HTMLParser +from typing import List HASHTAG_PATTERN = re.compile(r'(? str: - now = datetime.now().astimezone() - delta = now.timestamp() - value.timestamp() - - if delta < 1: - return "now" - - if delta < 8 * DAY: - if delta < MINUTE: - return f"{math.floor(delta / SECOND)}".rjust(2, " ") + "s" - if delta < HOUR: - return f"{math.floor(delta / MINUTE)}".rjust(2, " ") + "m" - if delta < DAY: - return f"{math.floor(delta / HOUR)}".rjust(2, " ") + "h" - return f"{math.floor(delta / DAY)}".rjust(2, " ") + "d" - - if delta < 53 * WEEK: # not exactly correct but good enough as a boundary - return f"{math.floor(delta / WEEK)}".rjust(2, " ") + "w" - - return ">1y" def highlight_keys(text, high_attr, low_attr=""): diff --git a/toot/utils/datetime.py b/toot/utils/datetime.py new file mode 100644 index 0000000..ab05fc1 --- /dev/null +++ b/toot/utils/datetime.py @@ -0,0 +1,52 @@ +import math +import os +import re + +from datetime import datetime, timezone + + +def parse_datetime(value): + """Returns an aware datetime in local timezone""" + + # In Python < 3.7, `%z` does not match `Z` offset + # https://docs.python.org/3.7/library/datetime.html#strftime-and-strptime-behavior + if value.endswith("Z"): + dttm = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc) + else: + dttm = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f%z") + + # When running tests return datetime in UTC so that tests don't depend on + # the local timezone + if "PYTEST_CURRENT_TEST" in os.environ: + return dttm.astimezone(timezone.utc) + + return dttm.astimezone() + + +SECOND = 1 +MINUTE = SECOND * 60 +HOUR = MINUTE * 60 +DAY = HOUR * 24 +WEEK = DAY * 7 + + +def time_ago(value: datetime) -> str: + now = datetime.now().astimezone() + delta = now.timestamp() - value.timestamp() + + if delta < 1: + return "now" + + if delta < 8 * DAY: + if delta < MINUTE: + return f"{math.floor(delta / SECOND)}".rjust(2, " ") + "s" + if delta < HOUR: + return f"{math.floor(delta / MINUTE)}".rjust(2, " ") + "m" + if delta < DAY: + return f"{math.floor(delta / HOUR)}".rjust(2, " ") + "h" + return f"{math.floor(delta / DAY)}".rjust(2, " ") + "d" + + if delta < 53 * WEEK: # not exactly correct but good enough as a boundary + return f"{math.floor(delta / WEEK)}".rjust(2, " ") + "w" + + return ">1y"