2026년 06월 07일 | DBMS Error 가이드
이 글에서 다루는 내용
22007 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22007 invalid datetime format 는?
PostgreSQL 에러 코드 22007 (invalid datetime format) 은 날짜·시간 값을 처리할 때 입력된 문자열이 PostgreSQL이 기대하는 datetime 형식과 일치하지 않을 때 발생합니다. 예를 들어 DATE, TIMESTAMP, TIMESTAMPTZ, TIME 등의 타입으로 형변환(CAST) 하거나 TO_DATE(), TO_TIMESTAMP() 같은 함수를 사용할 때 잘못된 형식의 문자열이 입력되면 이 에러가 트리거됩니다. 실무에서는 외부 시스템에서 데이터를 받아 적재하거나 ETL 파이프라인을 처리하는 과정에서 특히 빈번하게 마주치는 에러입니다.
주요 발생 원인
1. 날짜 구분자 또는 형식 불일치
가장 흔한 원인으로, 미국식 날짜 형식(MM/DD/YYYY)을 ISO 표준 형식(YYYY-MM-DD) 없이 그대로 캐스팅하거나, 날짜와 시간 사이의 구분자(T vs 공백)가 맞지 않는 경우입니다. PostgreSQL은 기본적으로 ISO 8601 형식을 따르기 때문에, 다른 지역 또는 시스템에서 생성된 날짜 문자열은 반드시 명시적 변환이 필요합니다. 예를 들어 '12/31/2023' 을 직접 DATE로 캐스팅하면 에러가 발생합니다.
2. 존재하지 않는 날짜 또는 시간 값 입력
월이 13이거나, 2월 30일처럼 달력에 존재하지 않는 날짜, 또는 시간이 25:00:00처럼 범위를 벗어난 값이 입력될 때 발생합니다. 이런 경우는 애플리케이션의 유효성 검사 로직이 부족하거나, 레거시 시스템에서 잘못된 데이터가 그대로 전달될 때 주로 나타납니다. 데이터 마이그레이션 작업 시 대량의 더티 데이터(dirty data)가 포함된 경우 이 에러가 수천 건씩 발생할 수도 있습니다.
3. TO_TIMESTAMP() / TO_DATE() 의 포맷 마스크 오류
TO_TIMESTAMP('2023-31-12', 'YYYY-DD-MM') 처럼 포맷 마스크와 실제 데이터의 순서가 뒤바뀌거나, 포맷 문자열 자체가 잘못 기술된 경우입니다. 특히 월(MM)과 분(MI)을 혼동하거나, 24시간제(HH24) 대신 12시간제(HH)를 사용하면서 AM/PM 지시자를 빠뜨리는 실수가 자주 발생합니다. 이 원인은 런타임이 아닌 개발 단계에서 충분한 테스트를 거치지 않으면 운영 환경에서 예기치 않게 터질 수 있습니다.
해결 방법
원인 1 해결: 명시적 포맷 지정으로 변환
미국식 날짜나 비표준 형식의 문자열은 TO_DATE() 또는 TO_TIMESTAMP() 함수로 포맷을 명시적으로 지정하여 변환합니다.
-- 잘못된 예 (에러 발생)
SELECT '12/31/2023'::DATE;
-- ERROR: invalid input syntax for type date: "12/31/2023"
-- 올바른 해결 방법
SELECT TO_DATE('12/31/2023', 'MM/DD/YYYY');
-- 결과: 2023-12-31
-- TIMESTAMP 포함 예
SELECT TO_TIMESTAMP('31-12-2023 14:30:00', 'DD-MM-YYYY HH24:MI:SS');
-- 결과: 2023-12-31 14:30:00+00
-- ISO 8601 형식으로 정규화 후 캐스팅
SELECT ('2023-12-31T14:30:00'::TIMESTAMP);
-- 결과: 2023-12-31 14:30:00
원인 2 해결: 유효하지 않은 날짜 필터링 및 예외 처리
대량 데이터 적재 시 잘못된 날짜 값을 사전에 필터링하거나, CASE문과 정규식을 활용해 방어적으로 처리합니다.
-- 잘못된 날짜를 NULL로 처리하는 안전한 변환 함수
CREATE OR REPLACE FUNCTION safe_to_date(p_value TEXT, p_format TEXT)
RETURNS DATE AS $$
BEGIN
RETURN TO_DATE(p_value, p_format);
EXCEPTION
WHEN invalid_datetime_format OR datetime_field_overflow THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 사용 예
SELECT safe_to_date('13/45/2023', 'MM/DD/YYYY');
-- 결과: NULL (에러 없이 안전하게 처리)
SELECT safe_to_date('12/31/2023', 'MM/DD/YYYY');
-- 결과: 2023-12-31
-- 정규식으로 YYYY-MM-DD 형식만 사전 검증 후 변환
SELECT
raw_date,
CASE
WHEN raw_date ~ '^\d{4}-\d{2}-\d{2}$'
THEN raw_date::DATE
ELSE NULL
END AS parsed_date
FROM staging_table;
원인 3 해결: 포맷 마스크 정확히 지정
TO_TIMESTAMP()와 TO_DATE()의 포맷 마스크 패턴을 정확히 확인하고 수정합니다.
-- 잘못된 예 (MM과 MI 혼동)
SELECT TO_TIMESTAMP('2023-12-31 14:30', 'YYYY-MI-DD HH24:MM');
-- 결과: 의도치 않은 값 또는 에러
-- 올바른 포맷 마스크 사용
SELECT TO_TIMESTAMP('2023-12-31 14:30:00', 'YYYY-MM-DD HH24:MI:SS');
-- 결과: 2023-12-31 14:30:00+00
-- 12시간제 사용 시 AM/PM 반드시 포함
SELECT TO_TIMESTAMP('2023-12-31 02:30:00 PM', 'YYYY-MM-DD HH:MI:SS AM');
-- 결과: 2023-12-31 14:30:00+00
-- Timezone 포함된 문자열 변환
SELECT TO_TIMESTAMP('2023-12-31 14:30:00 +09', 'YYYY-MM-DD HH24:MI:SS TZH');
-- 결과: 2023-12-31 05:30:00+00 (UTC 기준)
-- 포맷 마스크 확인을 위한 역방향 검증 (현재 날짜를 특정 포맷으로 출력)
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
-- 결과: 2023-12-31 14:30:00
예방 방법
1. 애플리케이션 레벨에서 ISO 8601 형식 표준화
데이터베이스에 날짜·시간 데이터를 입력하기 전, 애플리케이션 또는 ETL 레이어에서 반드시 ISO 8601 형식(YYYY-MM-DD, YYYY-MM-DDTHH:MI:SS)으로 정규화하는 정책을 수립하세요. PostgreSQL의 DateStyle 파라미터를 SET DateStyle = 'ISO, YMD'; 로 고정하고, 외부 데이터 소스에서 들어오는 모든 날짜 컬럼은 TEXT로 먼저 받아 safe_to_date() 같은 래퍼 함수를 통해 변환한 뒤 저장하는 패턴을 권장합니다.
-- 세션 또는 데이터베이스 레벨 DateStyle 설정
SET DateStyle = 'ISO, YMD';
-- 또는 postgresql.conf / ALTER DATABASE 설정
ALTER DATABASE mydb SET datestyle = 'ISO, YMD';
2. CHECK 제약 조건과 도메인 타입으로 컬럼 수준 방어
테이블 설계 단계부터 날짜·시간 컬럼에 DATE, TIMESTAMP WITH TIME ZONE 같은 네이티브 타입을 사용하고, TEXT 타입으로 날짜를 저장하는 안티패턴을 금지합니다. 필요하다면 PostgreSQL 도메인(DOMAIN)을 만들어 재사용 가능한 유효성 검사 로직을 중앙화하세요.
-- 안티패턴: TEXT로 날짜 저장 (지양)
CREATE TABLE bad_example (
event_date TEXT -- 절대 이렇게 하지 마세요
);
-- 권장 패턴: 네이티브 타입 사용
CREATE TABLE good_example (
event_date DATE NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 커스텀 도메인으로 재사용 가능한 날짜 타입 정의
CREATE DOMAIN valid_date AS DATE
CHECK (VALUE >= '1900-01-01' AND VALUE <= '2100-12-31');
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date valid_date NOT NULL
);
관련 에러
- 22008 (datetime_field_overflow): 날짜·시간 필드의 값이 허용 범위를 초과할 때 발생합니다. 예를 들어 월이 13이거나 시간이 25인 경우로, 22007과 함께 쌍으로 처리해야 하는 에러입니다.
- 22P02 (invalid_text_representation): 텍스트를 숫자나 불리언 등 다른 타입으로 변환할 때 형식이 맞지 않으면 발생하며, datetime 이외의 타입 캐스팅 실패와 관련됩니다.
- 42804 (datatype_mismatch): 함수 인자나 컬럼에 기대하지 않는 데이터 타입이 전달될 때 발생하며, 날짜 타입과 문자열 타입을 혼용할 때 나타날 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.