Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public static class Answer {
private Long questionId;
private Integer responseTime;
private String answerContent;
// 채팅 서버의 시각이라 UTC다. 시간대 없이 오므로 저장할 때 이 서버 시간대로 옮긴다.
private LocalDateTime answerCreatedAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import repit.repit_api_server.global.exception.BusinessException;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
Expand Down Expand Up @@ -432,6 +434,19 @@ private LocalDateTime orNow(LocalDateTime createdAt) {
return createdAt == null ? LocalDateTime.now() : createdAt;
}

/**
* 채팅 서버가 찍은 시각을 이 서버의 시각으로 옮긴다.
*
* <p>채팅 서버는 UTC 컨테이너에서 시간대 없이 LocalDateTime.now()를 보낸다. 그대로 저장하면 이 서버가 UTC가
* 아닐 때(로컬 KST) 답변만 이 서버가 찍은 질문 시각보다 9시간 이르게 남는다.
*/
// ponytail: 채팅 서버가 UTC라고 가정한다. 다른 시간대에서 돌리면 어긋나므로, 채팅 서버가 오프셋을 실어 보내면 그 값을 쓴다.
private LocalDateTime fromChatClock(LocalDateTime chatTime) {
return chatTime == null ? null : chatTime.atOffset(ZoneOffset.UTC)
.atZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
}

/**
* 질문 의도. 채점은 이 값 하나를 기준으로 이뤄진다.
*
Expand Down Expand Up @@ -474,7 +489,7 @@ private void saveAnswers(InterviewEntity interview, List<SaveInterviewRequest.Qn
.userId(interview.getUserId())
.responseTime(answer.getResponseTime())
.content(answer.getAnswerContent())
.createdAt(orNow(answer.getAnswerCreatedAt()))
.createdAt(orNow(fromChatClock(answer.getAnswerCreatedAt())))
.build());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicLong;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -215,8 +216,21 @@ private List<AnswerEntity> savedAnswers() {
assertThat(answers).extracting(AnswerEntity::getQuestionId).containsExactly(901L, 902L);
assertThat(answers).extracting(AnswerEntity::getUserId).containsOnly(7L);
assertThat(answers.get(0).getContent()).isEqualTo("스레드가 I/O 대기에 묶였습니다.");
assertThat(answers.get(0).getCreatedAt())
.isEqualTo(LocalDateTime.parse("2026-08-18T01:01:30"));
}

@Test
void 채팅_서버의_UTC_답변_시각을_이_서버_시간대로_옮긴다() {
// CI는 UTC라 그대로 두면 옮기지 않아도 통과한다. 로컬처럼 KST에서 돌려 본다.
TimeZone original = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
try {
service.saveInterview(request());

assertThat(savedAnswers().get(0).getCreatedAt())
.isEqualTo(LocalDateTime.parse("2026-08-18T10:01:30"));
} finally {
TimeZone.setDefault(original);
}
}

@Test
Expand Down
Loading