자료 정리 60분, 검토 60분, 출력 30분, 후속 점검 15분을 더하면 165분입니다. 하지만 일부 작업이 동시에 진행됐다면 달력에서 실제 차지한 시간은 이보다 짧습니다. 항목별 시간을 더하는 질문과 어느 순간 하나 이상의 작업이 켜져 있었는지 묻는 질문은 다릅니다. 이 글은 네 개의 가상 시간 구간을 정렬하고 겹침을 합쳐 단순 합계 165분과 합집합 135분을 대조합니다. 사람의 집중 시간이나 생산성을 측정한 기록이 아니라, 같은 시계를 쓰는 구간 데이터의 합산 규칙을 검산한 예제입니다.
작업별 소요 시간과 벽시계 점유 시간을 먼저 나눕니다.
입력은 09:00~10:00, 09:30~10:30, 10:30~11:00, 12:00~12:15입니다. 각 구간의 길이는 60, 60, 30, 15분이므로 단순 합계는 165분입니다. 한편 09:00부터 11:00까지는 적어도 하나의 작업이 이어지고, 이후 12:00부터 12:15까지 다시 켜집니다. 이 질문의 답은 120+15=135분입니다. 둘 중 한쪽이 무조건 틀린 합계가 아니라 서로 다른 지표입니다.
담당자가 다른 네 사람의 투입 시간을 합산한다면 중복 시간도 인원별 공수에 포함할 수 있습니다. 반면 한 장비의 가동 시간이나 하나의 일정을 점유한 길이를 구할 때는 겹침을 한 번만 세려는 경우가 있습니다. 무엇을 더하려는지 정하지 않은 채 중복을 모두 지우면 인건비 자료를 훼손할 수 있습니다. 이 글에서는 같은 가상의 한 자원에 대한 활성 시간의 합집합만 계산합니다. 비용, 청구 시간 또는 실제 노동 시간을 판단하는 규칙으로 자동 확대하지 않습니다.
구간의 끝점을 포함할지 입력 계약에서 정합니다.
모든 구간은 시작은 포함하고 끝은 제외하는 [시작, 끝)으로 읽습니다. 09:00~10:00과 10:00~10:30은 10시 한 순간을 두 번 점유하지 않습니다. 연속된 구간을 하나로 표현해도 총 길이는 90분으로 같습니다. 코드에서는 다음 시작이 이전 끝보다 작거나 같으면 합칩니다. 맞닿은 구간을 따로 남기는 방식도 길이 계산은 같지만 목록의 의미와 개수는 달라집니다.
날짜별 출석 횟수처럼 닫힌 구간의 끝 날짜도 세는 업무와 이 규칙을 섞지 마세요. 월요일부터 화요일까지 이틀이라는 셈법은 연속 시간의 끝−시작과 다른 정의입니다. PostgreSQL의 범위 문서가 경계 포함·제외와 빈 범위를 구분하는 이유도 여기에 있습니다. 실제 입력에는 시작·끝의 단위와 시간대를 같이 두고, 시스템별 기본값을 추측하지 않아야 합니다. 이 예제의 숫자는 자정 이후 분 단위이며 같은 날짜의 같은 기준을 쓴다는 가정으로 고정했습니다.
정렬한 뒤 현재 구간의 끝만 늘려 갑니다.
숫자로 바꾸면 입력은 [540,600], [570,630], [630,660], [720,735]입니다. 시작 시각 순서대로 읽으며 첫 구간을 결과에 넣습니다. 두 번째의 시작 570은 이전 끝 600보다 이르므로 끝을 630까지 늘립니다. 세 번째의 시작 630은 현재 끝과 같아 660까지 연결합니다. 마지막 720은 660 뒤이므로 새 구간으로 시작합니다. 결과 목록은 [540,660], [720,735]입니다.
겹친 구간을 만났을 때 끝을 무조건 새 구간의 끝으로 바꾸면 안 됩니다. 더 큰 구간 안에 짧은 구간이 들어오면 끝이 오히려 앞당겨질 수 있습니다. 그래서 현재 끝과 새 끝의 최댓값을 취합니다. 코드의 nested 검사는 기존 입력에 [550,560]을 추가해도 결과가 바뀌지 않는지 확인합니다. 중첩을 처리하지 못하는 구현은 겉보기에는 정상적인 두 구간 예제에서 통과하더라도 실제 목록에서는 시간을 잃을 수 있습니다. 정렬과 최댓값 갱신을 분리해 작은 예제로 따라가 보세요.
손으로 검토할 때는 시간축에 원본 구간을 각각 그린 뒤, 적어도 한 구간이 있는 부분만 한 줄로 칠하면 결과를 확인하기 쉽습니다. 예제에서는 09:00~11:00 한 줄과 12:00~12:15 한 줄입니다. 두 번째 작업이 시작한 09:30부터 10:00까지 30분이 중복 합산됐다는 것도 드러납니다. 이런 작은 그림은 합계가 잘못된 원인을 설명하지만 실제 직원의 동시 집중이나 처리 능력을 측정하는 증거는 아닙니다.
완전히 떨어진 구간이 하나 더 들어오면 합집합은 그 길이만큼 늘어야 하고, 기존 범위 안에 있는 구간을 추가하면 그대로여야 합니다. 실제 JSON의 nested 입력은 두 번째 성질을 확인했습니다. 맞닿은 구간도 목록을 하나로 합칠지의 정책과 총 길이는 구분하세요. 같은 길이가 나온다는 이유로 이벤트 개수까지 같다고 집계하면 안 됩니다. 작업별 원본 개수는 별도 열에 유지합니다.
| 기준 | 시간 구간 또는 판정 | 분 |
|---|---|---|
| 행별 길이 단순 합 | 60+60+30+15 | 165 |
| 합친 첫 구간 | 09:00~11:00 | 120 |
| 합친 둘째 구간 | 12:00~12:15 | 15 |
| 같은 자원 합집합 | 120+15 | 135 |
추가 실행 결과의 reorderedSame과 idempotent는 모두 true입니다. 입력 네 구간의 순서를 뒤집어도 합집합은 같고, 이미 합친 두 구간을 다시 합쳐도 [540,660]과 [720,735]가 유지됐습니다. 이 두 성질은 원본 행 순서와 반복 처리에 의존하는 오류를 찾는 보조 검사이며, 구간의 업무 의미까지 검증하는 것은 아닙니다.
한 번씩만 비교하는 방식으로는 연쇄 겹침을 놓칩니다.
첫 구간과 두 번째 구간의 겹침만 빼고 다음 행으로 넘어가면 세 번째가 확장된 범위와 어떻게 연결되는지 놓칠 수 있습니다. A와 B가 겹치고 B와 C가 겹치면, A와 C가 직접 겹치지 않더라도 세 구간의 합집합은 하나가 될 수 있습니다. 원래 행 쌍의 겹침 길이를 전부 빼는 방법도 세 개 이상이 동시에 겹칠 때 중복 차감을 만들 수 있어 조심해야 합니다.
예제의 핵심은 결과 구간을 누적하면서 현재 합집합의 끝과 다음 시작을 비교한다는 점입니다. 원본 입력은 보존하고 별도의 정렬 사본을 사용합니다. 입력 순서가 시간 순이라는 보장이 없으면 화면에 보이는 행 순서를 그대로 믿지 않습니다. 이미 합쳐진 결과를 다시 같은 함수에 넣어도 결과가 바뀌지 않는 성질도 좋은 보조 검사입니다. 다만 여기의 검증은 고정 사례에 대한 것으로, 모든 입력 조합을 수학적으로 증명하거나 실제 일정 시스템을 통합 시험한 것은 아닙니다.
빈 구간과 뒤집힌 구간은 다른 상태로 처리합니다.
[600,600]은 길이가 0인 구간이므로 합집합 목록에 넣지 않았습니다. 빈 입력 목록도 빈 결과와 총 0분으로 처리합니다. 반면 [600,540]은 끝이 시작보다 앞이므로 ValueError로 거부했습니다. 자정을 넘긴 작업이라고 임의로 하루를 더하면 입력 오류를 숨길 수 있습니다. 실제 자정 통과라면 시작 날짜와 끝 날짜까지 있어야 23시~다음 날 1시라는 의미를 확정할 수 있습니다.
종료 시각이 아직 없는 진행 중 작업도 자동으로 현재 시각으로 채우기 전에 기준을 정해야 합니다. 보고서를 다시 열 때마다 값이 달라지고, 누락된 종료 기록을 정상 진행으로 오해할 수 있기 때문입니다. 보고 기준 시각을 명시해 잘라 계산하는 방식과 미완료로 제외하는 방식 중 목적에 맞게 선택하고 제외 건수를 남깁니다. 다운로드 예제에는 진행 중이나 결측 구간이 없으므로 이를 처리하는 운영 기능이 검증됐다고 주장하지 않습니다. 데이터 상태와 길이 0이라는 정상 숫자를 구분하는 데 초점을 둡니다.
시간대가 다르면 먼저 같은 순간으로 정렬해야 합니다.
09:00이라는 글자만으로 서울과 런던의 같은 순간을 가리키지는 않습니다. 시간대나 오프셋이 다른 기록은 같은 시간축으로 변환한 뒤 비교해야 합니다. 시계가 반복되거나 건너뛰는 지역의 현지 시간은 날짜와 시간대 규칙 없이 단순 뺄셈하기 어렵습니다. Python datetime 문서의 aware와 naive 구분을 참고하되, 이 글의 분 단위 정수 예제가 시간대 변환까지 수행했다고 읽으면 안 됩니다.
장비 여러 대의 로그도 시계가 맞는지 확인해야 합니다. 같은 이름의 요청이라도 시작과 종료가 다른 호스트에서 찍혔다면 보정 차이가 구간 길이에 섞일 수 있습니다. 계측 시계의 오차와 실제 작업 간 겹침을 분리하세요. 아주 짧은 구간을 초 단위로 반올림하면 겹침 판정 자체가 달라질 수 있으므로 원시 정밀도로 합친 뒤 표시에서만 반올림하는 편이 낫습니다. 이 자료의 정수 분 계산은 그런 미세 정밀도 문제를 피하기 위해 설계한 작은 계약이며 실제 초 단위 작업의 증거가 아닙니다.
합친 시간에서 인건비 절감이나 자동화 성과를 추론하지 않습니다.
165분과 135분의 차이 30분은 입력 구간에서 겹쳐 센 길이입니다. 프로그램 덕분에 실제 30분을 절약했다는 뜻이 아닙니다. 누가 작업했는지, 기다리는 시간과 손으로 처리한 시간이 어떻게 나뉘는지, 같은 시간에 수행 가능한지의 정보가 없기 때문입니다. “겹친 구간을 한 번만 센 합집합은 135분”이라고 쓰면 관측과 계산의 범위가 맞습니다.
대기 시간이 포함된 다운로드와 사람의 검토 시간을 섞을 때도 주의합니다. 사람은 다른 작업을 할 수 있지만 장비는 계속 점유될 수 있고, 반대로 서버 여러 개는 동시에 일할 수 있습니다. 실제로 필요한 지표가 자원별 점유인지 팀 전체 공수인지 사용자 체감 지연인지에 따라 그룹을 나눕니다. 합집합을 구하는 알고리즘은 그룹 정의를 대신하지 않습니다. 분자·분모가 바뀌면 전환율 해석이 달라지는 것처럼, 합산 단위를 바꾸면 시간 숫자의 의미도 바뀐다는 점을 보고서에서 드러내야 합니다.
실무 명세에는 원본 구간과 합친 구간을 모두 남깁니다.
간단한 결과표라도 원본 ID, 시작·끝, 시간대, 제외 사유, 결과 구간 ID를 갖추면 어느 기록이 합쳐졌는지 추적하기 쉽습니다. 이 예제의 코드는 계산을 설명하기 위해 숫자 목록만 반환하지만, 운영 도구라면 각 원본의 출처를 보존하는 연결표를 추가하는 편이 좋습니다. 원본 행을 덮어써 합쳐 버리면 이후 오류를 수정하거나 작업별 공수를 따로 계산하기 어렵습니다.
먼저 세 가지를 독자 자료의 사본으로 확인하세요. 겹치지 않는 두 구간의 합계가 유지되는지, 완전히 포함된 짧은 구간을 넣어도 길이가 늘지 않는지, 뒤집힌 구간이 조용히 정상화되지 않는지입니다. 이 세 질문이 통과한 다음 시간대와 미완료 정책을 검토합니다. 실행 코드와 JSON은 이 글의 네 구간에 대한 결과를 재현하기 위한 자료입니다. 안전한 실험 입력을 고르는 방법과 데이터 변경을 원본 ID별로 대조하는 절차를 함께 읽으면 일정표를 자동 수정하기 전에 무엇을 보존해야 할지 더 분명해집니다.
입력과 판정을 다시 확인하려면
이 글의 가상 입력과 코드는 2026년 10월 15일 로컬 Python 3에서 실행했습니다. 글의 발행일과 실행일은 다릅니다. 별도 설치 패키지 없이 Python 표준 라이브러리로 실행할 수 있습니다.
터미널에서 python3 overlapping-work-intervals-20261015.py를 실행하면 JSON을 출력합니다. 사용자 자료나 비밀번호는 필요하지 않습니다. 입력과 실행 환경은 결과 JSON에 함께 남겼습니다.
계산 기준을 확인한 원문과 다음 작업
- Python — datetime — aware/naive와 시간차의 의미를 확인했습니다.
- PostgreSQL — Range types — 포함·제외 경계와 빈 범위의 구분을 확인했습니다.
- PostgreSQL — Range functions — 겹침·합집합·다중 범위 연산의 의미를 확인했습니다.
공식 원문은 2026-10-15에 확인했습니다. 문서가 설명하는 API·정의와 이 글의 가상 계산 결과는 구분해서 읽어 주세요.
함께 보면 좋은 글: 가상 데이터로 안전하게 실험하기, CSV 원본 변경 대조.



