PHP / 함수 / 날짜, 시간 / date(), localtime(), mktime(), time(), microtime()

PHP에서 date()는 시각을 문자열로 표시하고, localtime()은 날짜·시간의 구성 요소를 배열로 반환합니다. mktime()은 지정한 현지 날짜·시간을 Unix 타임스탬프로 바꾸며, time()·microtime()은 현재 시각을 각각 정수 초·소수 초 단위로 얻는 데 사용합니다. 같은 타임스탬프도 표시 시간대에 따라 날짜가 달라지므로 시간대를 먼저 명확히 정해야 합니다.

예제 실행 환경과 시간대

Unix 타임스탬프는 1970-01-01 00:00:00 UTC를 기준으로 표현하는 초 단위 시각 값입니다. 숫자 자체에 Asia/Seoul 같은 시간대 이름이 담기지는 않습니다. 서울 시각을 표시한다고 time()에 9시간을 더하면 다른 순간을 가리키게 되므로, 표시 함수의 시간대를 설정해야 합니다.

아래 예제는 PHP 8.x에서 실행할 수 있습니다. 각 PHP 블록을 설명한 이름의 UTF-8 파일로 저장하고, 터미널에서 해당 폴더로 이동한 뒤 다음처럼 실행하세요. 별도 DB나 라이브러리는 필요하지 않습니다. 브라우저가 없어도 결과를 볼 수 있도록 모든 예제는 명령행 출력을 사용합니다.

php date-demo.php

각 파일은 date_default_timezone_set()으로 Asia/Seoul을 지정합니다. 이는 현재 스크립트의 기본 시간대 설정이며 운영체제 시간을 바꾸지 않습니다. 여러 지역의 날짜를 한 프로그램에서 다룬다면 전역 기본값을 반복해서 바꾸기보다 DateTimeImmutable과 명시적인 DateTimeZone 사용을 검토하세요. 타임스탬프 범위는 PHP 정수 크기 등의 환경 제약도 받습니다.

date(): 타임스탬프를 원하는 문자열로 표시

date($format, $timestamp)는 지정한 정수 타임스탬프를 형식 문자열로 바꿉니다. 두 번째 인수를 생략하면 현재 시각을 사용합니다. 아래 코드를 date-demo.php로 저장합니다. 고정된 1704034800은 서울에서 2024년 1월 1일 자정에 해당하므로 실행 날짜가 달라도 결과를 비교할 수 있습니다.

<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Seoul');
$timestamp = 1704034800;
echo date('Y-m-d H:i:s P', $timestamp), PHP_EOL;
echo date('Y년 m월 d일 H시 i분', $timestamp), PHP_EOL;
date_default_timezone_set('UTC');
echo date('Y-m-d H:i:s P', $timestamp), PHP_EOL;
date_default_timezone_set('Asia/Seoul');
2024-01-01 00:00:00 +09:00
2024년 01월 01일 00시 00분
2023-12-31 15:00:00 +00:00

첫째·셋째 줄은 동일한 숫자를 서울과 UTC로 각각 표시한 결과입니다. 시간대만 바뀌었는데 연·월·일까지 달라지는 점을 확인하세요. 마지막에는 기본 시간대를 서울로 되돌려 뒤의 처리에 영향을 남기지 않습니다.

형식 문자 뜻
Y / m / d 네 자리 연도 / 두 자리 월 / 두 자리 일
H / i / s 24시간제 시 / 분 / 초
P +09:00 형태의 UTC 오프셋

m은 월, i는 분이므로 Y-m-d H:m:s처럼 잘못 섞지 않도록 주의합니다. 반환값은 계산용 날짜 객체가 아닌 문자열입니다. date()는 정수 초를 받기 때문에 u 형식으로 실제 마이크로초를 복원할 수 없으며 000000으로 표시합니다. 전체 동작은 date() 문서를 확인하세요.

localtime(): 월과 연도 값을 보정해서 사용

