Files
context-continuity/venv.old-py38/lib/python3.9/site-packages/trilium_py/utils/time_util.py
T
Master 943acbc573 feat: referentiel projets dynamique + validation nommage by design
Durcit la convention de nommage des projets (dérive constatée : 'Sliding
Automation', 'code_versioning'... au lieu des formes canoniques).

- trilium_api.py : projets_canoniques() lit le référentiel = valeurs du label
  projet sur les notes de type=projet (source unique, pas de constante en dur).
  Note-projet CodeVersioning créée (manquait).
- mcp_server.py : _valider_projet() branché dans les 6 tools de création
  (add_decision/history/backlog, new_conversation, create_entite, add_skill).
  Refuse un projet non canonique (suggestion si faute) ou inconnu (renvoi au
  processus de création de projet). Ne verrouille pas si référentiel illisible.
- lint_audit.py : VAL-nommage aligné sur le référentiel (attrape casse, espace
  ET snake_case ; l'ancien 'contient un espace' ratait code_versioning).
- Données : 79 notes ré-étiquetées vers les 3 formes canoniques.

Quality by design : l'erreur de nommage devient impossible à l'écriture, le
Lint n'est plus que le filet de sécurité.
2026-07-17 14:59:33 +02:00

189 lines
5.8 KiB
Python

import dateutil
from datetime import timedelta, datetime
from typing import Optional, Tuple, Union
from loguru import logger
def get_today() -> str:
"""
Get today's date in YYYY-MM-DD format.
Returns:
str: Today's date as a string in format "%Y-%m-%d"
"""
return datetime.now().strftime("%Y-%m-%d")
def get_yesterday() -> str:
"""
Get yesterday's date in YYYY-MM-DD format.
Returns:
str: Yesterday's date as a string in format "%Y-%m-%d"
"""
return (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
def get_local_timezone():
"""
Get the local timezone.
Returns:
tzinfo: The local timezone info
"""
logger.debug("Getting local timezone")
local_timezone = datetime.now().astimezone().tzinfo
logger.debug(f"Local timezone: {local_timezone}")
return local_timezone
def ensure_timezone(dt: datetime, tz=None) -> datetime:
"""
Ensure a datetime object has timezone information.
Args:
dt (datetime): The datetime object to check
tz: The timezone to use if dt has no timezone. If None, use local timezone.
Returns:
datetime: A datetime object with timezone information
"""
if dt.tzinfo is None:
if tz is None:
tz = get_local_timezone()
dt = dt.replace(tzinfo=tz)
logger.debug(f"Added timezone to datetime: {dt}")
return dt
def handle_dates(
dateCreated: Optional[datetime] = None,
utcDateCreated: Optional[datetime] = None
) -> Tuple[Optional[datetime], Optional[datetime]]:
"""
Ensure that both local and UTC times are defined and have the same time
(adjusted for timezone).
Args:
dateCreated (datetime, optional): Local datetime
utcDateCreated (datetime, optional): UTC datetime
Returns:
tuple: (local_datetime, utc_datetime)
Raises:
TypeError: If dateCreated or utcDateCreated is not a datetime object
"""
if not dateCreated and not utcDateCreated:
return None, None
if dateCreated and not isinstance(dateCreated, datetime):
logger.error(f"dateCreated is not datetime object, is {type(dateCreated)}")
raise TypeError("dateCreated must be a datetime object")
if utcDateCreated and not isinstance(utcDateCreated, datetime):
logger.error(f"utcDateCreated is not datetime object, is {type(utcDateCreated)}")
raise TypeError("utcDateCreated must be a datetime object")
# Ensure timezone info is set
if dateCreated:
dateCreated = ensure_timezone(dateCreated)
if utcDateCreated:
utcDateCreated = ensure_timezone(utcDateCreated, dateutil.tz.tzutc())
# Synchronize dates
return synchronize_dates(local_date=dateCreated, utc_date=utcDateCreated)
def synchronize_dates(
local_date: Optional[datetime],
utc_date: Optional[datetime]
) -> Tuple[Optional[datetime], Optional[datetime]]:
"""
Synchronize local and UTC dates. We expect only one of local or utc date to
be passed, use that to define the other, and return both as datetime objects.
Args:
local_date (datetime, optional): Local datetime with timezone info
utc_date (datetime, optional): UTC datetime with timezone info
Returns:
tuple: (local_datetime, utc_datetime)
Raises:
ValueError: If both local_date and utc_date are provided
"""
if local_date and utc_date:
msg = "Both local and UTC dates were provided, cannot determine which to use.\n"
msg = msg + "Please pass only one of local or UTC date."
logger.error(msg)
raise ValueError(msg)
local_timezone = get_local_timezone()
if local_date and not utc_date:
utc_date = local_date.astimezone(dateutil.tz.tzutc())
elif utc_date and not local_date:
local_date = utc_date.astimezone(local_timezone)
elif local_date and utc_date != utc_date.astimezone(dateutil.tz.tzlocal()):
logger.error("local_date and utc_date are inconsistent")
raise ValueError("local_date and utc_date are inconsistent.")
logger.debug(f"Synchronized dates: local={local_date}, utc={utc_date}")
return local_date, utc_date
def format_date_to_etapi(date: datetime, kind: str) -> str:
"""
From a datetime object, return a date string formatted to ETAPI requirements.
Args:
date (datetime): The datetime object to format
kind (str): Either 'local' or 'utc'
Returns:
str: Formatted date string
local: '2023-08-21 23:38:51.110-0200'
UTC : '2023-08-22 01:38:51.110Z'
"""
if kind == "local":
formatted_date = date.strftime("%Y-%m-%d %H:%M:%S.%d3%z")
elif kind == "utc":
date = date.astimezone(dateutil.tz.tzstr("Z")) # use Zulu time
formatted_date = date.strftime("%Y-%m-%d %H:%M:%S.%d3%Z")
else:
raise ValueError(f"Invalid kind: {kind}. Must be 'local' or 'utc'")
logger.debug(f"ETAPI Formatted date ({kind}): {formatted_date}")
return formatted_date
def format_dates_for_api(
local_date: Optional[datetime] = None,
utc_date: Optional[datetime] = None
) -> Tuple[Optional[str], Optional[str]]:
"""
Format dates for API calls. This is a convenience function that combines
handle_dates and format_date_to_etapi.
Args:
local_date (datetime, optional): Local datetime
utc_date (datetime, optional): UTC datetime
Returns:
tuple: (formatted_local_date, formatted_utc_date)
"""
if not local_date and not utc_date:
return None, None
local_dt, utc_dt = handle_dates(dateCreated=local_date, utcDateCreated=utc_date)
if not local_dt or not utc_dt:
return None, None
local_str = format_date_to_etapi(local_dt, kind='local')
utc_str = format_date_to_etapi(utc_dt, kind='utc')
return local_str, utc_str