2026년 08월 20일 | DBMS Error 가이드
이 글에서 다루는 내용
22031 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22031 invalid argument for sql json datetime function 는?
PostgreSQL 에러 코드 22031(invalid argument for sql json datetime function)은 SQL/JSON 경로 표현식에서 날짜/시간 관련 함수를 사용할 때 잘못된 형식의 값이 전달되었을 때 발생합니다. 주로 jsonb_path_query, jsonb_path_exists, jsonb_path_match 등의 함수와 함께 SQL/JSON 경로 내부의 .datetime() 메서드를 사용할 때 문제가 생깁니다. 예를 들어, JSON 문자열 값이 ISO 8601 날짜/시간 형식에 맞지 않거나, 기대하는 날짜 타입과 실제 값의 형식이 불일치할 경우 이 에러가 트리거됩니다.
주요 발생 원인
1. ISO 8601 형식을 따르지 않는 날짜/시간 문자열 사용
PostgreSQL의 SQL/JSON .datetime() 함수는 기본적으로 ISO 8601 표준 형식(예: "2024-01-15", "2024-01-15T10:30:00")을 기대합니다. JSON 데이터 내에 "15/01/2024"나 "January 15, 2024" 같은 비표준 형식의 날짜 문자열이 들어있으면 파싱에 실패하여 22031 에러가 발생합니다. 특히 외부 시스템으로부터 데이터를 수집하거나 레거시 데이터를 마이그레이션할 때 이런 형식 불일치가 자주 발생합니다.
2. .datetime() 함수에 잘못된 포맷 템플릿 전달
.datetime() 메서드에 사용자 정의 포맷 문자열을 전달할 때, PostgreSQL이 인식하지 못하는 잘못된 포맷 패턴을 사용하면 에러가 발생합니다. 예를 들어, to_timestamp와 유사하게 사용하려 했지만 실제 .datetime("YYYY/MM/DD") 형식이 JSON 값과 맞지 않을 때 이 에러가 발생합니다. 포맷 문자열과 실제 데이터의 형식이 정확히 일치해야 한다는 점을 반드시 기억해야 합니다.
3. 타임존 정보 불일치 또는 누락
timestamptz(타임존 포함 타임스탬프)를 비교하거나 처리하는 경우, JSON 값 내에 타임존 정보가 없거나 잘못된 오프셋이 포함된 경우에도 22031 에러가 발생할 수 있습니다. 예를 들어, "2024-01-15T10:30:00+99:00" 처럼 유효하지 않은 타임존 오프셋이 포함된 경우가 대표적입니다. SQL/JSON 경로 함수에서 타임존을 처리하는 방식은 일반 SQL의 타임존 처리보다 훨씬 엄격하므로 각별한 주의가 필요합니다.
해결 방법
원인 1 해결: 비표준 날짜 형식 처리
JSON 데이터 내부의 날짜 형식이 ISO 8601이 아닌 경우, jsonb_path_query 호출 전에 데이터를 변환하거나, .datetime() 호출 시 명시적인 포맷 패턴을 제공해야 합니다.
-- 에러 발생 예시: 비표준 날짜 형식
SELECT jsonb_path_query(
'{"event_date": "15/01/2024"}',
'$.event_date.datetime()'
);
-- ERROR: 22031 invalid argument for sql json datetime function
-- 해결책 1: 포맷 패턴 명시
SELECT jsonb_path_query(
'{"event_date": "15/01/2024"}',
'$.event_date.datetime("DD/MM/YYYY")'
);
-- 해결책 2: JSON 저장 전에 ISO 형식으로 변환
UPDATE events
SET payload = jsonb_set(
payload,
'{event_date}',
to_jsonb(
to_date(payload->>'event_date', 'DD/MM/YYYY')::text
)
)
WHERE payload->>'event_date' ~ '^\d{2}/\d{2}/\d{4}$';
-- 해결책 3: SQL 레벨에서 변환 후 비교
SELECT *
FROM events
WHERE (payload->>'event_date') IS NOT NULL
AND to_date(payload->>'event_date', 'DD/MM/YYYY') > '2024-01-01';
원인 2 해결: 올바른 포맷 템플릿 사용
.datetime() 함수에 전달하는 포맷 패턴이 실제 데이터 형식과 정확히 일치하는지 확인해야 합니다.
-- 에러 발생 예시: 포맷 불일치
SELECT jsonb_path_query(
'{"ts": "2024-01-15 10:30:00"}',
'$.ts.datetime("YYYY/MM/DD")'
);
-- ERROR: 22031 invalid argument for sql json datetime function
-- 해결책: 실제 데이터 형식에 맞는 포맷 패턴 사용
SELECT jsonb_path_query(
'{"ts": "2024-01-15 10:30:00"}',
'$.ts.datetime("YYYY-MM-DD HH24:MI:SS")'
);
-- 실제 업무 예시: 날짜 범위 필터링
SELECT id, payload
FROM orders
WHERE jsonb_path_exists(
payload,
'$.created_at.datetime("YYYY-MM-DD HH24:MI:SS") > $ts',
jsonb_build_object('ts', '2024-01-01T00:00:00'::timestamptz)
);
-- datetime() 없이 캐스팅으로 우회하는 방법
SELECT id, payload
FROM orders
WHERE (payload->>'created_at')::timestamp > '2024-01-01 00:00:00';
원인 3 해결: 타임존 처리 명확화
타임존 정보가 포함된 데이터를 다룰 때는 유효한 오프셋을 사용하고, 필요하다면 데이터 검증 로직을 추가합니다.
-- 에러 발생 예시: 잘못된 타임존 오프셋
SELECT jsonb_path_query(
'{"ts": "2024-01-15T10:30:00+99:00"}',
'$.ts.datetime()'
);
-- ERROR: 22031 invalid argument for sql json datetime function
-- 해결책 1: 유효한 타임존 오프셋 사용
SELECT jsonb_path_query(
'{"ts": "2024-01-15T10:30:00+09:00"}',
'$.ts.datetime()'
);
-- 해결책 2: 타임존 정보를 제거하고 처리
SELECT jsonb_path_query(
'{"ts": "2024-01-15T10:30:00"}',
'$.ts.datetime()'
);
-- 해결책 3: 데이터 저장 전 타임존 정규화
INSERT INTO logs (payload)
VALUES (
jsonb_build_object(
'ts', to_jsonb(now() AT TIME ZONE 'UTC')
)
);
-- 문제 데이터 사전 탐지 쿼리
SELECT id, payload->>'ts' AS ts_value
FROM logs
WHERE NOT (
payload->>'ts' ~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2}|Z)?$'
);
예방 방법
1. JSON 데이터 저장 시 날짜/시간 형식을 ISO 8601로 강제화
애플리케이션 레이어나 데이터베이스 트리거를 통해 JSON 데이터에 날짜/시간 값을 저장할 때 반드시 ISO 8601 형식(YYYY-MM-DDTHH:MI:SS 또는 YYYY-MM-DD)으로 저장하도록 정책을 수립하세요. 또한 CHECK 제약 조건이나 트리거를 사용하여 비정상적인 날짜 형식이 데이터베이스에 저장되는 것을 원천 차단하는 것이 좋습니다.
-- 트리거를 활용한 날짜 형식 검증 예시
CREATE OR REPLACE FUNCTION validate_json_datetime()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.payload ? 'event_date' THEN
IF NOT (NEW.payload->>'event_date' ~
'^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2}|Z)?)?$') THEN
RAISE EXCEPTION 'event_date must be ISO 8601 format, got: %',
NEW.payload->>'event_date';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
2. .datetime() 사용 전 입력값 유효성 검증 로직 추가
운영 쿼리에서 .datetime() 메서드를 사용하기 전에 정규식이나 jsonb_path_exists를 활용한 사전 유효성 검사를 수행하세요. 특히 외부에서 유입되는 데이터나 레거시 시스템 데이터를 처리할 때는 방어적 프로그래밍 접근법을 취하여 에러 발생 가능성을 최소화하는 것이 중요합니다.
-- 안전한 datetime 변환 함수 예시
CREATE OR REPLACE FUNCTION safe_json_datetime(p_json jsonb, p_key text)
RETURNS timestamptz AS $$
DECLARE
v_val text;
BEGIN
v_val := p_json ->> p_key;
IF v_val IS NULL THEN RETURN NULL; END IF;
IF v_val ~ '^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2}|Z)?)?$' THEN
RETURN v_val::timestamptz;
END IF;
RETURN NULL;
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
관련 에러
- 22007 (
invalid_datetime_format): 날짜/시간 문자열이 지정된 형식과 맞지 않을 때 발생하며, 22031과 유사하지만 일반 SQL 캐스팅 과정에서 발생한다는 점이 다릅니다. - 22P02 (
invalid_text_representation): 텍스트를 날짜/시간 타입으로 변환할 수 없을 때 발생합니다. - 2201B (
invalid_regular_expression): JSON 경로 표현식 내 정규식 오류 시 발생합니다. - 42883 (
undefined_function):.datetime()메서드를 지원하지 않는 PostgreSQL 버전(12 미만)에서 발생할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.