localtime($timestamp, true)는 tm_year 같은 이름을 키로 갖는 연관 배열을 반환합니다. 두 번째 인수가 false이거나 생략되면 숫자 인덱스 배열이므로, 구조를 읽기 쉽게 확인할 때는 true가 편리합니다. 다음을 localtime-demo.php로 저장해 php localtime-demo.php로 실행합니다.

<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Seoul');
$parts = localtime(1704034800, true);
print_r($parts);
printf("복원: %04d-%02d-%02d\n",
    $parts['tm_year'] + 1900,
    $parts['tm_mon'] + 1,
    $parts['tm_mday']);

tm_year는 124, tm_mon은 0, tm_mday는 1이며 마지막 줄은 “복원: 2024-01-01”입니다. tm_year를 그대로 연도로 표시하거나 tm_mon을 그대로 월로 표시하면 잘못된 결과가 됩니다.

키 의미와 범위 고정 예제의 값
tm_sec / tm_min / tm_hour 초 0–59 / 분 0–59 / 시 0–23 0 / 0 / 0
tm_mday 월 안의 일, 1–31 1
tm_mon 월, 0–11이므로 표시할 때 +1 0
tm_year 1900년 이후 연도 차이이므로 +1900 124
tm_wday 요일, 일요일 0부터 토요일 6 1, 월요일
tm_yday 연 안의 일, 1월 1일이 0 0
tm_isdst 양수는 서머타임 적용, 0은 미적용, 음수는 알 수 없음 0

이름의 local은 브라우저 사용자의 위치를 자동 감지한다는 뜻이 아닙니다. PHP에 설정된 시간대를 기준으로 해석합니다. localtime()도 인수를 생략하면 현재 시각을 사용하므로 결과를 재현할 때는 예제처럼 타임스탬프를 고정하세요. 배열 정의는 localtime() 문서에 나옵니다.

mktime(): 현지 날짜를 만들지만 유효성 검사는 별도

인수 순서는 시·분·초·월·일·연도입니다. 연·월·일 순서로 착각하기 쉽고, 생략한 선택 인수는 현재 현지 날짜·시간에서 가져오므로 재현 가능한 예제에서는 여섯 값을 모두 적습니다. 다음 코드를 mktime-demo.php로 저장해 실행하세요.

<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Seoul');
$timestamp = mktime(0, 0, 0, 1, 1, 2024);
if ($timestamp === false) {
    throw new RuntimeException('타임스탬프를 만들지 못했습니다.');
}
echo $timestamp, PHP_EOL;
echo date('Y-m-d H:i:s', $timestamp), PHP_EOL;
$normalized = mktime(0, 0, 0, 2, 30, 2024);
if ($normalized === false) {
    throw new RuntimeException('날짜 변환에 실패했습니다.');
}
echo '정규화: ', date('Y-m-d', $normalized), PHP_EOL;
echo '입력 날짜 유효: ', checkdate(2, 30, 2024) ? '예' : '아니요', PHP_EOL;
1704034800
2024-01-01 00:00:00
정규화: 2024-03-01
입력 날짜 유효: 아니요

2024년 2월은 29일까지이지만 mktime()은 2월 30일을 다음 달의 3월 1일로 정규화합니다. 따라서 변환에 성공했다는 이유만으로 입력 날짜가 유효하다고 판단하면 안 됩니다. 실제 날짜만 허용하려면 checkdate($month, $day, $year) 같은 검사를 먼저 수행하고, 시·분·초도 필요한 범위로 검증하세요.

PHP 8에서는 mktime()을 인수 없이 호출하면 ArgumentCountError가 납니다. 단순 현재 시각은 time()을 사용하세요. 실패 판정은 === false로 해야 유효한 타임스탬프 0을 실패로 오인하지 않습니다. 자세한 인수·정규화 규칙은 mktime() 문서를 참고하세요.

time(): 현재 시각을 한 번 얻어 여러 형식으로 표시

time()은 현재 Unix 타임스탬프를 정수로 반환합니다. 다음을 time-demo.php로 저장해 실행합니다. 같은 순간을 비교하려고 time()을 한 번 호출해 $now에 저장하고 두 표시 함수에 같은 값을 전달했습니다.

