From 48c2c0f85055506031780802d47c82c22a3580f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Fri, 17 Oct 2025 13:57:08 +0200 Subject: [PATCH] fix: handle negative timestamps --- src/feather/client.py | 18 +++++++++++++----- src/feather/data.py | 18 +++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/feather/client.py b/src/feather/client.py index 66c9bba..7940821 100644 --- a/src/feather/client.py +++ b/src/feather/client.py @@ -4,7 +4,7 @@ from __future__ import annotations import re from abc import ABC, abstractmethod -from ttrss.client import TTRClient +from ttrss.client import TTRClient, Headline import google_reader from feather.config import Config @@ -146,11 +146,19 @@ class GReaderArticle(Article): # several API references I've seen didn't mention canonical, but alternate seems to also be the article link (?) and should be an ok fallback self.article_url = item_content.alternate[0].href - self._compute_json_path() + self.json_path = self._get_json_path() ## Tiny Tiny RSS API ## +# Monkey patch Headline.__init__ to skip timestamp to datetime conversion +# Articles may have a negative timestamp and Python's datetime.fromtimestamp doesn't like that, so instead we keep the timestamp and deal with the issue in data.py/format_datetime +def Headline_init(self, attr, client): + super(Headline, self).__init__(attr, client) + + +Headline.__init__ = Headline_init + class TTRSession(ClientSession): """Tiny Tiny RSS API client""" @@ -234,8 +242,8 @@ class TTRArticle(Article): self.unread = article.unread self.title = article.title - self.published = article.updated.timestamp() - self.updated = article.updated.timestamp() + self.published = article.updated + self.updated = article.updated self.author = article.author self.summary = article.excerpt self.content = article.content @@ -248,4 +256,4 @@ class TTRArticle(Article): self.language = article.lang self.image_url = article.flavor_image - self._compute_json_path() + self.json_path = self._get_json_path() diff --git a/src/feather/data.py b/src/feather/data.py index b9a84ca..09738e4 100644 --- a/src/feather/data.py +++ b/src/feather/data.py @@ -5,7 +5,7 @@ from __future__ import annotations import os import json from abc import ABC -from datetime import datetime +from datetime import datetime, timedelta from pathlib import Path from hashlib import sha256 from tempfile import NamedTemporaryFile @@ -40,9 +40,13 @@ def sanitize_filename( def format_datetime(config: Config, timestamp: int) -> str: """Format a timestamp according to the configuraiton.""" - return datetime.fromtimestamp(timestamp, config.timezone).strftime( - config.time_format - ) + if timestamp < 0: + date = datetime(1970, 1, 1, tzinfo=config.timezone) + timedelta( + seconds=timestamp + ) + else: + date = datetime.fromtimestamp(timestamp, config.timezone) + return date.strftime(config.time_format) def atomic_write(path: Path, content: str): @@ -168,13 +172,13 @@ class Article(ABC): d["category"] = self.category.asdict() return d - def _compute_json_path(self): - self.json_path = self.config.json_root / f"{self._hash_id()}.json" + def _get_json_path(self) -> Path: + return self.config.json_root / f"{self._hash_id()}.json" def _write_json(self, recompute_path=False): """Write the JSON file associated with this article. Error if it already exists.""" if recompute_path: - self._compute_json_path() + self.json_path = self._get_json_path() stored_fields = ( "id", "unread",