"""Formatowanie długości ekliptycznej (LOG-01: kilka zapisów). Astrolog myśli w stopniach/minutach/sekundach w znaku, Excel woli dziesiętne, a część technik używa pozycji absolutnej 0–360°. Tu są czyste, bezstanowe funkcje konwersji — bez zależności od jakiegokolwiek silnika. """ from __future__ import annotations SIGNS = [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces", ] SIGN_ABBR = ["Ari", "Tau", "Gem", "Can", "Leo", "Vir", "Lib", "Sco", "Sag", "Cap", "Aqu", "Pis"] def norm360(lon: float) -> float: return lon % 360.0 def sign_index(lon: float) -> int: """0 = Aries … 11 = Pisces.""" return int(norm360(lon) // 30) def _dms(deg: float) -> tuple[int, int, int]: """Rozkład stopni (>=0) na (°, ', ") z poprawnym przeniesieniem zaokrąglenia.""" total = round(deg * 3600) d, rem = divmod(total, 3600) m, s = divmod(rem, 60) return d, m, s def in_sign(lon: float) -> str: """Np. 'Tau 28°12'57\"' — pozycja w znaku.""" lon = norm360(lon) idx = sign_index(lon) d, m, s = _dms(lon - idx * 30) if d >= 30: # zaokrąglenie przekroczyło granicę znaku idx = (idx + 1) % 12 d -= 30 return f"{SIGN_ABBR[idx]} {d}°{m:02d}'{s:02d}\"" def absolute(lon: float) -> str: """Np. '58°12'57\"' — pozycja absolutna 0–360°.""" d, m, s = _dms(norm360(lon)) return f"{d}°{m:02d}'{s:02d}\"" def decimal(lon: float, places: int = 6) -> float: return round(norm360(lon), places)