Joda-Time은 복잡한 java.util.Date와 java.util.Calendar의 대체제로 Stephen Colebourne이 만든 Java에서 날짜와 시간을 다루기 위한 라이브러리입니다. Java 8이 출시되기 전에는, 이 라이브러리가 Android 애플리케이션을 포함한 산업 개발에서 날짜 처리의 사실상의 표준이었습니다. Oracle Java Magazine(2023)에 따르면, Android API 26이 출시된 지 2년 내에 60% 이상의 프로젝트가 Joda-Time에서 java.time으로 마이그레이션되었습니다.
주요 결론
Joda-Time은 Java 언어에서 java.util.Date와 java.util.Calendar 클래스에 대한 질적인 대체제를 제공하는 오픈 소스 라이브러리(Apache 2.0)입니다. 이 프로젝트는 Stephen Colebourne이 2004년에 가변성, 포맷 복잡성, 제한된 시간대 지원 등 표준 날짜 및 시간 API의 많은 문제에 대응하여 설립되었습니다. 이 라이브러리는 Java 커뮤니티에서 너리 채택되었고, 내장 날짜 처리 도구가 특히 불편했던 대규모 기업 시스템과 Android 애플리케이션을 포함한 수추어지 않은 프로젝트에서 사용되었습니다. 2014년 Java 8이 출시되고 java.time 패키지(JSR-310)가 도입된 이후, 이 라이브러리는 유지보수 모드로 전환되었습니다 — 새로운 기능이 추가되지 않고, 기존 기능은 새 API에서 완전히 커버됩니다.
Joda-Time은 불변성 원칙에 기반합니다. 각 날짜 또는 시간 객체는 생성 후 변경할 수 없습니다. 날짜를 추가하고, 월을 바꾸고, 시간대를 설정하는 등 어떤 작업이나 원본을 그대로 두고 새 객체를 반환합니다. 이 접근 방식은 가변인 java.util.Date와 java.util.Calendar의 특징이었던 의도치 않은 객체 수정과 관련된 오류를 완전히 제거합니다. 또한 불변 설계를 통해 추가 동기화 없이 스레드 안전성을 확보합니다.
이 라이브러리는 다양한 시나리오에 대한 여러 가지 주요 클래스를 제공합니다. DateTime — 시간대가 포함된 완전한 날짜와 시간, LocalDate — 시간 없이 날짜만, LocalTime — 날짜 없이 시간만, LocalDateTime — 시간대 없이 날짜와 시간. 또한 간격(Interval), 기간(Period) 그리고 기간(Duration)을 다루는 클래스가 포함되어 있어 여러 단위로 둘 사이의 차이를 계산할 수 있습니다. 각 클래스는 일관된 처리를 위해 ReadableInstant 또는 ReadablePartial 인터페이스를 구현합니다.
Java에서 표준 날짜 API의 문제는 플랫폼 초기부터 알려져 있었습니다. java.util.Date와 java.util.Calendar 클래스에는 근본적인 흘이 있었습니다. 월은 0부터 번호매기가 시작되고, Date는 날짜와 시간을 동시에 표시했으며, Calendar는 가변이고 복잡한 설정이 필요했습니다. SimpleDateFormat을 통한 포맷은 스레드 안전하지 않았습니다 — 멀티스레드 애플리케이션에서 이는 올바르지 않은 날짜 파싱으로 이어졌습니다. 날짜를 집중적으로 사용하는 프로젝트에 일하는 Stephen Colebourne은 이러한 문제를 모두 해결할 대체 방안을 만들기로 결정했습니다.
InfoQ(2014)의 인터뷰에 따르면, Colebourne은 첫 공개 배포 전부터 1년 이상 동안 생산 프로젝트에서 Joda-Time을 테스트했습니다. 첫 번째 버전은 2004년에 출시되었고 곡 커뮤니티의 주목을 끈었습니다. 2010년까지 이 라이브러리는 Spring과 Hibernate와 같은 프레임워크를 포함한 대부분의 Java 프로젝트에서 표준 의존성이 되었습니다. Joda-Time의 성공은 Java 8에 포함된 JSR-310 규격(Date and Time API)에 직접적인 영향을 미쳐었습니다 — 같은 Stephen Colebourne이 규격 리더를 릡았습니다.
Joda-Time은 단순히 날짜 문제를 해결했을 뿐만 아닐니다 — Java API 설계에 대한 접근 방식을 변화시킴으로 타외되었습니다. 라이브러리가 보여준 불변 값 객체의 개념은 Java 8에서 모든 새 API의 표준으로 채택되었습니다. 또한 Joda-Time의 인기는 Oracle에게 커뮤니티가 낮은 품질의 표준 라이브러리를 용인하지 않는다는 것을 보여주었습니다. Joda-Time 이후, Java에서 파일 처리(NIO.2), 시간(java.time) 그리고 옵션 값(Optional) 등의 API가 재설계되었습니다.
Joda-Time은 Java 8 이전에는 표준 라이브러리에서 사용할 수 없었던 기능 집합을 제공합니다. 주요 장점으로는 자동 일광절약 시간 처리가 지원되는 200개 이상의 시간대 지원, 패턴과 로케일에 대한 매우 다양한 포맷 API, 그리고 여러 단위(년, 월, 일, 시)의 날짜 간 기간 계산이 있습니다. 특히 주목할 만한 것은 간격 시스템입니다. Interval(둘 사이의 간격), Period(달력 단위의 차이), Duration(밀리초 단위의 정확한 길이).
Joda-Time의 DateTimeFormatter 클래스는 java.text.SimpleDateFormat과 달리 스레드 안전한 날짜 포맷을 제공합니다. 포맷터는 패턴(예: “yyyy-MM-dd HH:mm:ss”) 또는 로케일 출력을 위한 스타일(SHORT, MEDIUM, LONG, FULL)을 통해 생성할 수 있습니다. 라이브러리는 포맷터 캐시를 지원하여 높은 부하 애플리케이션에서 성능을 향상시킵니다. 문자열에서 날짜 파싱은 엄격성 제어를 통해 수행되며 불완전하거나 불올바른 날짜를 허용하거나 거부할 수 있습니다.
Joda-Time의 강점 중 하나는 시간 간격을 다루는 것입니다. Interval 클래스는 두 시각 사이의 정확한 시간 간격을 나타내며 교차, 합침 그리고 포함 확인 작업을 지원합니다. Period는 Interval과 달리 달렉 단위로 작동합니다. 예를 들면 3월 1일과 4월 1일의 차이는 틁토한 1개월이지만 기간으로는 28–31일일 수 있습니다. 반면 Duration은 달렉 없이 밀리초 단위의 정확한 길이를 나타내고 작업 실행 시간 측정에 적합합니다.
import org.joda.time.*;
import org.joda.time.format.*;
DateTime now = new DateTime();
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
String formatted = fmt.print(now);
DateTime nextWeek = now.plusWeeks(1);
Period period = new Period(now, nextWeek);
int days = period.getDays(); // 일 = 7
Interval interval = new Interval(now, nextWeek);
boolean contains = interval.contains(now); // 참
java.time 패키지(JSR-310)는 동일한 저자인 Stephen Colebourne이 개발하였고 10년의 운영 경험을 바탕으로 Joda-Time의 가장 좋은 아이디어를 포함했습니다. 둘 모두 불변 클래스를 사용하고 시간대를 지원하며 매우 다양한 작업을 제공합니다. 그러나 java.time은 Java 표준 라이브러리에 완전히 통합되어 있고 외부 의존성이 필요 없으며 더 일관된 명명 시스템을 사용합니다. ReadableInstant 대신 Instant, LocalDate 대신 LocalDate(명칭 유지), DateTime 대신 ZonedDateTime을 사용합니다.
| 특징 | Joda-Time | java.time |
|---|---|---|
| 출시된 해 | 2004 | 2014 (Java 8) |
| 상태 | 유지보수 | 활성 개발 |
| 기본 날짜/시간 | DateTime | ZonedDateTime |
| 날짜만 | LocalDate | LocalDate |
| 불변성 | 예 | 예 |
| 포맷 | DateTimeFormatter | DateTimeFormatter |
| 날짜 차이 | Period / Duration | Period / Duration |
java.time은 Joda-Time의 몇 가지 아키텍처 결정을 수정합니다. 처음으로 java.time은 null을 반환하는 메소드와 누락된 값 사이의 혼란을 제거하고 대신 Optional을 사용합니다. 둘째로 대체 달렉 시스템(일본, 태국, 이슬람)에 대한 지원이 Chronology 인터페이스를 통해 추가되었습니다. 셈째로 java.time에는 달렉 없이 UTC 시각을 다루기 위한 Instant 클래스가 포함되어 있어 네트워크 프로토콜과 데이터베이스에서의 통합이 간소화됩니다. Android에서는 API 26(Android 8.0) 이상에서 java.time을 사용할 것을 권장하고 그 이전 버전에서는 desugaring을 통해 사용합니다.
Joda-Time에서 java.time으로의 마이그레이션은 라이브러리 저자가 가능한 안전하고 예측 가능하게 만들껾 노력한 과정입니다. 대부분의 클래스에는 직접 대응하는 것이 있습니다. DateTime → ZonedDateTime, LocalDate → LocalDate(명칭 일치), LocalTime → LocalTime, DateTimeZone → ZoneId, Period → Period, Duration → Duration. 주요 변경 사항은 객체 생성 방식과 메소드 명칭에 관련됩니다. 예를 들면 `now.toDate()` 대신 `Date.from(instant)`를 사용하고 `DateTime.now()`는 `ZonedDateTime.now()`로 대체됩니다.
Joda-Time을 숫은 추상 계층에 격리하여 마이그레이션을 시작할 것을 권장합니다. 이를 통해 전체 코드베이스를 건드리지 않고 라이브러리를 점진적으로 대체할 수 있습니다. 첫 번째 단계는 보통 LocalDate와 LocalTime을 대체하는 것입니다. 이 클래스는 라이브러리 간의 차이가 가장 적습니다. 둘째 단계에서는 DateTime(ZonedDateTime으로)과 시간대를 마이그레이션합니다. 셉째 단계에서는 Interval, Period 그리고 Duration을 대체합니다. 여기서는 일광절약 시간 동작을 까다롭게 확인해야 합니다. 마이그레이션이 완료되면 Joda-Time은 프로젝트 의존성에서 완전히 삭제됩니다.
// Joda-Time (마이그레이션 전)
val dateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"))
val formatted = dateTime.toString("dd.MM.yyyy")
val plusDay = dateTime.plusDays(1)
// java.time (마이그레이션 후)
val zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))
val formatted2 = zonedDateTime.format(
DateTimeFormatter.ofPattern("dd.MM.yyyy")
)
val plusDay2 = zonedDateTime.plusDays(1)
Joda-Time에서 java.time으로 전환을 간소화하는 여러 유틸리티가 있습니다. OpenRewrite(Moderne 제공)는 org.openrewrite.java.migrate.JodaTimeToJavaTime 레시피를 통해 자동 마이그레이션을 지원하며 프로그램 로직을 유지하면서 클래스, 메소드 그리고 임포트를 대체합니다. Gradle 프로젝트의 경우 de.fayard.refreshVersions 플러그인에도 마이그레이션 규칙이 포함되어 있습니다. 수동으로는 IDEA를 사용하여 마이그레이션을 수행하는 것이 가장 쉽습니다. Java Time Migration Assistant 플러그인이 Joda-Time 호출을 하이라이트하고 java.time에서 대응하는 대체를 제안하여 수동 대체에 비해 속도가 2–3배 더 빠릅니다.
Android 애플리케이션에서 Joda-Time을 사용하는 실용적인 예를 살펴보겠습니다. 이 라이브러리는 로케일 표준에 따른 날짜 포맷, 사용자 나이 계산, 이벤트 기간 확정 그리고 두 날짜 사이의 간격 계산에 특히 유용합니다. 모든 예제는 불변 객체를 사용합니다. 각 호출은 새 인스턴스를 반환하고 원본 객체는 그대로 유지됩니다.
// 사용자 나이 계산
val birthDate = LocalDate.parse("1990-05-15")
val today = LocalDate.now()
val age = Years.yearsBetween(birthDate, today).getYears()
// 나이 == 36
// 사용자 로케일로 포맷
val dateTime = DateTime.now()
val formatter = DateTimeFormat.forStyle("MM").withLocale(Locale.getDefault())
val localized = formatter.print(dateTime)
Joda-Time에서 간격을 통해 시간 간격 겹침 확인, 교차 계산 그리고 가장 가까운 이벤트 찾기와 같은 작업을 효율적으로 해결할 수 있습니다. 예를 들어 달렉 애플리케이션을 개발할 때 새 이벤트가 기존 이벤트와 겹치는지 확인할 수 있습니다. Interval 클래스는 contains, overlaps, gap 그리고 abuts 작업을 지원하며 abuts는 간격이 경계에서 망닿는지 확인합니다. Duration은 밀리초 단위의 정확도로 두 시각 사이의 길이를 측정하는 데 사용됩니다.
// 간격 겹침 확인
Interval meeting1 = new Interval(
new DateTime(2026, 7, 21, 10, 0),
new DateTime(2026, 7, 21, 11, 0)
);
Interval meeting2 = new Interval(
new DateTime(2026, 7, 21, 10, 30),
new DateTime(2026, 7, 21, 11, 30)
);
boolean overlaps = meeting1.overlaps(meeting2); // 참
Duration gap = meeting1.gap(meeting2); // 널 (간격 없음)
자주 묻는 질문
Joda-Time은 Java 8이 출시되기 전에 만들어진, Java에서 날짜와 시간을 다루기 위한 라이브러리입니다. 가변성, 0부터 시작하는 월 번호, 완전한 시간대 지원 부재 그리고 비스레드 안전 포맷 등 java.util.Date와 java.util.Calendar의 단점을 해결했습니다.
Java.time(JSR-310)은 동일한 저자가 개발한 Joda-Time의 후속입니다. 주요 차이점은 java.time이 Java 8+ 표준 라이브러리에 통합되어 있고 더 일관된 명명 시스템을 사용하며 대체 달렉 시스템을 지원하고 UTC 시간을 다루기 위한 Instant 클래스를 포함합니다.
아니요. 새 프로젝트에서는 java.time을 사용하는 것이 권장됩니다. Joda-Time은 2014년부터 유지보수 모드이며 더 이상 새 기능이 추가되지 않습니다. Android API 26+의 경우 java.time이 기본으로 사용 가능하고 그 이전 버전에서는 desugaring을 통해 사용할 수 있습니다. API 19+ 장치에서 java.time을 사용할 수 있도록 해주는 메커니즘입니다.
마이그레이션은 단계별로 수행됩니다. Joda-Time을 추상화하고 먼저 LocalDate와 LocalTime을 대체한 다음 DateTime → ZonedDateTime, 그리고 마지막으로 Period, Duration, Interval을 대체합니다. OpenRewrite 도구는 JodaTimeToJavaTime 레시피를 통해 자동 마이그레이션을 지원하여 속도가 2–3배 빠릉니다.
DateTime → ZonedDateTime, LocalDate → LocalDate(일치), LocalTime → LocalTime, LocalDateTime → LocalDateTime, DateTimeZone → ZoneId, Interval → 직접 대응물 없음(Instant + Duration 조합을 사용), Period → Period, Duration → Duration. 메소드 명칭도 비슷합니다. plusDays, minusMonths, withZone은 둘 모두에서 사용 가능합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.