코딩을 하다보면 시스템 시간과 날짜가 필요한 경우가 많습니다.
보통 로깅을 하기 위해서죠. 다행히 Windows API에서 시스템 시간과 날짜를 가져오는 interface를 제공합니다.
SYSTEMTIME은 아래와 같이 정의되어 있어요
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
}SYSTEMTIME, *PSYSTEMTIME;
SYSTEMTIME을 사용하기 위해서는 #include <windows.h>를 해 줘야 해요.
1. GetSystemTime()
GetSystemTime은 UTC 시간을 가져옵니다.
UTC 시간은, 원자시계를 기준으로 한 표준시로써, 1972년 1월 1일부터 시행된 국제 표준시 입니다.
한국 시간으로는 KST(Korean Standard Time) UTC + 9:00 이 됩니다. UTC 시간에 9시간을 더하라.
라는 뜻이죠.
2. GetLocalTime()
GetLocalTime()은 현재 PC에 세팅되어 있는 시간을 가져옵니다.
우리는 GetLocalTime을 가져오는 것이 로깅 시간과 맞기 때문에 많이 사용됩니다.
SYSTEMTIME을 사용하기 위해 windows.h 을 추가했고,
WORD -> std::string 을 하기 위해(to_string()) string.h 을 추가했어요.
#include <iostream>
#include <windows.h>
#include <string>
enum Week
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
int main()
{
SYSTEMTIME st;
GetLocalTime(&st);
std::string strYear = std::to_string(st.wYear);
std::string strMonth = std::to_string(st.wMonth);
std::string strDay = std::to_string(st.wDay);
std::cout << "현재 날짜:"<< strYear <<"년 "<< strMonth<<"월 "<< strDay<<"일 ";
switch (st.wDayOfWeek)
{
case Sunday:
std::cout << "일요일" << std::endl;
break;
case Monday:
std::cout << "월요일" << std::endl;
break;
case Tuesday:
std::cout << "화요일" << std::endl;
break;
case Wednesday:
std::cout << "수요일" << std::endl;
break;
case Thursday:
std::cout << "목요일" << std::endl;
break;
case Friday:
std::cout << "금요일" << std::endl;
break;
case Saturday:
std::cout << "토요일" << std::endl;
break;
}
std::string strHour = std::to_string(st.wHour);
std::string strMinute = std::to_string(st.wMinute);
std::string strSecond = std::to_string(st.wSecond);
std::cout << "현재 시간:" << strHour << ":" << strMinute << ":" << strSecond << std::endl;
return 0;
}
enum 대신 enum class 를 사용하는 것을 권장하나 여기에서는 간단한 예제이니 쉽게 하려고 enum을 사용했어요.
실행 결과, 아래와 같이 현재 시간과 날짜를 출력하실 수 있습니다.
혹시 windows.h를 추가했을 때 "byte": 모호한 기호입니다.
에러가 발생하게 되면 C++17에서 추가된 std::byte와 windows.h의 byte와 정의 중복으로 발생한 것으로
프로젝트 속성 >> c/c++전처리기 정의 부분에 _HAS_STD_BYTE=0 을 추가하시면 됩니다.
띄어쓰기 조심하셔야 합니다.
이번에는 현재 시간을 알아내는 방법에 대해 알아보겠습니다.
현재 시간을 알아내는 방법은 크게 두가지가 있습니다.
(1) time_t 사용하는 방법
(2) chrono 사용하는 방법
하나씩 알아보겠습니다.
time_t 사용하는 방법
c언어에서는 time.h에, c++ 언어에서는 ctime에 typedef를 통해 정의되어 있어요.
즉, #include <ctime>을 해주셔야 해요. 유닉스와 POSIX 시스템에서는 time_t 를 정수 혹은 부동소수점으로
정의하고 있어요.
이 때 값은 1970년 1월 1일 자정(UTC)에서부터 현재까지 흐른 초수를 의미합니다.
현재 많은 경우 time_t 형을 32비트 정수 형으로 잡고 있는데, 2038년이 되면 32비트 형에서 오버플로우가 발생하여 64비트가 생겨났어요. 2038년 이전까지만 사용하실 꺼면, time_t 를 사용하셔도 되지만 그 이후까지 사용하실꺼면, __time64_t를 사용하시면 됩니다.
// 'time()' 은 시스템의 현재 시간을 'time_t' type으로 반환해요
std::time_t now1 = std::time(nullptr);
tm tm_1;
// localtime은 'time_t' 값을 달력 시간으로 변환하고
// 멤버가 있는 'tm' 구조체에 대한 포인터를 반환합니다.
localtime_s(&tm_1, &now1);
std::cout << "현재 날짜:" << tm_1.tm_mday << "/" << (tm_1.tm_mon + 1) << "/" << (tm_1.tm_year + 1900) << std::endl;
std::cout << "현재 시간:" << tm_1.tm_hour << ":" << tm_1.tm_min << ":" << tm_1.tm_sec << std::endl;
std::cout << "현재 요일:" << tm_1.tm_wday << std::endl; // 일요일: 0 ~ 토요일: 6
std::cout << "올해 몇번째 날:" << tm_1.tm_yday << std::endl; // 1/1 : 0, 1/2 : 1, ...
std::cout << "서머타임 적용 여부:" << tm_1.tm_isdst << std::endl; // 0이면 서머타임 없음
__time64_t now2 = _time64(nullptr);
tm tm_2;
gmtime_s(&tm_2, &now2);
std::cout << "세계 표준 날짜:" << tm_2.tm_mday << "/" << (tm_2.tm_mon + 1) << "/" << (tm_2.tm_year + 1900) << std::endl;
std::cout << "세계 표준 시간:" << tm_2.tm_hour << ":" << tm_2.tm_min << ":" << tm_2.tm_sec << std::endl;
char cStrfTime[64];
strftime(cStrfTime, 64, "%Y년 %m월 %d일 %H시 %M분 %S초\n", &tm_2);
printf(cStrfTime);
위 코드 실행 결과는 아래와 같아요.
일부러 세계시간과 우리나라 시간의 날짜가 변동되도록 해서 12시 이후에 돌려본건데.
우리나라가 세계 표준시보다 9시간 빠르죠? 64비트 버전에서도 같은 방법으로 localtime_s를 사용하면
우리나라 local time을 얻어올 수 있어요.
아참. struct tm의 연도에는 +1900을 달에는 +1을 해줘야 해요. tm 구조체 변수 목록은 아래와 같아요
변수
|
의미
|
실행 시점의 값
(localtime_s) |
실행 시점의 값
(gmtime_s) |
tm_year
|
년(1900년 이후 경과)
|
123
|
123
|
tm_mon
|
월(0~11)
|
2
|
2
|
tm_mday
|
일(1~31)
|
10
|
9
|
tm_hour
|
시(24시)
|
0
|
15
|
tm_min
|
분
|
10
|
10
|
tm_sec
|
초
|
36
|
36
|
tm_wday
|
요일(0~6)
|
5
|
4
|
tm_yday
|
연도의 경과 일수
|
68
|
67
|
tm_isdst
|
서머 타임
|
0
|
0
|
strftime의 포맷 목록은 아래와 같아요. (대소문자를 주의하세요!)
포맷
|
의미
|
실행 시점의 값
(gmtime_s) |
%Y
|
연도
|
2023
|
%m
|
월(01~12)
|
03
|
%B
|
영어 월(풀네임)
|
March
|
%b, %h
|
영어 월(줄임말)
|
Mar
|
%A
|
영어 요일(풀네임)
|
Thursday
|
%a
|
영어 요일(줄임말)
|
Thu
|
%d
|
일(앞에 0 붙음)
|
09
|
%e
|
일(앞에 0 안붙음)
|
9
|
%H
|
시(24시)
|
15
|
%l
|
시(12시)
|
3
|
%M
|
분
|
10
|
%S
|
초
|
36
|
%p
|
AM 또는 PM
|
PM
|
%j
|
연도의 일
|
068
|
%y
|
연도 마지막 2글자
|
23
|
chrono 사용하는 방법
chrono는 C++11부터 표준으로 사용되기 시작했어요. chrono 라이브러리에는 기본적으로
system_clock과 steady_clock 두 개의 시간 측정 클래스가 존재해요.
1. system_clock
전통적인 유닉스 타임으로 1970년 1월 1일 목요일
0시 0분 0초 이후로 흘러간 시간을 나타내요.
2. steady_clock
OS가 마지막 부팅 이후로 흘러간 시간을 나타내요.
현재 시간을 가져올 때에는 system_clock을 사용하는 것이 맞지만 함수의 실행 시간을 측정하기 위해서는 steady_clock을 사용해야 합니다.
이유는, system_clock과 같은 경우 OS 시간 동기화 등에 의해 시간 측정 중에 시간 값이 변할 수 있기 때문인데
steady_clock은 monotonic하게 증가하기 때문에 그럴 가능성이 없습니다.
만약 OS 시간을 과거로 되돌린다면??
system_clock은 음수 값이 나올 수 있다는 얘기입니다.
auto now = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(now);
char buf[256];
ctime_s(buf, sizeof(buf), &end_time);
std::cout << "현재 시간:" << buf << std::endl;
위의 글을 쓰면서 시간이 지나서 현재 시간은, 아래와 같네요.
오늘의 꿀팁!!
chrono를 사용하여 함수 시간 측정 등 초시계를 사용할 때에는 steady_clock을 사용하자!!
함수 시간 측정하는 방법은 아래 글에서 확인하세요