1. 소개

이 빠른 사용방법(예제)에서는 Unix 타임스탬프 에서 날짜 표현을 구문 분석하는 방법을 배웁니다 . Unix 시간 은 1970년 1월 1일 이후 경과된 초 수입니다. 그러나 타임스탬프는 나노초 정밀도까지 시간을 나타낼 수 있습니다. 따라서 사용 가능한 도구를 확인하고 모든 범위의 타임스탬프를 Java 개체로 변환하는 메서드를 만듭니다.

2. 이전 방식(Java 8 이전)

Java 8 이전에는 가장 간단한 옵션이 Date  및 Calendar 였습니다. Date 클래스 에는 밀리초 단위의 타임스탬프를 직접 허용하는 생성자가 있습니다.

public static Date dateFrom(long input) {
    return new Date(input);
}

Calendar 를 사용하면 getInstance() 다음에 setTimeInMillis() 를 호출해야 합니다 .

public static Calendar calendarFrom(long input) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(input);
    return calendar;
}

즉, 입력이 초, 나노초 또는 그 사이의 다른 정밀도인지 알아야 합니다. 그런 다음 타임스탬프를 밀리초로 수동으로 변환해야 합니다.

3. 새로운 방식(자바 8+)

Java 8은 Instant 를 도입 했습니다. 이 클래스에는 초 및 밀리초에서 인스턴스를 만드는 유틸리티 메서드가 있습니다. 또한 그 중 하나는 나노초 조정 매개변수를 허용합니다.

Instant.ofEpochSecond(seconds, nanos);

그러나 우리는 여전히 타임스탬프의 정확성을 미리 알아야 합니다. 예를 들어 타임스탬프가 나노초 단위라는 것을 알고 있는 경우 몇 가지 계산이 필요합니다.

public static Instant fromNanos(long input) {
    long seconds = input / 1_000_000_000;
    long nanos = input % 1_000_000_000;

    return Instant.ofEpochSecond(seconds, nanos);
}

먼저 타임스탬프를 10억으로 나누어 초를 얻습니다. 그런 다음 나머지 를 사용하여 몇 초 후에 부품을 가져옵니다.

4. Instant를 통한 범용 솔루션

추가 작업을 피하기 위해 모든 입력을 대부분의 클래스가 구문 분석할 수 있는 밀리초로 변환할 수 있는 메서드를 만들어 보겠습니다. 먼저 타임스탬프의 범위를 확인합니다. 그런 다음 계산을 수행하여 밀리초를 추출합니다. 또한 과학적 표기법을 사용하여 조건을 더 읽기 쉽게 만들 것입니다.

또한 타임스탬프는 부호 가 있으므로 양수 범위와 음수 범위를 모두 확인해야 합니다(음수 타임스탬프는 1970년부터 거꾸로 계산됨을 의미합니다).

이제 입력이 나노초 단위인지 확인하는 것으로 시작하겠습니다 .

private static long millis(long timestamp) {
    if (millis >= 1E16 || millis <= -1E16) {
        return timestamp / 1_000_000;
    }

    // next range checks
}

먼저 1E16 범위(1 다음에 0이 16개)에 있는지 확인합니다. 음수 값은 1970년 이전 날짜를 나타내므로 이를 확인해야 합니다. 그런 다음 값을 백만으로 나누어 밀리초가 됩니다.

마찬가지로 마이크로초는 1E14 범위에 있습니다. 이번에는 1000으로 나눕니다.

if (timestamp >= 1E14 || timestamp <= -1E14) {
    return timestamp / 1_000;
}

값이 1E11에서 -3E10 범위에 있으면 아무것도 변경할 필요가 없습니다 . 이는 우리의 입력이 이미 밀리초 단위의 정밀도를 가지고 있음을 의미합니다.

if (timestamp >= 1E11 || timestamp <= -3E10) {
    return timestamp;
}

마지막으로 입력이 이러한 범위 중 하나가 아닌 경우 초 단위여야 하므로 이를 밀리초로 변환해야 합니다.

return timestamp * 1_000;

4.1. 인스턴트 입력 정규화

이제 Instant.ofEpochMilli() 를 사용하여 랜덤의 정밀도로 입력에서 Instant 를 반환하는 메서드를 만들어 보겠습니다 .

public static Instant fromTimestamp(long input) {
    return Instant.ofEpochMilli(millis(input));
}

값을 나누거나 곱할 때마다 정밀도가 손실됩니다.

4.2. LocalDateTime 을 사용한 현지 시간

순간순간을 나타냅니다. 그러나 시간대 가 없으면 세계에서 우리의 위치에 따라 달라지므로 쉽게 읽을 수 없습니다. 따라서 현지 시간 표현을 생성하는 메서드를 만들어 보겠습니다. 테스트에서 다른 결과를 피하기 위해 UTC를 사용합니다.

public static LocalDateTime localTimeUtc(Instant instant) {
    return LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
}

이제 방법이 특정 형식을 예상할 때 잘못된 정밀도를 사용하면 완전히 다른 날짜가 어떻게 발생하는지 테스트할 수 있습니다. 먼저 올바른 날짜를 이미 알고 있는 나노초 단위의 타임스탬프를 전달하지만 이를 마이크로초로 변환하고 이전에 생성한 fromNanos() 메서드를 사용합니다.

@Test
void givenWrongPrecision_whenInstantFromNanos_thenUnexpectedTime() {
    long microseconds = 1660663532747420283l / 1000;
    Instant instant = fromNanos(microseconds);
    String expectedTime = "2022-08-16T15:25:32";

    LocalDateTime time = localTimeUtc(instant);
    assertThat(!time.toString().startsWith(expectedTime));
    assertEquals("1970-01-20T05:17:43.532747420", time.toString());
}

이 문제는 이전 하위 섹션에서 만든 fromTimestamp() 메서드 를 사용할 때 발생하지 않습니다 .

@Test
void givenMicroseconds_whenInstantFromTimestamp_thenLocalTimeMatches() {
    long microseconds = 1660663532747420283l / 1000;

    Instant instant = fromTimestamp(microseconds);
    String expectedTime = "2022-08-16T15:25:32";

    LocalDateTime time = localTimeUtc(instant);
    assertThat(time.toString().startsWith(expectedTime));
}

5. 결론

이 기사에서는 핵심 Java 클래스로 타임스탬프를 변환하는 방법을 배웠습니다. 그런 다음 서로 다른 수준의 정밀도를 가질 수 있는 방법과 이것이 결과에 미치는 영향을 확인했습니다. 마지막으로 입력을 정규화하고 일관된 결과를 얻는 간단한 방법을 만들었습니다.

그리고 항상 그렇듯이 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner