날짜와 시간이 포함 된 문자열을 특정 시점으로 구문 분석하는 것은 (Java에서 " Instant
"라고 함) 매우 복잡합니다. Java는 이것을 여러 번 반복하여 다루어 왔습니다. 최신 하나 java.time
와 java.time.chrono
, 커버 거의 모든 요구 (제외 시간 팽창 시킴 :)).
그러나 이러한 복잡성은 많은 혼란을 초래합니다.
날짜 구문 분석을 이해하는 핵심은 다음과 같습니다.
왜 Java가 날짜를 파싱하는 방법이 많은가?
- 시간을 측정하기위한 여러 시스템이 있습니다. 예를 들어, 역사적인 일본 달력은 각 황제 또는 왕조의 통치 기간에서 파생되었습니다. 그런 다음 UNIX 타임 스탬프가 있습니다. 다행히도 (비즈니스) 전 세계가 같은 것을 사용했습니다.
- 역사적으로 시스템은 여러 가지 이유로 전환되었습니다 . 예를 들어 율리우스 력에서 1582 년 그레고리력까지. 따라서 '서부'날짜 이전에는 다르게 처리해야합니다.
- 물론 변경 사항은 한 번에 발생하지 않았습니다. 달력은 일부 종교와 유럽의 다른 지역의 헤드 쿼트에서 나왔기 때문에 다른식이 요법을 믿었습니다. 예를 들어 독일은 1700 년까지 전환하지 않았습니다.
... 그리고 왜 LocalDateTime
, ZonedDateTime
외. 너무 복잡한
있다 시간대 . 표준 시간대는 기본적으로 지구 표면의 "스트라이프" * [1] 이며, 해당 기관은 시간 오프셋이있는 시점과 동일한 규칙을 따릅니다. 여기에는 여름 시간 규칙이 포함됩니다.
시간대는 다양한 영역에 대해 시간이 지남에 따라 변경되며, 주로 누가 정복했는지에 따라 달라집니다. 한 시간대의 규칙도 시간이 지남에 따라 변경 됩니다.
시간 오프셋이 있습니다. 표준 시간대는 "프라하"일 수 있지만 여름 시간 오프셋과 겨울 시간 오프셋이 있기 때문에 표준 시간대와 동일하지 않습니다.
시간대가 포함 된 타임 스탬프를 받으면 해당 연도의 일부에 따라 오프셋이 달라질 수 있습니다. 윤기 시간 동안 타임 스탬프는 두 번 다른 시간을 의미 할 수 있으므로 추가 정보 없이는 신뢰할 수 없습니다. 변환.
참고 : 타임 스탬프 란 "선택적으로 시간대 및 / 또는 시간 오프셋이있는 날짜 및 / 또는 시간을 포함하는 문자열"을 의미합니다.
여러 시간대는 특정 기간 동안 동일한 시간 오프셋을 공유 할 수 있습니다. 예를 들어 서머 타임 오프셋이 적용되지 않는 경우 GMT / UTC 시간대는 "런던"시간대와 동일합니다.
좀 더 복잡하게 만들려면 (그러나 사용 사례에 그렇게 중요하지는 않습니다) :
- 과학자들은 지구의 역 동성을 관찰합니다. 이를 바탕으로 그들은 개별 연도 말에 초를 추가합니다. (그래서
2040-12-31 24:00:00
유효한 날짜 - 시간이 될 수 있습니다.) 이것은 메타 데이터를 정기적으로 업데이트해야 시스템이 바로 날짜 변환을 위해 사용하는. 예를 들어 Linux에서는 이러한 새 데이터를 포함하여 Java 패키지를 정기적으로 업데이트합니다.
업데이트가 항상 과거 및 미래 타임 스탬프 모두에 대해 이전 동작을 유지하지는 않습니다. 따라서 일부 시간대 변경을 기준으로 두 타임 스탬프를 구문 분석 하면 서로 다른 버전의 소프트웨어에서 실행될 때 다른 결과를 얻을 수 있습니다. 영향을받는 시간대와 다른 시간대를 비교하는 데에도 적용됩니다.
이로 인해 소프트웨어에 버그가 발생하면 UNIX 타임 스탬프 와 같이 복잡한 규칙이없는 타임 스탬프를 사용하는 것이 좋습니다.
미래 날짜의 경우 7로 인해 날짜를 정확하게 변환 할 수 없습니다. 예를 들어 현재 구문 분석은 8524-02-17 12:00:00
향후 구문 분석에서 몇 초가 걸릴 수 있습니다.
이를위한 JDK의 API는 현대의 요구와 함께 발전했습니다.
- 초기 Java 릴리스에는
java.util.Date
연도, 월, 일 및 시간이 있다고 가정하고 약간 순진한 접근 방식이있었습니다. 이것은 빨리 충분하지 않았다.
- 또한 데이터베이스의 요구 사항이 다르기 때문에
java.sql.Date
자체 제한 사항이 있기 때문에 매우 초기 에 도입되었습니다.
- 다른 캘린더와 시간대를 잘 다루지 않았으므로
Calendar
API가 도입되었습니다.
- 이것은 여전히 표준 시간대의 복잡성을 다루지 않았습니다. 그러나 위 API의 혼합은 실제로 작업하기가 어려웠습니다. 따라서 Java 개발자가 글로벌 웹 응용 프로그램 작업을 시작하면서 JodaTime과 같은 대부분의 사용 사례를 대상으로하는 라이브러리가 빠르게 인기를 얻었습니다. JodaTime은 약 10 년 동안 사실상의 표준이었습니다.
- 그러나 JDK는 JodaTime과 통합되지 않았으므로 작업이 약간 번거로 웠습니다. 따라서 JSR-310 은이 문제에 접근하는 방법에 대한 매우 긴 토론 끝에 주로 JodaTime을 기반 으로 만들어졌습니다 .
Java에서 처리하는 방법 java.time
타임 스탬프를 구문 분석 할 유형 결정
타임 스탬프 문자열을 사용할 때는 어떤 정보가 들어 있는지 알아야합니다. 이것이 중요한 포인트입니다. 이 권한을 얻지 못하면 "즉시 만들 수 없음"또는 "영역 오프셋 누락"또는 "알 수없는 영역 ID"등과 같은 예외적 인 예외가 발생합니다.
날짜와 시간이 포함되어 있습니까?
시간 오프셋이 있습니까?
시간 오프셋이 +hh:mm
부품입니다. 경우에 따라 '줄루 시간', 표준시 또는 그리니치 표준시 +00:00
로 대체 될 수 있습니다 . 또한 시간대를 설정합니다.
이 타임 스탬프에는을 사용 합니다.Z
UTC
GMT
OffsetDateTime
시간대가 있습니까?
이 타임 스탬프에는을 사용 ZonedDateTime
합니다.
영역은 다음 중 하나로 지정됩니다.
- 이름 ( "프라하", "태평양 표준시", "PST") 또는
- "zone ID"( "America / Los_Angeles", "Europe / London")는 java.time.ZoneId로 표시됩니다 .
시간대 목록은 ICAAN이 지원 하는 "TZ 데이터베이스"에 의해 컴파일됩니다 .
ZoneId
의 javadoc 에 따르면 영역 ID를 어떻게 든 지정 Z
하고 오프셋 으로 지정할 수 있습니다 . 이것이 실제 영역에 어떻게 매핑되는지 잘 모르겠습니다. TZ 만있는 타임 스탬프가 윤초 시간 오프셋 변경에 속하면 모호하며 해석이 적용 ResolverStyle
됩니다 (아래 참조).
이없는 경우 누락 된 컨텍스트가 가정되거나 무시됩니다. 그리고 소비자는 결정해야합니다. 따라서 누락 된 정보를 추가하여 구문 분석 LocalDateTime
하고 변환해야 OffsetDateTime
합니다.
- UTC 시간 이라고 가정 할 수 있습니다 . UTC 오프셋을 0 시간으로 추가하십시오.
- 전환이 발생한 시점 이라고 가정 할 수 있습니다 . 시스템 시간대를 추가하여 변환하십시오.
- 무시 하고 그대로 사용할 수 있습니다 . 예를 들어 두 번 비교 또는 빼거나 () 참조
Duration
하거나 모르거나 실제로 중요하지 않은 경우 (예 : 로컬 버스 일정)에 유용합니다 .
부분 시간 정보
- 타임 스탬프가 포함 된 내용을 바탕으로, 당신이 취할 수
LocalDate
, LocalTime
, OffsetTime
, MonthDay
, Year
, 또는 YearMonth
그것에서.
전체 정보가 있으면를 얻을 수 있습니다 java.time.Instant
. 또한 내부적으로 OffsetDateTime
와 사이를 변환하는 데 사용됩니다 ZonedDateTime
.
그것을 파싱하는 방법을 알아 내십시오.
DateTimeFormatter
타임 스탬프 문자열과 형식을 문자열로 구문 분석 할 수 있는 광범위한 설명서 가 있습니다.
미리 만들어진 DateTimeFormatter
들 더보기 간략히 모든 표준 타임 스탬프 형식을 포함해야한다. 예를 들어, ISO_INSTANT
구문 분석 할 수 있습니다 2011-12-03T10:15:30.123457Z
.
특별한 형식이있는 경우 고유 한 DateTimeFormatter (파서이기도 함)를 만들 수 있습니다 .
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
의 소스 코드를보고를 사용하여 소스 코드를 DateTimeFormatter
작성하는 방법에 대해 영감을 얻는 것이 좋습니다 DateTimeFormatterBuilder
. 또한 ResolverStyle
파서가 형식 및 모호한 정보에 대해 LENIENT, SMART 또는 STRICT인지 여부를 제어 하는 방법도 살펴보십시오 .
임시 접근 자
이제 빈번한 실수는의 복잡성에 들어가는 것입니다 TemporalAccessor
. 개발자가 작업하는 방식에서 비롯된 것입니다 SimpleDateFormatter.parse(String)
. 오른쪽, DateTimeFormatter.parse("...")
당신을 제공합니다 TemporalAccessor
.
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
그러나 이전 섹션의 지식을 갖추고 있으면 필요한 유형으로 편리하게 구문 분석 할 수 있습니다.
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
실제로 DateTimeFormatter
어느 쪽도 필요하지 않습니다 . 구문 분석하려는 유형에는 parse(String)
메소드가 있습니다.
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
와 관련 TemporalAccessor
하여 문자열에 어떤 정보가 있는지 모호하고 런타임에 결정하려는 경우 사용할 수 있습니다.
나는 당신의 영혼에 대한 이해의 빛을 비추기를 바랍니다 :)
참고 : java.time
Java 6 및 7 : BackTen-Backport 백 포트가 있습니다 . 안드로이드에는 ThreeTenABP가 있습니다.
[1] 그것들은 줄무늬가 아니라 이상한 극단도 있습니다. 예를 들어, 일부 인접한 태평양 섬 에는 +14 : 00 및 -11 : 00 시간대가 있습니다. 즉, 한 섬에는 5 월 1 일 오후 3시, 다른 섬에는 아직 4 월 12 일 오후 4시 30 분이 있습니다 (정확하게 계산 된 경우).
ZonedDateTime
오히려 이상을LocalDateTime
. 이름은 반 직관적입니다. 는Local
의미 있는 일반 지역이 아닌 특정 시간대를. 따라서LocalDateTime
개체는 타임 라인에 연결되지 않습니다. 타임 라인에서 특정 순간을 얻으려면 의미를 가지려면 시간대를 적용해야합니다.