Ruby의 Time 클래스는 특정 시점의 시간을 표현할 때 사용하는 클래스입니다.
이 글을 끝까지 읽고 나면 Ruby에서 시간을 마스터하는 데 필요한 모든 지식을 갖추게 될 것입니다!
다루는 주제:
- 문자열을 시간 객체로 변환하는 방법
- 시간을 구성 요소(일/시/밀리초 등)로 분해하는 방법
- Ruby에서
Date,DateTime,Time클래스를 활용하는 방법 - 원하는 형식으로 시간을 포맷팅하는 방법
- 두
Time객체 간의 차이를 계산하는 방법
그럼 시작해 보겠습니다!
Ruby의 Time 클래스
Ruby에서 Time 클래스는 날짜와 시간을 함께 표현할 수 있습니다.
날짜(date)는 세 가지 요소로 구성됩니다:
- 일(day)
- 월(month)
- 연도(year)
그리고 시간(time)은 다음과 같습니다:
- 시(hours)
- 분(minutes)
- 초(seconds)
Time 클래스는 이 정보를 에포크(Epoch), 즉 유닉스 시간(Unix time) 이후 경과한 초 단위 숫자로 내부적으로 저장합니다.
Time 객체를 초기화하는 방법은 여러 가지가 있습니다:
Time.now를 사용하면 현재 시각을 나타내는 객체를 얻을 수 있습니다- 유닉스 타임스탬프와
at메서드로Time객체를 생성할 수 있습니다 Time.new에 시작 날짜를 숫자로 전달할 수 있습니다 (형식: 연도/월/일)
예시는 다음과 같습니다:
Time.now # 2018-10-19 15:43:20 +0200 Time.new(2018, 1, 1) # 2018-01-01 00:00:00 +0100 Time.at(15000000000) # 2017-07-14 04:40:00 +0200
시간 객체에게 원하는 구성 요소를 물어볼 수 있습니다.
예를 들어…
특정 시간 객체가 어떤 일, 월, 시를 나타내는지 확인할 수 있습니다:
t = Time.now puts t.day puts t.month puts t.hour
또한 해당 날짜가 특정 요일에 해당하는지도 확인할 수 있습니다.
예를 들어: "이 날짜는 일요일인가요?" 같은 질문입니다.
이런 메서드들을 프리디케이트(predicate) 메서드라고 하며, true 또는 false를 반환합니다.
예제:
t = Time.now puts t.monday? puts t.sunday? puts t.friday?
직접 실행해 보세요!
타임존(Time Zones) 다루기
Time 객체에는 항상 타임존 정보가 연결되어 있습니다.
zone 메서드를 사용하면 객체의 현재 타임존을 확인할 수 있으며, 결과는 타임존 약어로 반환됩니다.
타임존 오프셋(offset)이 필요하다면 utc_offset 메서드를 사용하세요. 결과는 초 단위로 반환되지만, 3600으로 나누면 시간 단위로 변환할 수 있습니다.
예제:
t = Time.now t.zone # "CET" t.utc_offset / 3600 # 1
UTC 기준 현재 시각도 손쉽게 구할 수 있습니다:
Time.now.utc
Ruby 시간 포맷팅
Ruby는 기본적으로 정해진 방식으로 Time 객체를 출력합니다.
하지만 사용 사례에 따라 다른 형식이 필요할 때가 있습니다.
좋은 소식이 있습니다!
거의 모든 형식을 원하는 대로 만들어 주는 메서드가 존재합니다.
그 메서드가 바로 strftime으로, 이름 그대로 'format time(시간을 포맷하라)'이라는 의미입니다.
사용법은 포맷 지정자(format specifier)가 담긴 문자열을 인자로 전달하는 것이며, 각 지정자가 실제 값으로 치환됩니다. printf 메서드를 사용해 본 적이 있다면 그 개념과 매우 유사합니다.
몇 가지 예제를 살펴보겠습니다:
time = Time.new
time.strftime("%d/%m/%Y") # "05/12/2015"
time.strftime("%k:%M") # "17:48"
time.strftime("%I:%M %p") # "11:04 PM"
time.strftime("Today is %A") # "Today is Sunday"
time.strftime("%d of %B, %Y") # "21 of December, 2015"
time.strftime("Unix time is %s") # "Unix time is 1449336630"
보시다시피 이 메서드는 매우 유연합니다. 날짜 없이 시간만 출력할 수도 있고, 연도·일·현재 월 이름이 포함된 보기 좋은 날짜 형식도 만들 수 있습니다.
Strftime 포맷 지정자 표
| 포맷 | 설명 |
|---|---|
| %d | 일 (01..31) |
| %m | 월 (01..12). 1..12 형태가 필요하면 %-m 사용 |
| %k | 시 (0..23) |
| %M | 분 |
| %S | 초 (00..60) |
| %I | 시 (1..12) |
| %p | AM/PM |
| %Y | 연도 |
| %A | 요일 (이름) |
| %B | 월 (이름) |
더 많은 포맷 옵션이 궁금하다면 아래 링크를 참고하세요:
- https://strftime.net/
- https://devdocs.io/ruby/time#method-i-strftime
타임스탬프 생성하기
타임존에 영향을 받지 않는 타임스탬프를 생성하면 특정 이벤트의 시각을 기록하기에 좋습니다.
방법은 다음과 같습니다:
Time.now.to_i # 1549054305
이 타임스탬프는 UTC(협정 세계시) 기준 초 단위 숫자입니다.
시간 차이 계산 (덧셈과 뺄셈)
때로는 현재 시각이 아니라 미래나 과거의 시각이 필요합니다.
Time 객체끼리 덧셈을 할 수 있다는 점을 기억하세요!
Time의 내부 표현이 '초' 단위이기 때문에 다음과 같이 작성할 수 있습니다:
# 10초 더하기 time = Time.new + 10
이 예제에서는 현재 시각에서 10초 뒤를 가리키는 시간 객체를 얻습니다.
그런 다음 그 시각이 이미 지났는지 확인할 수도 있습니다.
Time.new > time
'어제' 같은 날짜를 구하려면 하루가 몇 초인지 계산해야 합니다.
이렇게요:
# 초 * 분 * 시 60 * 60 * 24 # 86400
이제 이 숫자를 현재 날짜에서 빼면 됩니다:
Time.now - 86400
Rails를 사용 중이라면 훨씬 간결하게 표현할 수 있습니다:
Time.now - 1.day
Date 클래스
Date 클래스에는 분, 초, 시라는 개념이 없으며, 모든 값을 '일' 단위로 내부적으로 저장합니다.
Date 클래스를 사용하려면 먼저 require 'date'가 필요합니다.
현재 날짜는 Date.today로 구할 수 있습니다.
주의할 점은, Time과 달리 Date.new는 today의 별칭(alias)이 아니라는 것입니다.
Date.today # 현재 날짜 Date.new # 음수 날짜를 반환
Date 연산은 Time 클래스와 비슷하지만, 초가 아닌 '일' 단위로 더한다는 차이가 있습니다.
Date.today + 1 # 하루 더하기
Date는 시, 분, 초에 대한 정보를 전혀 가지고 있지 않으므로, 이런 정보가 필요 없을 때만 사용하는 것이 좋습니다.
날짜 및 시간 파싱(Parsing)
Date.parse 메서드는 날짜처럼 보이는 문자열을 파싱하려고 시도합니다.
입력된 형식을 감지하기 위한 간단한 휴리스틱 알고리즘을 사용하기 때문에, 때때로 의도치 않은 결과가 나올 수 있습니다.
예제들:
Date.parse("10/10/2010") # -> 2010-10-10
Date.parse("September 3") # -> 2015-09-03
Date.parse("May I have a cup of coffee, please?") # -> 5월 1일로 해석
더 엄격한 검증이 필요하다면 Date.iso8601 메서드를 사용하세요.
iso8601 날짜는 다음 형식을 따릅니다:
year-month-day
잘못된 입력이 들어오면 ArgumentError 예외가 발생합니다.
예제:
Date.iso8601("2018-10-01")
Date.strptime 메서드와 포맷 지정자 조합을 사용하면 직접 정의한 입력 형식을 처리할 수 있습니다. 이 지정자들은 strftime에서 사용하는 것과 동일합니다.
예제:
Date.strptime("3 of September", "%d of %B") # 2015-09-03
시간 파싱 역시 Time 클래스로 가능합니다.
다시 말해, '2018-01-01 00:00' 같은 문자열을 Time 객체로 변환하는 방법입니다!
예제:
require 'time'
Time.parse("September 20 18:00")
입력 문자열의 형식을 직접 지정하고 싶다면 strptime 메서드를 사용하세요.
이렇게요:
require 'time'
Time.strptime("1 of December 2017", "%d of %B %Y")
Time.parse와 Date.parse의 유일한 차이는 반환되는 객체의 종류(Time 또는 Date)입니다.
Date 상수 활용하기
Date 클래스에는 유용하게 쓸 수 있는 상수들이 몇 가지 있습니다.
예를 들어, 1년의 월 이름 배열과 요일 이름 배열이 제공됩니다.
월 배열은 인덱스 1부터 시작하기 때문에 '월 번호 → 월 이름' 매핑을 바로 얻을 수 있습니다.
요일 배열은 일요일부터 시작하지만, rotate 메서드를 사용하면 월요일 시작으로 변경할 수 있습니다.
Date::MONTHNAMES # (인덱스 0 = nil) Date::DAYNAMES.rotate(1)
DateTime 클래스
DateTime 클래스는 Date의 서브클래스로, 날짜에 더해 초 단위 정보까지 저장할 수 있습니다.
예제:
require 'date' DateTime.superclass # Date DateTime.now # DateTime: 2018-10-15T16:06:39+02:00
Time과 DateTime은 같은 작업을 수행할 수 있지만, 결정적인 차이는 Time이 C로 구현되어 있어 훨씬 빠르다는 점입니다.
성능 비교:
Comparison:
Time: 2644596.6 i/s
DateTime: 231634.8 i/s - 11.42x slower
ActiveSupport – Time & Date 확장 메서드
Rails를 사용해 본 적이 있다면 3.days.ago 같은 코드에 익숙할 것입니다.
이러한 메서드는 순수 Ruby에는 존재하지 않으며, Rails의 ActiveSupport 컴포넌트가 추가해 주는 기능입니다.
아래 예제들을 살펴보세요. 이 메서드들이 Time이나 Date 객체가 아닌 커스텀 ActiveSupport 클래스를 반환한다는 점에 주목하세요.
1.hour.to_i # 3600 1.day # ActiveSupport::Duration 3.days.ago # ActiveSupport::TimeWithZone
이 메서드들로 시간 연산을 수행하면 내일 날짜 같은 것도 쉽게 구할 수 있습니다:
Time.now + 1.day
Rails 전용으로 제공되는 다른 시간 관련 메서드들:
date = Time.current date.change(hour: 20) date.at_beginning_of_day
포맷팅 관련 메서드도 있습니다:
date = Date.today date.to_formatted_s(:short) # "16 Jul" date.to_formatted_s(:long) # "July 16, 2018"
이 메서드들의 구현 코드는 생각보다 단순하니, 한 번 직접 살펴보는 것을 추천합니다. TimeWithZone의 소스 코드도 볼만한 가치가 충분합니다.
마무리
이 글에서는 Time & Date 클래스의 기본 개념, Ruby에서 시간을 파싱하고 포맷팅하는 방법, 그리고 ActiveSupport의 시간 확장 기능 활용법까지 알아보았습니다.
읽어주셔서 감사합니다!