Date 객체를 활용하면 생성 및 수정 시간을 저장하거나 시간을 측정할 수 있고, 현재 날짜를 출력하는 용도 등으로 활용할 수 있다.
new Date()
인수 없이 호출하면 현재 날짜와 시간이 저장된 Date 객체가 반환된다.
new Date(milliseconds)
UTC 기준(UTC+0) 1970년 1월 1일 0시 0분 0초에서 milliseconds 밀리초(1/1000 초) 후의 시점이 저장된 Date 객체가 반환된다.
1970년의 첫날을 기준으로 흘러간 밀리초를 나타내는 정수는 타임스탬프(timestamp)라고 부른다.
타임스탬프를 사용하면 날짜를 숫자 형태로 간편하게 나타낼 수 있다.
new Date(timestamp)를 사용해 타임스탬프를 사용해 특정 날짜가 저장된 Date 객체를 손쉽게 만들 수 있고 date.getTime() 메서드를 사용해 Date 객체에서 타임스탬프를 추출하는 것도 가능하다.
1970년 1월 1일 이전 날짜에 해당하는 타임스탬프 값은 음수이다. 예시를 살펴보자.
new Date(datestring)
인수가 하나인데, 문자열이라면 해당 문자열은 자동으로 구문 분석(parsed)된다.
구문 분석에 적용되는 알고리즘은 Date.parse에서 사용하는 알고리즘과 동일하다.
new Date(year, month, date, hours, minutes, seconds, ms)
주어진 인수를 조합해 만들 수있는 날짜가 저장된 객체가 반환된다.(지역 시간대 기준), 첫 번째와 두 번째 인수만 필수값이다.
최소 정밀도는 1밀리초(1/1000초)이다.
getFullYear()
연도(네 자릿수)를 반환한다.
getMonth()
월을 반환한다.(0이상 11이하).
getDate()
일을 반환한다. (1이상 31이하)
getHours(), getMinutes(), getSeconds(), getMilliseconds()
시, 분, 초, 밀리초를 반환한다.
getDay()
일요일을 나타내는 0부터 토요일을 나타내는 6까지의 숫자 중 하나를 반환한다.
몇몇 나라에서 요일의 첫날이 일요일이 아니긴 하지만, getDay에선 항상 0이 일요일을 나타낸다. 이를 변경할 방법은 없다.
위에서 소개된 메서드 모두는 현지 시간 기준 날짜 구성요소를 반환한다.
위 메서드 이름에 있는 'get' 다음에 'UTC'를 붙여주면 표준시(UTC+0) 기준의 날짜 구성 요소를 반환해주는 메서드 getUTCFullYear(), getUTCMonth(), getUTCDay()를 만들 수 있다.
현지 시간대가 UTC 시간대와 다르다면 아래 예시를 실행했을 때 얼럿창엔 다른 값이 출력된다.
아래 두 메서드는 위에서 소개한 메서드와 달리 표준시(UTC+0) 기준의 날짜 구성 요소를 반환해주는 메서드가 없다.
getTime()
주어진 일시와 1970년 1월 1일 00시 00분 00초 사이의 간격(밀리초 단위)인 타임스탬프를 반환한다.
getTimezoneOffset()
현지 시간과 표준 시간의 차이(분)을 반환한다.
setTime()를 제외한 모든 메서드는 setUTCHours() 같이 표준시에 따라 날짜 구성 요소를 설정해주는 메서드가 있다.
setHours와 같은 메서드는 여러 개의 날짜 구성요소를 동시에 설정할 수 있는데, 메서드의 인수에 없는 구성요소는 변경되지 않는다.
입력받은 날짜 구성요소가 범위를 벗어나면 초과분은 자동으로 다른 날짜 구성요소에 배분된다.
'2016년 2월 28일'의 이틀 뒤 날짜를 구하고 싶다고 가정해보자.
답은 3월 2일 혹은 3월 1일(윤년)이 될 텐데, 2016년이 윤년인지 아닌지 생각할 필요 없이 단순히 이틀을 더해주기만 하면 답을 구할 수 있다.
나머지 작업은 Date 객체가 알아서 처리해 주기 때문이다.
0이나 음수를 날짜 구성요소에 설정하는 것도 가능하다.
이를 응용하면 날짜에 마이너스 연산자를 적용해 밀리초 기준 시차를 구할 수 있다.
시차를 측정해 나만의 스톱워치를 만들어보자.
현재 타임스탬프를 반환하는 메서드 Date.now()를 응용하면 된다.
Date.now()는 new Date().getTime() 과 의미론적으로 동일하지만 중간에 Date객체를 만들지 않는다는 점이 다르다. 따라서 new Date().getTime()를 사용하는 것보다 빠르고 가비지 컬렉터의 일을 덜어준다는 장점이 있다.
자바스크립트를 사용해 게임을 구현한다던가 전문분야의 애플리케이션을 구현할 때와 같이 성능이 중요한 경우에 Date.now()가 자주 활용된다. 물론 편의를 위해서도 활용된다.
위 예시를 Date.now()를 사용해 변경하면 성능이 더 좋다.
두 함수는 완전히 동일한 작업을 수행하지만, 한 함수는 날짜를 밀리초 단위로 얻기 위해 date.getTime()를 사용하고 있고, 다른 함수는 마이너스 연산자 적용 시 객체가 숫자형으로 변화한다는 특징을 사용하고 있다. 두 함수가 변환하는 값은 항상 동일하다.
속도는 어떨까
연속해서 함수를 아주 많이 호출한 후, 실제 연산이 종료되는 데 걸리는 시간을 비교하면 두 함수의 성능을 비교할 수 있을 것이다. diffSubtract와 diffGetTime은 아주 간단한 함수이기 때문에 유의미한 시차를 구하려고 하면 각 함수를 최소한 십만 번 호출해야한다.
측정을 해보자.
형 변환이 없어서 엔진 최적화에 드는 자원이 줄어들므로 getTime()을 이용한 방법이 훨씬 빠르다.
자, 벤치마크로 쓸 뭔가를 구하긴했다. 그런데 이 벤치마크는 그다지 좋은 벤치마크가 이니다.
bench(diffSubtract)를 실행하고 있을 때 CPU가 어떤 작업을 병렬적으로 처리하고 있고, 여기에 CPU의 자원이 투입되고 있었다고 가정해보자. 그리고 bench(diffGetTime)를 실행할 땐, 이 작업이 끝난 상태라고 가정해보자.
멀티 프로세스를 지원하는 운영체계에서 이런 시나리오는 흔히 발생한다.
첫 번째 benchmark가 실행될 땐 사용할 수 있는 CPU 자원이 적었기 때문에 이 벤치마크는 좋지 않다.
좀 더 신뢰할만한 벤치마크 테스트를 만들려면 benchmark를 번갈아 가면서 여러 번 돌려야한다.
모던 자바스크립트 엔진은 아주 많이 실행된 코드인 'hot code'를 대상으로 최적화를 수행한다. (실행 횟수가 적은 코드는 최적화할 필요가 없다.) 위 예시에서 bench를 첫 번째 실행했을 때는 최적화가 잘 적용되지 않기 때문에 아래 코드 처럼 메인 반복문을 실행하기 전에 예열용(heat-up)으로 bench를 실행할 수 있다.
참고하면 좋은 글
http://mrale.ph 에서 V8엔진을 설명한 좋은 글들을 볼 수 있다.
YYYY-MM-DD, YYYY-MM, YYYY 같이 더 짧은 문자열 형식도 가능하다.
위 조건을 만족하는 문자열을 대상으로 Date.parse(str)를 호출하면 문자열과 대응하는 날짜의 타임스탬프가 반환된다. 문자열의 형식이 조건에 맞지 않은 경우엔 NaN이 반환된다.
Date.parse(str)를 이용하면 타임스탬프만으로도 새로운 Date 객체를 바로 만들 수 있다.