[코드카타] 알고리즘 71번

양승우·2025년 1월 7일

코드카타

목록 보기
46/58

문제 및 데이터 이해

A71. 개인정보 수집 유효기간

개인정보 n개가 있습니다. 약관 종류는 여러 가지 있으며 각 약관마다 개인정보 보관 유효기간이 정해져 있습니다. 수집된 개인정보는 유효기간 전까지만 보관 가능하며, 유효기간이 지났다면 반드시 파기해야 합니다.
예를 들어, A라는 약관의 유효기간이 12 달이고, 2021년 1월 5일에 수집된 개인정보가 A약관으로 수집되었다면 해당 개인정보는 2022년 1월 4일까지 보관 가능하며 2022년 1월 5일부터 파기해야 할 개인정보입니다.
당신은 오늘 날짜로 파기해야 할 개인정보 번호들을 구하려 합니다.
모든 달은 28일까지 있다고 가정합니다.
(일부 중략)


리스트 내에 ' '와 '.'을 구분자로 입력된 값들을 풀어내어
날짜에 지정된 월만큼 더하고(date_add),
더한 값이 today보다 작거나 같다면 파기해야 하는 데이터로 걸러내는 문제.

주요 함수 설명

본래 문제의 의도


제한 사항이 굉장히 길다
보면 알 수 있다시피 구분자를 통해 split을 실시하고,
특정 인덱스의 값에 특정 값을 더해주는, 간단하지만 번거로운 과정을 거쳐야 한다

datetime과 dateutil

하지만 시간/날짜를 다루는 함수와 함께라면 훨씬 간단하게 문제를 해결할 수 있다.
실제로 sql에서도 date_add/date_sub 함수가 있지 않은가?

python에서는 datetime.timedelta() 함수를 통해 이를 구현할 수 있다

datetime.timedelta()

Adding Dates and Times in Python

006 두 날짜의 차이를 알려면? ― datetime.timedelta

datetime.timedelta()는 두 날짜의 차이를 계산할 때 사용하는 함수이다. 이 때 +와 -를 사용할 수 있으므로 특정 날짜에 원하는 시간 단위를 더하거나 뺄 수 있다

단, 현재 우리가 가진 데이터는 str 자료형이므로, 이를 date 자료형으로 변환할 필요가 있다.

날짜형 <-> 문자형 데이터 변환 : strptime(), strftime()

[Python] 문자열을 날짜 또는 시간으로 변환 (Convert String to Date or Time)
str 자료형을 date 자료형으로 변환하기 위해서 strptime() 함수를 사용할 수 있다

date_str = '2023-05-20'
date_object = datetime.strptime(date_str, '%Y-%m-%d').date()

반대로 date 자료형을 str 자료형으로 변환할 때는 strftime() 함수를 사용할 수 있다

date = "2022-01-10"
date_ = datetime.strptime(date, "%Y-%m-%d") # date_는 문자형
prev_date = date_.strftime("%Y-%m-%d") # prev_date는 날짜형

timedelta()의 한계 : month와 year

하지만 timedelta()에는 치명적인 단점이 있는데, 바로 month와 year 단위의 계산이 불가능하다는 점이다.

timedelta에 가능한 단위
days 일
seconds 초
microseconds 마이크로 초
milliseconds 밀리 초 (1밀리 초는 1000마이크로 초)
minutes 분
hours 시간
weeks 주 (7일을 의미함)

dateutil.relativedelta()

그래서 months, years 단위를 사용하기 위해 dateutil의 relativedelta() 함수를 사용할 수 있다.
Python : timedelta(months=3) 방법

from dateutil.relativedelta import relativedelta
import datetime as dt 

now = dt.datetime.now()
delta = relativedelta(months=3)

diff = now - delta

풀이 과정

이제 앞서 설명한 함수들을 동원하여 문제를 풀어보도록 하자.

terms와 privacies 분리

먼저 terms와 privacies에 들어있는 값들을 구분자를 통해 split을 해주어야 한다
다만 privacies의 경우, 날짜 데이터는 현재 형태 그대로 사용('%Y.%m.%d')이 가능하므로 놔두고, ' '만을 구분자로 진행하면 된다.

terms : 약관 종류와 유효기간 분리
privacies : 날짜와 약관 종류 분리

def split_blank_for_list(lst):
    lll = []
    for l in lst:
        lll.append(l.split(' '))
    return lll

날짜 계산

privacies에서 날짜값을 뽑아내, 약관 종류에 따라 date_add를 수행한다
앞서 언급한 대로 '%Y.%m.%d' 형태 그대로 사용했다

def make_after_date(terms, privacies):
    for new_privacy in privacies:
        after_months = 0
        for new_term in terms:
            if new_privacy[1] == new_term[0]:
                after_months = int(new_term[1])
                new_privacy[0] = (datetime.strptime(new_privacy[0], '%Y.%m.%d').date() + relativedelta(months= after_months)).strftime('%Y.%m.%d')
    return privacies

사실 여기서 바로 today와 비교하는 것이 코드는 더 짧은데,
이왕 하는 거 한 번 날짜를 계산해서 반환하여 정리하는 과정을 가졌다

최종 코드

마지막으로 앞서 만든 2개의 함수를 사용해 문제 요구 사항을 충족시키는 정답 함수를 만들었다.
id는 1번부터 시작하므로 enumerate로 뽑아낸 idx에 +1을 해줘서 result에 append 해주었다.

from datetime import datetime
from dateutil.relativedelta import relativedelta

def split_blank_for_list(lst):
    lll = []
    for l in lst:
        lll.append(l.split(' '))
    return lll

def make_after_date(terms, privacies):
    for new_privacy in privacies:
        after_months = 0
        for new_term in terms:
            if new_privacy[1] == new_term[0]:
                after_months = int(new_term[1])
                new_privacy[0] = (datetime.strptime(new_privacy[0], '%Y.%m.%d').date() + relativedelta(months= after_months)).strftime('%Y.%m.%d')
    return privacies

def solution(today, terms, privacies):
    answer = []
    new_terms = split_blank_for_list(terms)
    new_privacies = split_blank_for_list(privacies)
    
    final_privacies = make_after_date(new_terms, new_privacies)
    
    for i, privacy in enumerate(final_privacies):
        if datetime.strptime(privacy[0], '%Y.%m.%d').date() <= datetime.strptime(today, '%Y.%m.%d').date():
            answer.append(i+1)
    
    return answer
profile
어제보다 오늘 더

0개의 댓글