커뮤니티 게시글 상대 시간 표기 다국어 지원

This commit is contained in:
Yu Sung
2025-12-19 16:02:09 +09:00
parent f51fe327e9
commit bea50b0085
5 changed files with 147 additions and 2 deletions
@@ -58,7 +58,7 @@ struct CreatorCommunityAllItemView: View {
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
Text(item.date)
Text(item.relativeTimeText())
.font(.custom(Font.light.rawValue, size: 13.3))
.foregroundColor(Color.gray77)
}
@@ -175,6 +175,7 @@ struct CreatorCommunityAllItemView_Previews: PreviewProvider {
content: "너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!",
price: 10,
date: "3일전",
dateUtc: "2025-08-10T15:00:00",
isCommentAvailable: false,
isAdult: false,
isLike: true,
@@ -26,7 +26,7 @@ struct CreatorCommunityItemView: View {
.font(.custom(Font.preBold.rawValue, size: 18))
.foregroundColor(Color.white)
Text(item.date)
Text(item.relativeTimeText())
.font(.custom(Font.preRegular.rawValue, size: 14))
.foregroundColor(Color(hex: "78909C"))
}
@@ -100,6 +100,7 @@ struct CreatorCommunityItemView_Previews: PreviewProvider {
content: "안녕하세요",
price: 10,
date: "3일전",
dateUtc: "2025-08-10T15:00:00",
isCommentAvailable: false,
isAdult: false,
isLike: false,
@@ -5,6 +5,8 @@
// Created by klaus on 2023/12/19.
//
import Foundation
struct GetCommunityPostListResponse: Decodable {
let postId: Int
let creatorId: Int
@@ -15,6 +17,7 @@ struct GetCommunityPostListResponse: Decodable {
let content: String
let price: Int
let date: String
let dateUtc: String
let isCommentAvailable: Bool
let isAdult: Bool
let isLike: Bool
@@ -23,3 +26,96 @@ struct GetCommunityPostListResponse: Decodable {
let commentCount: Int
let firstComment: GetCommunityPostCommentListItem?
}
// MARK: - 상대 시간 문자열
extension GetCommunityPostListResponse {
/// `dateUtc`(UTC 문자열)을 파싱해 디바이스 타임존 기준
/// 상대 시간(방금 전/분/시간/일/개월/년 전)을 반환합니다.
/// 파싱 실패 시, `date`를 그대로 반환합니다.
func relativeTimeText(now: Date = Date()) -> String {
guard let createdAt = DateParser.parse(dateUtc) else {
return date
}
let nowDate = now
let interval = max(0, nowDate.timeIntervalSince(createdAt))
// 연/월 차이는 달력 기준으로 계산(윤년/월 길이 반영)
let calendar = Calendar.current
let ym = calendar.dateComponents([.year, .month],
from: createdAt,
to: nowDate)
if let years = ym.year, years >= 1 {
return I18n.Time.yearsAgo(years)
}
if let months = ym.month, months >= 1 {
return I18n.Time.monthsAgo(months)
}
// 일/시간/분은 초 기반으로 간단 처리
if interval < 60 {
return I18n.Time.justNow
} else if interval < 3600 {
let minutes = Int(interval / 60)
return I18n.Time.minutesAgo(max(1, minutes))
} else if interval < 86_400 {
let hours = Int(interval / 3600)
return I18n.Time.hoursAgo(max(1, hours))
} else {
let days = Int(interval / 86_400)
return I18n.Time.daysAgo(max(1, days))
}
}
}
// MARK: - 내부: 다양한 포맷 파서를 시도
private enum DateParser {
static func parse(_ text: String) -> Date? {
for parser in parsers {
if let d = parser(text) { return d }
}
return nil
}
// 시도 순서: ISO8601(소수초 포함) → ISO8601 → RFC3339 유사 → 공백 구분 기본
private static let parsers: [(String) -> Date?] = [
{ ISO8601.fractional.date(from: $0) },
{ ISO8601.basic.date(from: $0) },
{ DF.rfc3339.date(from: $0) },
{ DF.basic.date(from: $0) }
]
private enum ISO8601 {
static let fractional: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
f.timeZone = TimeZone(secondsFromGMT: 0)
return f
}()
static let basic: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime]
f.timeZone = TimeZone(secondsFromGMT: 0)
return f
}()
}
private enum DF {
static let rfc3339: DateFormatter = {
let f = DateFormatter()
f.locale = Locale(identifier: "en_US_POSIX")
f.timeZone = TimeZone(secondsFromGMT: 0)
f.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return f
}()
static let basic: DateFormatter = {
let f = DateFormatter()
f.locale = Locale(identifier: "en_US_POSIX")
f.timeZone = TimeZone(secondsFromGMT: 0)
f.dateFormat = "yyyy-MM-dd HH:mm:ss"
return f
}()
}
}