System.currentTimeMillis
호스트 컴퓨터에서 시스템 시계를 수동으로 변경하는 것 외에 를 통해 제시된대로 코드에서 또는 JVM 인수로 현재 시간을 재정의하는 방법이 있습니까?
작은 배경 :
현재 날짜 (예 : 월 1 일, 1 년 1 일 등) 동안 많은 논리를 순환하는 여러 회계 작업을 실행하는 시스템이 있습니다.
불행하게도, 기존의 많은 코드는 다음과 같은 기능을 호출 new Date()
또는 Calendar.getInstance()
결국 아래로 전화 둘 다, System.currentTimeMillis
.
테스트 목적으로 지금 당장 시스템 클럭을 수동으로 업데이트하여 테스트 실행 중 코드가 생각하는 시간과 날짜를 조작해야합니다.
그래서 내 질문은 :
에 의해 반환되는 것을 재정의하는 방법이 System.currentTimeMillis
있습니까? 예를 들어, JVM이 해당 메소드에서 리턴하기 전에 오프셋을 자동으로 더하거나 빼도록 지시하려면?
미리 감사드립니다!
답변
내가 강하게 대신 시스템 시계와 장난, 당신은 글 머리 기호 및 레거시 코드가 교체 클럭을 사용하는 것을 리팩토링을 물린 것이 좋습니다. 이상적으로 는 의존성 주입으로 수행해야하지만 교체 가능한 싱글 톤을 사용하더라도 테스트 가능성을 얻을 수 있습니다.
이것은 싱글 톤 버전의 검색 및 대체로 거의 자동화 될 수 있습니다.
- 교체
Calendar.getInstance()
와 함께Clock.getInstance().getCalendarInstance()
. - 교체
new Date()
로Clock.getInstance().newDate()
- 교체
System.currentTimeMillis()
로Clock.getInstance().currentTimeMillis()
(필요에 따라 등)
첫 번째 단계를 수행 한 후에는 싱글 톤을 한 번에 조금씩 DI로 바꿀 수 있습니다.
답변
tl; dr
호스트 컴퓨터에서 시스템 시계를 수동으로 변경하는 것 외에 System.currentTimeMillis를 통해 표시되는 현재 시간을 코드 또는 JVM 인수로 사용하여 현재 시간을 재정의하는 방법이 있습니까?
예.
Instant.now(
Clock.fixed(
Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
)
)
Clock
java.time에서
가짜 날짜-시간 값으로 테스트를 용이하게하기 위해 플러그 가능 클록 교체 문제에 대한 새로운 솔루션이 있습니다. java.time 패키지 에서 자바 (8)는 추상 클래스 포함 java.time.Clock
명시 적 목적을 :
필요에 따라 대체 클럭을 연결할 수 있도록
의 구현을 꽂을 수 Clock
있지만 필요에 맞게 이미 구현 된 것을 찾을 수도 있습니다. 편의상 java.time에는 특수 구현을 생성하는 정적 메소드가 포함되어 있습니다. 이러한 대체 구현은 테스트 중에 유용 할 수 있습니다.
변경된 케이던스
다양한 tick…
방법은 다른 케이던스로 현재 순간을 증가시키는 클럭을 생성합니다.
기본값 은 하드웨어에 따라 Java 8 및 Java 9에서 밀리 초 단위 로 나노초 단위 로 Clock
업데이트 된 시간을보고합니다 . 실제 현재 모멘트를 다른 세분성으로보고하도록 요청할 수 있습니다.
tickSeconds
-초 단위로 증가tickMinutes
-전체 분 단위로 증가tick
-전달 된Duration
인수 에 따라 증가합니다 .
거짓 시계
일부 클럭은 거짓말로 인해 호스트 OS의 하드웨어 클럭과 다른 결과를 생성 할 수 있습니다.
예를 들어, 올해 초 크리스마스 첫 순간에 잠그십시오. 다시 말해서 산타와 그의 순록이 처음으로 멈출 때 . 요즘 가장 빠른 시간대 Pacific/Kiritimati
는 +14:00
입니다.
LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );
항상 같은 순간을 반환하려면 특수 고정 시계를 사용하십시오. 우리는 Kiritimati 에서 크리스마스의 첫 순간을 맞이합니다. UTC 는 12 월 24 일 전일 오전 10 시보 다 일찍 14 시간 일찍 벽시계 시간 을 표시합니다 .
Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );
instant.toString () : 2016-12-24T10 : 00 : 00Z
zdt.toString () : 2016-12-25T00 : 00 + 14 : 00 [태평양 / 키리 마티]
IdeOne.com의 라이브 코드를 참조하십시오 .
실제 시간, 다른 시간대
Clock
구현에서 할당 한 시간대를 제어 할 수 있습니다 . 이것은 일부 테스트에 유용 할 수 있습니다. 그러나 프로덕션 코드에서는 항상 선택 사항 ZoneId
또는 ZoneOffset
인수를 명시 적으로 지정 해야하는 것이 좋습니다 .
UTC를 기본 영역으로 지정할 수 있습니다.
ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );
특정 시간대를 지정할 수 있습니다. 지정 적절한 시간대 이름 의 형식 continent/region
예컨대, America/Montreal
, Africa/Casablanca
, 또는 Pacific/Auckland
. 표준 시간대 가 아니EST
거나 표준화되지 않았으며 고유하지 않은 3-4 문자 약어를 사용하지 마십시오 .IST
ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
JVM의 현재 기본 시간대를 특정 Clock
오브젝트 의 기본값으로 지정할 수 있습니다 .
ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );
이 코드를 실행하여 비교하십시오. 그것들은 모두 타임 라인에서 같은 시점, 같은 시점을보고합니다. 그것들은 벽시계 시간 에서만 다릅니다 . 다시 말해, 같은 것을 말하는 세 가지 방법, 같은 순간을 나타내는 세 가지 방법.
System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );
America/Los_Angeles
이 코드를 실행 한 컴퓨터의 JVM 현재 기본 영역입니다.
zdtClockSystemUTC.toString () : 2016-12-31T20 : 52 : 39.688Z
zdtClockSystem.toString () : 2016-12-31T15 : 52 : 39.750-05 : 00 [미국 / 몬트리올]
zdtClockSystemDefaultZone.toString () : 2016-12-31T12 : 52 : 39.762-08 : 00 [미국 / 로스 앤젤레스]
Instant
클래스는 정의에 의해 UTC 항상. 따라서이 세 가지 영역 관련 Clock
사용법은 동일한 효과를 갖습니다.
Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );
instantClockSystemUTC.toString () : 2016-12-31T20 : 52 : 39.763Z
instantClockSystem.toString () : 2016-12-31T20 : 52 : 39.763Z
instantClockSystemDefaultZone.toString () : 2016-12-31T20 : 52 : 39.763Z
기본 시계
에 기본적으로 사용되는 구현 Instant.now
은에서 반환 된 구현 Clock.systemUTC()
입니다. 이것은를 지정하지 않을 때 사용되는 구현 Clock
입니다. 시험판 Java 9 소스 코드를Instant.now
참조하십시오 .
public static Instant now() {
return Clock.systemUTC().instant();
}
기본 Clock
에 OffsetDateTime.now
와 ZonedDateTime.now
있다 Clock.systemDefaultZone()
. 소스 코드를 참조하십시오 .
public static ZonedDateTime now() {
return now(Clock.systemDefaultZone());
}
기본 구현의 동작은 Java 8과 Java 9 사이에서 변경되었습니다. Java 8 에서 클래스의 나노초 분해능 저장 기능에도 불구하고 현재 순간은 밀리 초 단위 의 해상도로 캡처됩니다 . Java 9는 물론 컴퓨터 하드웨어 시계의 기능에 따라 나노초의 해상도로 현재 순간을 캡처 할 수있는 새로운 구현을 제공합니다.
java.time에 대하여
java.time의 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 까다로운 기존에 대신 기존 과 같은 날짜 – 시간의 수업을 java.util.Date
, Calendar
, SimpleDateFormat
.
자세한 내용은 Oracle Tutorial을 참조하십시오 . 많은 예제와 설명을 보려면 스택 오버플로를 검색하십시오. 사양은 JSR 310 입니다.
Joda 타임 프로젝트는 지금에 유지 관리 모드 의로 마이그레이션을 조언 java.time의 클래스.
java.time 객체를 데이터베이스와 직접 교환 할 수 있습니다 . JDBC 4.2 이상을 준수 하는 JDBC 드라이버를 사용하십시오 . 문자열이 필요없고 수업이 필요 없습니다 . Hibernate 5 & JPA 2.2는 java.time을 지원 합니다 .java.sql.*
java.time 클래스는 어디서 구할 수 있습니까?
- Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11 이상 -번들로 구현 된 표준 Java API의 일부입니다.
- Java 9 에는 몇 가지 사소한 기능과 수정 사항이 있습니다.
- Java SE 6 및 Java SE 7
- 대부분의 java.time 기능은 ThreeTen-Backport의 Java 6 & 7로 백 포트됩니다 .
- 기계적 인조 인간
- java.time 클래스 의 최신 버전의 Android (26+) 번들 구현 .
- 이전 Android (<26)의 경우 API desugaring 이라는 프로세스 는 원래 Android에 내장되지 않은 java.time 기능 의 하위 세트를 가져옵니다 .
- desugaring이 필요한 것을 제공하지 않으면 ThreeTenABP 프로젝트 는 위에서 언급 한 ThreeTen-Backport 를 Android에 맞게 조정합니다 . ThreeTenABP 사용 방법…을 참조하십시오 .
답변
으로는 존 소총 말했다 :
“Joda Time 사용”은 “java.util.Date/Calendar를 사용하여 X를 어떻게 달성합니까?”
그래서 여기에 간다 (당신이 모든 것을 ( new Date()
을 new DateTime().toDate()
) 로 대체했다고 가정 )
//Change to specific time
DateTimeUtils.setCurrentMillisFixed(millis);
//or set the clock to be a difference from system time
DateTimeUtils.setCurrentMillisOffset(millis);
//Reset to system time
DateTimeUtils.setCurrentMillisSystem();
인터페이스가있는 라이브러리를 가져 오려면 (아래 Jon의 주석 참조) 표준 인터페이스뿐만 아니라 구현을 제공하는 Prevayler ‘s Clock을 사용할 수 있습니다. 전체 병은 96kB에 불과하므로 은행을 파기해서는 안됩니다 …
답변
일부 DateFactory 패턴을 사용하는 것은 좋지만 제어 할 수없는 라이브러리는 다루지 않습니다 .System.currentTimeMillis에 의존하는 구현으로 유효성 검사 주석 @Past를 상상하십시오 (그렇습니다).
그래서 우리는 jmockit을 사용하여 시스템 시간을 직접 조롱합니다.
import mockit.Mock;
import mockit.MockClass;
...
@MockClass(realClass = System.class)
public static class SystemMock {
/**
* Fake current time millis returns value modified by required offset.
*
* @return fake "current" millis
*/
@Mock
public static long currentTimeMillis() {
return INIT_MILLIS + offset + millisSinceClassInit();
}
}
Mockit.setUpMock(SystemMock.class);
원래의 조롱되지 않은 millis 값을 얻을 수 없기 때문에 대신 나노 타이머를 사용합니다. 이는 벽시계와 관련이 없지만 여기에서 상대적 시간은 충분합니다.
// runs before the mock is applied
private static final long INIT_MILLIS = System.currentTimeMillis();
private static final long INIT_NANOS = System.nanoTime();
private static long millisSinceClassInit() {
return (System.nanoTime() - INIT_NANOS) / 1000000;
}
HotSpot을 사용하면 여러 번의 호출 후 시간이 정상으로 돌아온다는 문서화 된 문제가 있습니다. 다음은 문제 보고서입니다. http://code.google.com/p/jmockit/issues/detail?id=43
이를 극복하기 위해이 인수를 사용하여 하나의 특정 HotSpot 최적화 실행 JVM을 설정해야 -XX:-Inline
합니다.
이는 프로덕션에는 적합하지 않지만 테스트에는 적합하며 특히 DataFactory가 비즈니스에 적합하지 않고 테스트로 인해 도입 된 경우에는 응용 프로그램에 절대적으로 투명합니다. 내장 된 JVM 옵션을 다른 시간에 실행하는 것이 좋을 것입니다. 너무 나빠서 이와 같은 해킹이 없으면 불가능합니다.
전체 기사는 내 블로그 게시물 ( http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/)에 있습니다.
완벽한 편리한 클래스 SystemTimeShifter가 게시물에 제공됩니다. 테스트에서 클래스를 사용하거나 다른 시간에 응용 프로그램 (또는 전체 응용 프로그램 서버)을 실행하기 위해 실제 메인 클래스보다 첫 번째 메인 클래스로 매우 쉽게 사용할 수 있습니다. 물론 이것은 프로덕션 환경이 아니라 주로 테스트 목적으로 사용됩니다.
2014 년 7 월 편집 : JMockit은 최근에 많이 바뀌었고 이것을 올바르게 사용하려면 JMockit 1.0을 사용해야합니다 (IIRC). 인터페이스가 완전히 다른 최신 버전으로 확실히 업그레이드 할 수 없습니다. 나는 필요한 것들을 인라인하는 것에 대해 생각하고 있었지만, 새로운 프로젝트에서 이것을 필요로하지 않기 때문에 나는 이것을 전혀 개발하지 않고 있습니다.
답변
답변
Aspect-Oriented Programming (예 : AspectJ)을 사용하여 System 클래스를 짜서 테스트 케이스 내에서 설정할 수있는 사전 정의 된 값을 리턴하십시오.
또는 응용 프로그램 클래스를 짜서 호출을 System.currentTimeMillis()
또는로 리디렉션new Date()
자신의 또 다른 유틸리티 클래스.
직조 시스템 클래스 (java.lang.*
그러나 )는 조금 더 까다롭기 때문에 rt.jar에 대해 오프라인 직조를 수행하고 테스트에 별도의 JDK / rt.jar을 사용해야 할 수도 있습니다.
이진 직조 라고 하며 시스템 클래스 직조를 수행하고 그 문제를 피하기위한 특수 도구 도 있습니다 (예 : VM 부트 스트래핑이 작동하지 않을 수 있음)
답변
실제로 VM에서 직접이 작업을 수행 할 수있는 방법은 없지만 테스트 머신에서 프로그래밍 방식으로 시스템 시간을 설정할 수 있습니다. 대부분의 (모든?) OS에는이를위한 명령 줄 명령이 있습니다.