사주 개선 1편: 입력 날짜와 보정 날짜가 달라질 때 계산 기준 세우기
사주 풀이 기능을 처음 만들 때는 사용자가 입력한 양력 날짜와 시각을 그대로 계산 라이브러리에 전달했다. 입력과 결과가 바로 연결된다는 점은 단순했지만, 출생 지역의 경도와 표준시 기준이 달라지는 순간 설명하기 어려운 결과가 생겼다.
이번 개선의 질문은 하나였다.
사용자가 입력한 시각과 실제 계산에 사용한 시각이 다를 수 있다면, 어떤 날짜를 음력과 시주 계산의 기준으로 삼아야 할까?
결론은 지역 태양시 보정 결과를 계산의 기준으로 삼고, 보정 전후가 달라진 사실을 결과에 함께 보여 주는 것이었다.
표준시와 태양시 사이의 간격
표준시는 넓은 지역에서 시계를 맞추기 위한 약속이고, 태양시는 관측 장소의 경도에 따라 태양이 남중하는 시각이 달라지는 방식이다. 한국 표준시의 기준 경도는 동경 135도다. 서울처럼 경도가 127도 안팎인 지역은 표준시 기준보다 태양의 위치가 시계보다 늦게 움직인다.
woosday는 선택한 출생 지역의 경도와 시간대 오프셋을 사용해 다음 두 항목을 계산한다.
| 보정 요소 | 의미 | 적용 방식 |
|---|---|---|
| 경도 차이 | 지역과 시간대 기준 경도의 차이 | 경도 1도당 약 4분으로 환산 |
| 균시차 | 날짜에 따라 달라지는 태양 운동의 차이 | 간단한 일자 기반 근사식 적용 |
| 시간대 오프셋 | Asia/Seoul, Asia/Tokyo 같은 지역 시간 | 해당 시각의 UTC 오프셋에서 기준 경도 계산 |
실제 서비스에서는 이 보정값을 분 단위로 반올림한다. 보정 결과는 숨기지 않고 correctedSolarTime과 correctionMinutes로 응답에 담는다.
ZonedDateTime zoned = ZonedDateTime.of(date, time, timeZone);
double localMeridian = zoned.getOffset().getTotalSeconds() / 3600.0 * 15.0;
int correctionMinutes = Math.toIntExact(Math.round(
(longitude - localMeridian) * 4.0 + equationOfTime
));
LocalDateTime corrected = LocalDateTime.of(date, time).plusMinutes(correctionMinutes);
이 값은 천문 관측의 모든 변수를 재현하는 정밀 모델이 아니다. 서비스가 어떤 기준으로 시간을 바꿨는지 설명하고, 경계 시각에서 결과가 바뀔 수 있음을 알리는 실용적인 보정 단계다.
날짜가 바뀌는 경우를 따로 처리하기
가장 조심해야 했던 부분은 자정 부근이다. 예를 들어 입력이 3월 15일 00시인데 지역 태양시 보정값이 -42분이면 계산 시각은 3월 14일 23시 18분이 된다. 이때 입력 날짜를 그대로 사용하면 음력 날짜와 시주의 날짜가 서로 다른 기준을 바라보게 된다.
그래서 계산 순서를 다음처럼 고정했다.
- 입력 날짜와 시각을 지역 태양시로 보정한다.
- 보정된
LocalDateTime을SolarTime으로 변환한다. SolarTime에서 음력 날짜와 절기와 네 기둥을 함께 얻는다.- 보정으로 날짜가 바뀌었다면 결과에 경고를 추가한다.
SolarTime solarTime = SolarTime.fromYmdHms(
correctedTime.getYear(), correctedTime.getMonthValue(),
correctedTime.getDayOfMonth(), correctedTime.getHour(),
correctedTime.getMinute(), correctedTime.getSecond()
);
String lunarDate = solarTime.getLunarHour().getLunarDay().toString();
핵심은 입력 객체의 날짜를 다시 조합해 음력을 계산하지 않는 것이다. 보정된 SolarTime에서 음력 시각을 얻어야 날짜 경계에서 계산 기준이 흔들리지 않는다.
화면에서 보정 사실을 설명하기
보정 날짜만 화면에 표시하면 사용자는 입력을 잘못했거나 서비스가 임의로 날짜를 바꿨다고 생각할 수 있다. 그래서 리포트에는 다음 정보를 함께 보여 준다.
- 입력한 날짜와 시각
- 선택한 출생 지역과 시간대
- 지역 태양시로 보정된 시각
- 보정 분량
- 보정으로 날짜가 바뀐 경우의 경고
특히 보정값이 30분 이상이면 별도 안내를 표시한다. 이 문구는 결과가 더 정확하다고 주장하기 위한 것이 아니라, 어떤 입력과 기준으로 계산했는지 확인하도록 돕는 장치다.
테스트로 고정한 경계
정상적인 오후 시각만 테스트하면 이 문제를 발견하기 어렵다. 다음 경계를 테스트에 포함했다.
| 테스트 | 확인할 것 |
|---|---|
| 서울과 도쿄의 같은 입력 | 시간대와 경도에 따라 보정값이 달라지는지 |
| 자정 직전과 직후 | 보정 날짜와 음력 날짜가 같은 기준인지 |
| 보정값이 30분 이상 | 경고가 결과에 포함되는지 |
| 잘못된 시간대 | ZoneId 검증으로 요청을 거부하는지 |
날짜 보정은 결과를 화려하게 만드는 기능이 아니다. 입력값의 한계를 먼저 인정하고, 계산 기준을 일관되게 지키는 작업에 가깝다.
다음 글
날짜를 맞춰도 절기 교체 시각과 자시 경계에서는 결과가 달라질 수 있다. 다음 글에서는 시간대와 절입 경계를 어떻게 다루고 테스트했는지 정리한다.