Skip to content

Commit 012a970

Browse files
committed
Fix return local time handling
1 parent 5306b46 commit 012a970

4 files changed

Lines changed: 137 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2351,6 +2351,12 @@ Computation: The graphical ephemeris now builds its displayed day series over th
23512351

23522352
GUI: The ephemeris longitude/declination view now uses one coherent x-axis mapping for plotted curves, month grid lines, hover dates, and snapped station markers. This removes the right-shifted hover date behavior, restores the missing month boundary line, and places the hover popup slightly closer to the snapped plot point.
23532353

2354+
## 2026-04-09 01:26 (return timezone consistency)
2355+
2356+
Computation: Return charts now convert computed revolution instants back to local civil time through the chart timezone when a real `tzid` is available, instead of reusing a fixed natal offset/DST assumption. Planetary return state now retains the zone fallback fields needed for reopen and stepper rebuilds, and revolution hit timestamps are rounded to the nearest second before the final refinement path.
2357+
2358+
GUI: Solar, lunar, and planetary return tabs now show consistent local chart times in the wheel header and workspace labels, including after reopening a saved return session or stepping through return cycles. No other visible interface behavior changed.
2359+
23542360
## 2026-03-29 23:39 (synastry root-session swapping)
23552361

23562362
Computation: No astrology midpoint or chart-construction formulas changed in this pass. Synastry still uses the same two source radix charts and composite conversion still uses the same pseudo-position builder, but synastry runtime state is now modeled as its own root workspace session whose active center chart can swap between the two radix inputs.

