날짜/시간 값을 디지털 데이터로 저장하는 방식에는 여러 가지 방식이 있다. 가장 널리 쓰이는 방식으로 Unix Date/Time, Windows Timestamp, MS-DOS Date/Time 등이 있다.
십여 종류 이상 있지만 기본 원리는 크게 2가지이다. (1) 미리 정한 기준 시각과의 시간 차이를 숫자로 바꾸어 표현하는 방식과 (2) 특정 비트마다 시간의 각 요소(년, 월, 일 등)를 의미하도록 지정해놓는 방식이다. 종류 별로 살펴보기로 한다.
(1) 기준 시각과의 시간 차이를 표현하는 방식
1. Unix Date/Time (32 bits)
1970-01-01 00:00:00(UTC)를 기준으로 이 시점과 몇 초만큼 차이가 나는지를 4바이트로 기록한다. 1초가 지날 때마다 값도 1씩 증가한다.
예를 들어, 2016년 2월 11일 오전 9시 정각을 Unix time으로 표현해보자.
기준 시각(1970-01-01 00:00:00)과 36년 1개월 10일 9시간, 즉 1455181200초만큼 차이가 난다. 1455181200을 16진수로 표현하면 56BC4D90이고 이 값이 2016년 2월 11일 오전 9시을 Unix Date/Time을 표현한 값(Big Endian)이다. 바이트 표현방식이 Little Endian이면 904DBC56이다.
4바이트로 표현하기 때문에 시간을 표현하는 데 제약이 있다.(4바이트가 표현할 수 있는 정보 개수는 2^32 = 4294967296개인데 이 방식은 가장 앞 비트가 1인 경우는 제외하기 때문에 최대 2^31 = 2147483648개만 표현할 수 있다.) 나타낼 수 있는 최대값은 (Big Endian으로) 7FFFFFFF이고 Unix Date/Time으로 표현할 수 있는 가장 큰 시각은 2038-01-19 03:14:07이다.
- 시작 시간 : 1970-01-01 00:00:00 [0000000]
- 끝 시간 : 2038-01-19 03:14:07 [7FFFFFFF]
2. HFS+(32 bits)
1904-01-01 00:00:00(UTC)를 기준으로 몇 초만큼 떨어져 있는지를 4바이트로 기록한다.
1번과 마찬가지 방식으로 계산할 수 있고 2016년 2월 11일 오전 9시는 HFS+ 시간 형식으로 D2E1FE10(Big Endian), 10FEE1D2(Little Endian)이다.
Unix Date/Time과 달리 가장 앞 비트가 1인 경우도 포함하므로 최대값은 FFFFFFFF이고 시간으로 표현하면 2040-02-06 06:28:15이다.
3. Windows Timestamp(64 bits)
1601-01-01 00:00:00(UTC)를 기준으로 몇 100ns만큼 떨어져 있는가를 8바이트로 나타낸다. 즉 기준시각에서 100ns가 지날 때마다 1씩 증가한다.
2016년 2월 11일 오전 9시는 1601년1월1일과 13099654800초만큼 떨어져 있으므로 130996548000000000 (100ns)을 16진수로 표현한 1D164AA9657A800이 Big Endian Windows Time이고 00A85796AA64D101이 Little Endian 값이다.
4. Windows Filetime (64 bits)
3번 Windows Time : Big Endian 형식을 4byte 기준으로 뒤집어놓고 사이에 : (또는 .) 을 추가한 방식이다.
예를 들어 Windows Time이 Big Endian으로 01D164AA9657A800이면 Filetime 형식으로 9657A800:01D164AA이다.
(2) 비트마다 시간요소를 지정하는 방식
1. MS-DOS Date/Time(32 bits)
1~4와는 달리 값이 기준시각을 기준으로 현재시각까지의 시간을 숫자로 나타내는 것이 아니라 년, 월, 일 등을 가리키는 bit가 각각 지정되어 있다. 앞 2바이트는 시간(시, 분, 초)를 의미하고, 뒤 2바이트는 (년, 월, 일)을 가리킨다. 시간의 최소 단위는 2초이다.
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
m |
m |
m |
s |
s |
s |
s |
s |
H |
h |
h |
h |
h |
m |
m |
m |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
M |
M |
M |
D |
D |
D |
D |
D |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
M |
32개의 bit를 앞에서부터 0번부터 31번까지라고 하면, 0~15번은 시간에 대한 bit고 16~31번은 날짜에 대한 bit이다. 년도는 1980 + (24~30번(7개) bit로 이루어진 이진수 값), 월은 (31,16~18번(4개) bit로 이루어진 값), 일은 (19~23번(5개) bit로 이루어진 값)으로 계산할 수 있다. 마찬가지로 시는 (8~12번(5개) bit로 이루어진 값), 분은 (13~15,0~2번(6개) bit로 이루어진 값), 초는 2 x (3~7번(5개) bit로 이루어진 값)으로 구할 수 있다.
예를 들어, MS-DOS 시간값이 90484B48로 주어지면, 9048은 시간, 4B48은 날짜와 관련된 값이다. 4B48을 byte 단위에서 뒤집은 484B에서 앞 7비트가 년도, 다음 4비트가 월, 다음 5비트가 일을 가리킨다.(뒤집기 전 4B48을 기준으로 한 비트 위치는 위에서 기술한 값이다) 16진수 484B는 이진수로 0100100/0010/01011이므로 년도는 0100100(2진수) = 36(10진수)에 1980을 더한 2016년이다. 월은 2월, 일은 01011(2) = 11(10)일이다.
마찬가지로 시간 9048을 뒤집은 4890에서 앞 5비트는 시, 다음 6비트는 분, 다음 5비트는 초를 가리킨다. 16진수 4890은 2진수로 01001/000100/10000이므로 시는 01001(2)=9(10)시, 분은 4분, 초는 10000(2)=16에 2를 곱한 32초다.
따라서 90484B48은 2016년 2월 11일 9시 4분 32초를 가리킨다.
각 요소마다 할당된 비트는 년도(2^7=128개), 월(2^4=16개), 일(2^5=32개), 시(2^5=32개), 분(2^6=64개), 초(2^5=32개)이므로 시간을 표현하지 않는 데 쓰이지 않는 값도 존재한다. 월은 16개 중 4개, 일은 32개 중 1개, 시는 32개 중 8개, 분은 4개, 초는 2개가 숫자를 나타내는 데 쓰이지 않는다. 메모리 측면에서 같은 비트로 표현할 수 있는 날짜/시간의 개수가 앞선 방법(1~4)보다 적어서 비효율적이라고 할 수 있다. 장점은 앞의 방법은 시간을 구하기 위해서 시간값을 초로 변환한 뒤 다시 날짜로 변환해야 하는데, 이 방법은 시간 값으로부터 바로 날짜와 시간을 구할 수 있다.
http://www.sandersonforensics.com/forum/content.php?131-A-brief-history-of-time-stamps을 참고했습니다.