<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Seoul');
$now = time();
echo 'timestamp: ', $now, PHP_EOL;
echo '서울: ', date('Y-m-d H:i:s P', $now), PHP_EOL;
date_default_timezone_set('UTC');
echo 'UTC: ', date('Y-m-d H:i:s P', $now), PHP_EOL;
date_default_timezone_set('Asia/Seoul');

첫 줄의 숫자와 날짜는 실행 시점에 따라 달라집니다. 서울 줄은 +09:00, UTC 줄은 +00:00이며 두 줄이 나타내는 실제 순간은 같습니다. time() 숫자를 날짜 형식 문자열과 혼동하거나, 밀리초를 요구하는 다른 시스템에 초 단위 값을 그대로 보내지 않도록 단위를 확인하세요. 기준과 반환형은 time() 문서에서 확인할 수 있습니다.

타임스탬프에 86400을 더하면 경과 시간 24시간을 더하는 것이며 모든 지역의 달력상 “다음 날 같은 시각”과 항상 같지는 않습니다. 서머타임이 있는 지역의 달력 계산이나 월 단위 예약은 시간대가 지정된 날짜 객체로 처리하고 해당 경계 조건을 따로 확인해야 합니다.

microtime(): 소수 초 현재 시각과 실행 시간 측정의 차이

microtime()의 기본 반환값은 “초 미만의 소수 부분 정수 초 부분”을 공백으로 구분한 문자열입니다. microtime(true)는 Unix 기준 초를 소수부가 있는 float로 반환합니다. 이름에 micro가 있어도 반환 숫자 전체를 마이크로초 단위 정수로 해석하면 안 됩니다. 다음 코드를 microtime-demo.php로 저장합니다.

<?php
declare(strict_types=1);
echo '문자열: ', microtime(), PHP_EOL;
printf("실수 초: %.6f\n", microtime(true));
$wallStart = microtime(true);
$monoStart = hrtime(true);
if ($monoStart === false) {
    throw new RuntimeException('고해상도 시계를 사용할 수 없습니다.');
}
$sum = 0;
for ($i = 1; $i <= 10000; $i++) {
    $sum += $i;
}
$monoEnd = hrtime(true);
$wallEnd = microtime(true);
if ($monoEnd === false) {
    throw new RuntimeException('고해상도 시계를 사용할 수 없습니다.');
}
echo '합계: ', $sum, PHP_EOL;
printf("microtime 차이(초): %.9f\n", $wallEnd - $wallStart);
printf("hrtime 경과(초): %.9f\n", ($monoEnd - $monoStart) / 1_000_000_000);

문자열 줄은 0으로 시작하는 소수 부분과 정수 초 부분, 실수 초 줄은 소수점 이하 여섯 자리로 표시한 시각입니다. 서로 다른 호출이므로 두 시각이 완전히 같다고 기대하지 마세요. 합계는 항상 50005000이고, 두 경과 시간은 실행 환경에 따라 달라집니다. 출력 소수 자릿수를 늘리는 것과 실제 시계의 정확도를 높이는 것은 다릅니다.

microtime(true)의 차이는 현재 시각을 나타내는 시계를 사용하므로 시스템 시각 조정의 영향을 받을 수 있습니다. 처리 시간 측정에는 PHP 공식 문서가 권장하는 hrtime()처럼 단조 증가하는 고해상도 시계를 사용합니다. hrtime(true)의 숫자는 나노초 단위이므로 차이를 1,000,000,000으로 나누면 초가 됩니다. 그 절대값을 달력상의 시각으로 변환하지 마세요.

이 반복문은 함수 사용법을 보기 위한 실습이며 PHP나 서버 성능을 대표하는 벤치마크는 아닙니다. 실제 성능 비교에서는 대상 작업·입력·반복 횟수와 부하 조건을 맞춰 여러 번 관찰해야 합니다. 반환 형식과 측정 용도의 구분은 microtime() 문서도 참고하세요.