engine/supplementary_adapter.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,12 @@ def normalize_retained_state(self, frame, current_chart=None, retained=None, pla
507507
state['place_payload'] = place_payload
508508
if 'plus' not in state:
509509
state['plus'] = bool(getattr(getattr(current_chart, 'time', None), 'plus', True))
510+
if 'zh' not in state:
511+
state['zh'] = int(getattr(getattr(current_chart, 'time', None), 'zh', 0) or 0)
512+
if 'zm' not in state:
513+
state['zm'] = int(getattr(getattr(current_chart, 'time', None), 'zm', 0) or 0)
514+
if 'daylight' not in state:
515+
state['daylight'] = bool(getattr(getattr(current_chart, 'time', None), 'daylightsaving', False))
510516
state['cycle_offset'] = int(state.get('cycle_offset', 0) or 0)
511517
return state
512518

@@ -568,18 +574,24 @@ def build(self, frame, driver_state, binding, current_chart=None, session=None):
568574
raw_dt = tuple(int(v) for v in (t1, t2, t3, t4, t5, t6))
569575
place = payload_to_place(retained.get('place_payload'), fallback=(getattr(current_chart, 'place', None) if current_chart is not None else getattr(base_chart, 'place', None)))
570576
plus = bool(retained.get('plus', True))
577+
zh = int(retained.get('zh', getattr(base_chart.time, 'zh', 0) or 0))
578+
zm = int(retained.get('zm', getattr(base_chart.time, 'zm', 0) or 0))
579+
daylight = bool(retained.get('daylight', getattr(base_chart.time, 'daylightsaving', False)))
571580
if getattr(frame.options, 'ayanamsha', 0) != 0:
572581
try:
573582
t1, t2, t3, t4, t5, t6 = frame.calcPrecNutCorrectedRevolution(revs, pid, topo_place=place, seed=(t1, t2, t3, t4, t5, t6))
574583
except Exception:
575584
pass
576585
time_obj = chart.Time(t1, t2, t3, t4, t5, t6, False, base_chart.time.cal, chart.Time.GREENWICH, plus, 0, 0, False, place, False)
577586
revolution = chart.Chart(base_chart.name, base_chart.male, time_obj, place, chart.Chart.REVOLUTION, '', frame.options, False)
578-
display_dt = frame._revolution_display_datetime(base_chart, t1, t2, t3, t4, t5, t6, plus=plus, zh=0, zm=0, daylight=False)
587+
display_dt = frame._revolution_display_datetime(base_chart, t1, t2, t3, t4, t5, t6, plus=plus, zh=zh, zm=zm, daylight=daylight)
579588
retained.update({
580589
'planet_type': planet_type,
581590
'place_payload': place_to_payload(place),
582591
'plus': plus,
592+
'zh': zh,
593+
'zm': zm,
594+
'daylight': daylight,
583595
'cycle_offset': cycle_offset,
584596
'raw_return_datetime': raw_dt,
585597
})

morin.py

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3760,7 +3760,13 @@ def _rebuild_workspace_lunar_return_child(self, current_chart, base_chart, targe
37603760
place = getattr(current_chart, 'place', None) or base_chart.place
37613761
time = chart.Time(t1, t2, t3, t4, t5, t6, False, base_chart.time.cal, chart.Time.GREENWICH, True, 0, 0, False, place, False)
37623762
revolution = chart.Chart(base_chart.name, base_chart.male, time, place, chart.Chart.LUNAR, '', self.options, False)
3763-
display_dt = self._revolution_display_datetime(base_chart, t1, t2, t3, t4, t5, t6, plus=True, zh=0, zm=0, daylight=False)
3763+
display_dt = self._revolution_display_datetime(
3764+
base_chart, t1, t2, t3, t4, t5, t6,
3765+
plus=True,
3766+
zh=getattr(base_chart.time, 'zh', 0),
3767+
zm=getattr(base_chart.time, 'zm', 0),
3768+
daylight=getattr(base_chart.time, 'daylightsaving', False),
3769+
)
37643770
return (revolution, display_dt)
37653771

37663772
def _supplementary_launch_context_for_session(self, feature_kind, chart_session_obj, fallback_chart=None):
@@ -6542,6 +6548,9 @@ def _workspace_open_planetary_return_from_document(self, document_id, planet_typ
65426548
result.binding.parent_source_datetime = self._datetime_to_display_tuple(ctx['source_datetime'])
65436549
place = supplementary_adapter.payload_to_place(result.binding.retained_state.get('place_payload'), fallback=base_chart.place)
65446550
plus = bool(result.binding.retained_state.get('plus', True))
6551+
zh = int(result.binding.retained_state.get('zh', getattr(base_chart.time, 'zh', 0) or 0))
6552+
zm = int(result.binding.retained_state.get('zm', getattr(base_chart.time, 'zm', 0) or 0))
6553+
daylight = bool(result.binding.retained_state.get('daylight', getattr(base_chart.time, 'daylightsaving', False)))
65456554
raw_return_dt = result.binding.retained_state.get('raw_return_datetime')
65466555
cycle_offset = int(result.binding.retained_state.get('cycle_offset', 0) or 0)
65476556
label = self._workspace_timed_label(mtexts.revtypeList[planet_type], result.display_datetime[0], result.display_datetime[1], result.display_datetime[2], result.display_datetime[3], result.display_datetime[4], result.display_datetime[5])
@@ -6551,7 +6560,10 @@ def _workspace_open_planetary_return_from_document(self, document_id, planet_typ
65516560
result.chart,
65526561
result.display_datetime,
65536562
result.binding,
6554-
after_change=lambda active_cs: self._install_workspace_planetary_revolution_stepper(active_cs, base_chart, planet_type, place, plus, raw_return_dt, cycle_offset=cycle_offset),
6563+
after_change=lambda active_cs: self._install_workspace_planetary_revolution_stepper(
6564+
active_cs, base_chart, planet_type, place, plus, raw_return_dt,
6565+
zh=zh, zm=zm, daylight=daylight, cycle_offset=cycle_offset,
6566+
),
65556567
)
65566568
existing_session['launcher_kind'] = 'planetary_return'
65576569
existing_session['planetary_return_type'] = int(planet_type)
@@ -6564,7 +6576,10 @@ def _workspace_open_planetary_return_from_document(self, document_id, planet_typ
65646576
radix=base_chart,
65656577
parent_document_id_override=ctx.get('parent_document_id'),
65666578
indent_level_override=(selected_indent + 1),
6567-
after_open=lambda active_cs: self._install_workspace_planetary_revolution_stepper(active_cs, base_chart, planet_type, place, plus, raw_return_dt, cycle_offset=cycle_offset),
6579+
after_open=lambda active_cs: self._install_workspace_planetary_revolution_stepper(
6580+
active_cs, base_chart, planet_type, place, plus, raw_return_dt,
6581+
zh=zh, zm=zm, daylight=daylight, cycle_offset=cycle_offset,
6582+
),
65686583
)
65696584
if doc is not None:
65706585
session = self._find_workspace_session(doc.document_id)
@@ -7305,6 +7320,31 @@ def _zone_adjusted_datetime(self, y, m, d, h, mi, s, plus, zh, zm, daylight=Fals
73057320
return (int(y), int(m), int(d), int(h), int(mi), int(s))
73067321

73077322
def _revolution_display_datetime(self, radix, y, m, d, h, mi, s, plus=None, zh=None, zm=None, daylight=None):
7323+
if radix is not None:
7324+
tzid = getattr(getattr(radix, 'time', None), 'tzid', '') or ''
7325+
zoneinfo_cls = getattr(geonames, 'ZoneInfo', None)
7326+
if not tzid and getattr(radix, 'place', None) is not None:
7327+
try:
7328+
tzid = geonames.Geonames.get_timezone_name(radix.place.lon, radix.place.lat) or ''
7329+
except Exception:
7330+
tzid = ''
7331+
if tzid and zoneinfo_cls is not None:
7332+
try:
7333+
utc_dt = datetime.datetime(
7334+
int(y), int(m), int(d), int(h), int(mi), int(s),
7335+
tzinfo=datetime.timezone.utc,
7336+
)
7337+
local_dt = utc_dt.astimezone(zoneinfo_cls(tzid))
7338+
return (
7339+
local_dt.year,
7340+
local_dt.month,
7341+
local_dt.day,
7342+
local_dt.hour,
7343+
local_dt.minute,
7344+
local_dt.second,
7345+
)
7346+
except Exception:
7347+
pass
73087348
if plus is None:
73097349
plus = getattr(radix.time, 'plus', True)
73107350
if zh is None:
@@ -7936,7 +7976,7 @@ def _reset_lr_step_state():
79367976
else:
79377977
self._rev_stepper.Show(False)
79387978

7939-
def _install_workspace_planetary_revolution_stepper(self, active_cs, radix_chart, planet_type, place, plus, raw_return_dt, cycle_offset=0):
7979+
def _install_workspace_planetary_revolution_stepper(self, active_cs, radix_chart, planet_type, place, plus, raw_return_dt, zh=0, zm=0, daylight=False, cycle_offset=0):
79407980
if active_cs is None or radix_chart is None or raw_return_dt is None:
79417981
return
79427982
pid = revolutions.Revolutions.planetary_pid(planet_type)
@@ -7949,6 +7989,9 @@ def _install_workspace_planetary_revolution_stepper(self, active_cs, radix_chart
79497989
self._rev_ctx = {
79507990
'place': place,
79517991
'plus': plus,
7992+
'zh': zh,
7993+
'zm': zm,
7994+
'daylight': daylight,
79527995
'revtype': chart.Chart.REVOLUTION,
79537996
'planet_type': int(planet_type),
79547997
'cycle_offset': int(cycle_offset),
@@ -7971,7 +8014,10 @@ def _apply_planet_rev(revs2):
79718014
self._planet_rev_dt = raw_dt
79728015
display_dt = self._revolution_display_datetime(
79738016
radix_chart, int(ry), int(rm), int(rd), int(rhh), int(rmi), int(rss),
7974-
plus=self._rev_ctx['plus'], zh=0, zm=0, daylight=False,
8017+
plus=self._rev_ctx['plus'],
8018+
zh=self._rev_ctx.get('zh', 0),
8019+
zm=self._rev_ctx.get('zm', 0),
8020+
daylight=self._rev_ctx.get('daylight', False),
79758021
)
79768022
active_cs.change_chart(chart2, display_datetime=display_dt, change_reason='step')
79778023

@@ -8158,7 +8204,13 @@ def _open_quick_planet_revolution(self, planet_type):
81588204

81598205
time = chart.Time(t1, t2, t3, t4, t5, t6, False, radix.time.cal, chart.Time.GREENWICH, plus, 0, 0, False, place, False)
81608206
revolution = chart.Chart(radix.name, radix.male, time, place, chart.Chart.REVOLUTION, '', self.options, False)
8161-
display_dt = self._revolution_display_datetime(radix, t1, t2, t3, t4, t5, t6, plus=plus, zh=0, zm=0, daylight=False)
8207+
display_dt = self._revolution_display_datetime(
8208+
radix, t1, t2, t3, t4, t5, t6,
8209+
plus=plus,
8210+
zh=getattr(radix.time, 'zh', 0),
8211+
zm=getattr(radix.time, 'zm', 0),
8212+
daylight=getattr(radix.time, 'daylightsaving', False),
8213+
)
81628214
label = self._workspace_timed_label(mtexts.revtypeList[planet_type], display_dt[0], display_dt[1], display_dt[2], display_dt[3], display_dt[4], display_dt[5])
81638215
doc = self._open_workspace_session(
81648216
revolution, session_label=label,
@@ -8168,7 +8220,14 @@ def _open_quick_planet_revolution(self, planet_type):
81688220
if doc is None:
81698221
return
81708222

8171-
self._rev_ctx = {'place': place, 'plus': plus, 'revtype': chart.Chart.REVOLUTION}
8223+
self._rev_ctx = {
8224+
'place': place,
8225+
'plus': plus,
8226+
'zh': getattr(radix.time, 'zh', 0),
8227+
'zm': getattr(radix.time, 'zm', 0),
8228+
'daylight': getattr(radix.time, 'daylightsaving', False),
8229+
'revtype': chart.Chart.REVOLUTION,
8230+
}
81728231
self._planet_rev_dt = datetime.datetime(int(revs.t[0]), int(revs.t[1]), int(revs.t[2]), int(revs.t[3]), int(revs.t[4]), int(revs.t[5]))
81738232
try:
81748233
if hasattr(self, '_rev_stepper') and self._rev_stepper:
@@ -8197,7 +8256,10 @@ def _apply_planet_rev(revs2):
81978256
if active_cs is not None:
81988257
display_dt = self._revolution_display_datetime(
81998258
radix, int(ry), int(rm), int(rd), int(rhh), int(rmi), int(rss),
8200-
plus=self._rev_ctx['plus'], zh=0, zm=0, daylight=False,
8259+
plus=self._rev_ctx['plus'],
8260+
zh=self._rev_ctx.get('zh', 0),
8261+
zm=self._rev_ctx.get('zm', 0),
8262+
daylight=self._rev_ctx.get('daylight', False),
82018263
)
82028264
active_cs.change_chart(chart2, display_datetime=display_dt, change_reason='step')
82038265

@@ -11372,14 +11434,27 @@ def onRevolutions(self, event):
1137211434
time = chart.Time(t1, t2, t3, t4, t5, t6, False, radix.time.cal, chart.Time.GREENWICH, plus, 0, 0, False, place, False)
1137311435

1137411436
revolution = chart.Chart(radix.name, radix.male, time, place, revtype, '', self.options, False)
11375-
display_dt = self._revolution_display_datetime(radix, t1, t2, t3, t4, t5, t6, plus=plus, zh=0, zm=0, daylight=False)
11437+
display_dt = self._revolution_display_datetime(
11438+
radix, t1, t2, t3, t4, t5, t6,
11439+
plus=plus,
11440+
zh=getattr(radix.time, 'zh', 0),
11441+
zm=getattr(radix.time, 'zm', 0),
11442+
daylight=getattr(radix.time, 'daylightsaving', False),
11443+
)
1137611444
rev_label = self._workspace_timed_label(mtexts.typeList[revtype], display_dt[0], display_dt[1], display_dt[2], display_dt[3], display_dt[4], display_dt[5])
1137711445
self._open_workspace_session(
1137811446
revolution, session_label=rev_label,
1137911447
radix=radix, view_mode=chart_session.ChartSession.CHART,
1138011448
display_datetime=display_dt,
1138111449
)
11382-
self._rev_ctx = {'place': place, 'plus': plus, 'revtype': revtype}
11450+
self._rev_ctx = {
11451+
'place': place,
11452+
'plus': plus,
11453+
'zh': getattr(radix.time, 'zh', 0),
11454+
'zm': getattr(radix.time, 'zm', 0),
11455+
'daylight': getattr(radix.time, 'daylightsaving', False),
11456+
'revtype': revtype,
11457+
}
1138311458

1138411459
# 이전에 떠 있던 스텝퍼가 있으면 닫기(다른 리턴일 수도 있으니 먼저 정리)
1138511460
try:
@@ -11569,7 +11644,10 @@ def _apply_planet_rev(revs2):
1156911644
if active_cs2 is not None:
1157011645
display_dt = self._revolution_display_datetime(
1157111646
radix, int(ry), int(rm), int(rd), int(rhh), int(rmi), int(rss),
11572-
plus=self._rev_ctx['plus'], zh=0, zm=0, daylight=False,
11647+
plus=self._rev_ctx['plus'],
11648+
zh=self._rev_ctx.get('zh', 0),
11649+
zm=self._rev_ctx.get('zm', 0),
11650+
daylight=self._rev_ctx.get('daylight', False),
1157311651
)
1157411652
active_cs2.change_chart(
1157511653
chart2,

revolutions.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ def __init__(self):
4444
def _planet_params(self, typ):
4545
return Revolutions.PLANETARY_SPECS.get(typ, (None, 0))
4646

47+
def _rounded_transit_values(self, year, month, day, time_float):
48+
total_seconds = int(round(float(time_float) * 3600.0))
49+
y = int(year)
50+
m = int(month)
51+
d = int(day)
52+
53+
while total_seconds >= 86400:
54+
total_seconds -= 86400
55+
y, m, d = util.incrDay(y, m, d)
56+
while total_seconds < 0:
57+
total_seconds += 86400
58+
y, m, d = util.decrDay(y, m, d)
59+
60+
hour = total_seconds // 3600
61+
minute = (total_seconds % 3600) // 60
62+
second = total_seconds % 60
63+
return (y, m, d, int(hour), int(minute), int(second))
64+
4765
@classmethod
4866
def is_planetary_type(cls, typ):
4967
return typ in cls.PLANETARY_SPECS
@@ -56,8 +74,8 @@ def planetary_pid(cls, typ):
5674
return spec[0]
5775

5876
def _candidate_datetime(self, year, month, tr):
59-
hour, minute, second = util.decToDeg(tr.time)
60-
return datetime.datetime(int(year), int(month), int(tr.day), int(hour), int(minute), int(second))
77+
values = self._rounded_transit_values(year, month, tr.day, tr.time)
78+
return datetime.datetime(*values)
6179

6280
def _set_candidate(self, year, month, trans, index):
6381
self.createRevolution(int(year), int(month), trans, int(index))
@@ -118,11 +136,7 @@ def _lunar_month_hits(self, year, month, chrt):
118136
trans.month(int(year), int(month), chrt, astrology.SE_MOON)
119137
hits = []
120138
for tr in trans.transits:
121-
hour, minute, second = util.decToDeg(tr.time)
122-
values = (
123-
int(year), int(month), int(tr.day),
124-
int(hour), int(minute), int(second),
125-
)
139+
values = self._rounded_transit_values(year, month, tr.day, tr.time)
126140
hits.append((self._dt_from_t(values), values))
127141
hits = tuple(hits)
128142

@@ -150,11 +164,7 @@ def _planetary_month_hits(self, typ, year, month, chrt):
150164
trans.month(int(year), int(month), chrt, planet)
151165
hits = []
152166
for tr in trans.transits:
153-
hour, minute, second = util.decToDeg(tr.time)
154-
values = (
155-
int(year), int(month), int(tr.day),
156-
int(hour), int(minute), int(second),
157-
)
167+
values = self._rounded_transit_values(year, month, tr.day, tr.time)
158168
hits.append((self._dt_from_t(values), values))
159169
hits = tuple(hits)
160170

@@ -314,10 +324,10 @@ def compute(self, typ, by, bm, bd, chrt, target_year=None):
314324

315325

316326
def createRevolution(self, year, month, trans, num = 0):
317-
self.t[0] = year
318-
self.t[1] = month
319-
self.t[2] = trans.transits[num].day
320-
h, m, s = util.decToDeg(trans.transits[num].time)
321-
self.t[3] = h
322-
self.t[4] = m
323-
self.t[5] = s
327+
values = self._rounded_transit_values(year, month, trans.transits[num].day, trans.transits[num].time)
328+
self.t[0] = values[0]
329+
self.t[1] = values[1]
330+
self.t[2] = values[2]
331+
self.t[3] = values[3]
332+
self.t[4] = values[4]
333+
self.t[5] = values[5]

0 commit comments

Comments
 (0)