본문 바로가기
기술스택을 쌓아보자/Python

[Codewars 6kyu / Python]- datetime 패키지로 날짜 빼고 시간만 처리하기/시간 연산하기

by 소리331 2023. 4. 16.
반응형

문제

여러분은 한 기업의 컴퓨팅 전문가입니다. 특정 시간들에 대한 인풋값을 입력하면 해당 시간의 range, avg, median 값을 출력해야 합니다. 예상되는 input과 output의 형태는 아래와 같습니다.

"01|15|59, 1|47|6, 01|17|20, 1|32|34, 2|3|17"

=> "Range: 00|47|18 Average: 01|35|15 Median: 01|32|34"

- 이 문제는 날짜를 다룰 필요가 없습니다. 

- 시간은 60진수 바탕이기 때문에 일반적인 숫자처럼 다루기 보다는 time 형태로 변환하여 연산해야합니다.

 

Codewars - Achieve mastery through coding practice and developer mentorship

A coding practice website for all programming levels – Join a community of over 3 million developers and improve your coding skills in over 55 programming languages!

www.codewars.com

[삼천포] datetime 패키지로 시간만 처리하기

보통 datetime 을 쓸 때, datetime.datetime 혹은 datetime.date 패키지를 사용한다. 여기서 datetime.datetime 과 datetime.date의 차이는, date는 시간이 없고 datetime 은 시간 처리가 있다는 것이다. 그러나 datetime은 시간만 단독으로 쓰지 못한다. 때문에 datetime.time을 사용한다.

from datetime import datetime, date, time

datetime(2023, 4, 15, 2, 3, 1) #연월일시분초
date(2023, 4, 15) #연월일
time(2, 3, 1) #시분초

그런데 이렇게 날짜, 시간객체로 변환하면 우리가 원하는 평균, 빼기 등의 연산이 되지 않는다. 때문에 사실 이 문제에서는 time 보다는 timedelta로 변환하는게 적합해보인다.

from datetime import time

time(2,3,3) - time(2,3,1)

# 연산이 되지 않는다.
TypeError                                 Traceback (most recent call last)
<ipython-input-13-7a269ee28cf9> in <cell line: 1>()
----> 1 time(2,3,3) - time(2,3,1)

TypeError: unsupported operand type(s) for -: 'datetime.time' and 'datetime.time

 

시간 객체 연산하기(time operation by timedelta)

때문에 위의 문제를 풀기 위해, 나는 모든 input 값을 timedelta로 변환했다. 이후에는 아래와 같은 연산이 가능해진다.

timedelta(hours=2,minutes=3,seconds=3) - timedelta(hours=2,minutes=3,seconds=1)

# 결과
datetime.timedelta(seconds=2)

공홈에서는 아래처럼 한다

from datetime import timedelta
>>> year = timedelta(days=365)
>>> ten_years = 10 * year
>>> ten_years
datetime.timedelta(days=3650)
>>> ten_years.days // 365
10
>>> nine_years = ten_years - year
>>> nine_years
datetime.timedelta(days=3285)
>>> three_years = nine_years // 3
>>> three_years, three_years.days // 365
(datetime.timedelta(days=1095), 3)

 

문제 풀이

나는 아래와 같이 풀었다.

from datetime import time, timedelta, date

def stat(strg):
    if strg == "":
        return strg
    
    inps = []
    for t in [
        t.split("|") for t in strg.replace(" ", "").split(",")
    ]:
        t = list(map(int, t))
        inps.append(timedelta(hours=t[0], minutes=t[1], seconds=t[2]))
    inps = sorted(inps)

    average = cal_avg(inps)
    range_ = cal_range(inps)
    mid = cal_mid(inps)
    
    return f"Range: {range_} Average: {average} Median: {mid}"


def trim(i):
    return str(i).zfill(2)


def time_format(td):
    r = td if type(td) in [int, float] else td.seconds
    h, m, s = int(r//3600), int(r%3600//60), int(r%60)
    print(h,m,s)
    return f"{trim(h)}|{trim(m)}|{trim(s)}"

def cal_avg(inps):
    total_seconds = sum(list(map(lambda x: x.seconds, inps)))
    mean_seconds = total_seconds/len(inps)
    return time_format(mean_seconds)

def cal_range(inps):
    min_, max_  = inps[0], inps[-1]
    r = max_ - min_
    return time_format(r)

def cal_mid(inps):
    if len(inps)%2==0:
        idx=len(inps)//2
        mid = (inps[idx-1] + inps[idx])/2
    else:
        idx=len(inps)//2+1
        mid = inps[idx-1]
    return time_format(mid)

 

Codewars는 다 풀고 나면 타인의 풀이도 공유되는데, 아래처럼 time 객체를 사용하지 않고 푸는 케이스도 있었다.

from statistics import median, mean

def stat(s):
    if not s: return ''

    t = [itime(w) for w in s.split(',')]
    return 'Range: {} Average: {} Median: {}'.format(stime(max(t) - min(t)), stime(int(mean(t))), stime(int(median(t))))

def itime(w):
    return sum([int(c) * 60**i for i, c in enumerate(w.split('|')[::-1])])
    
def stime(n):
    return '{:02d}|{:02d}|{:02d}'.format(n // 3600, (n % 3600) // 60, n % 60)

참조링크

 

datetime — Basic date and time types

Source code: Lib/datetime.py The datetime module supplies classes for manipulating dates and times. While date and time arithmetic is supported, the focus of the implementation is on efficient attr...

docs.python.org

 

반응형

댓글