DateTime 클래스
DateTime 클래스
Date의 하위 클래스로, 날짜뿐 아니라 시·분·초와 오프셋(offset)까지 쉽게 다루게 해 주는 클래스가 DateTime이에요.
경고:
DateTime클래스는 deprecated(사용 중단)로 간주되요. 새 코드에서는Time클래스를 쓰는 걸 권장해요.DateTime는 윤초(leap second)를 고려하지 않고(1930년대부터 도입된 UTC+08:00 12월 31일 23:59:60 같은) 서머타임 규칙도 추적하지 않아요.
만들기
DateTime 객체는 DateTime::new, DateTime::jd, DateTime::ordinal, DateTime::commercial, DateTime::parse, DateTime::strptime, DateTime::now, Time#to_datetime 등으로 만들어요.
require 'date'
DateTime.new(2001,2,3,4,5,6)
#=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
일·시·분·초의 마지막 요소는 분수(fractional number)일 수 있어요. 분수의 정밀도는 최대 나노초로 간주해요.
DateTime.new(2001,2,3.5)
#=> #<DateTime: 2001-02-03T12:00:00+00:00 ...>
선택 인자 offset은 현지 시간과 UTC의 차이를 나타내요. 예를 들어 Rational(3,24)는 UTC보다 3시간 앞선 것을, Rational(-5,24)는 5시간 뒤진 것을 나타내요. offset은 -1부터 +1까지여야 하고 정밀도는 최대 초로 간주돼요. 기본값은 0이에요(UTC와 동일).
DateTime.new(2001,2,3,4,5,6,Rational(3,24))
#=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
offset은 문자열 형태도 받아요:
DateTime.new(2001,2,3,4,5,6,'+03:00')
#=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
선택 인자 달력 개혁일(start)은 율리우스 일수를 나타내며 2298874~2426355 또는 음/양의 무한대여야 해요. 기본값은 Date::ITALY(2299161 = 1582-10-15)예요.
d = DateTime.parse('3rd Feb 2001 04:05:06+03:30')
#=> #<DateTime: 2001-02-03T04:05:06+03:30 ...>
d.hour #=> 4
d.min #=> 5
d.sec #=> 6
d.offset #=> (7/48)
d.zone #=> "+03:30"
d += Rational('1.5')
#=> #<DateTime: 2001-02-04%16:05:06+03:30 ...>
d = d.new_offset('+09:00')
#=> #<DateTime: 2001-02-04%21:35:06+09:00 ...>
d.strftime('%I:%M:%S %p')
#=> "09:35:06 PM"
d > DateTime.new(1999)
#=> true
DateTime vs Time — 언제 뭘 써야 할까?
셰익스피어와 세르반테스가 같은 날에 죽었다는 건 역사적으로 알려진 사실이라, 유네스코는 이 때문에 4월 23일을 세계 책의 날로 정했어요. 그런데 영국은 그레고리력 개혁을 아직 채택하지 않았었고(1752년까지), 그래서 실제로는 두 사람의 죽음은 10일이나 차이가 나요. Ruby의 Time 클래스는 proleptic 그레고리력을 구현하고 달력 개혁이라는 개념 자체가 없기 때문에, 이걸 Time 객체로는 표현할 수가 없어요. 여기서 DateTime이 필요해 지는 거죠:
shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND)
#=> Tue, 23 Apr 1616 00:00:00 +0000
cervantes = DateTime.iso8601('1616-04-23', Date::ITALY)
#=> Sat, 23 Apr 1616 00:00:00 +0000
벌써 이상한 점이 보여요 — 요일이 서로 달라요. 더 나아가 보면:
cervantes == shakespeare
#=> false
(shakespeare - cervantes).to_i
#=> 10
즉 실제로는 10일 차이가 나는 거예요(실제로는 세르반테스가 하루 일찍 죽었지만 23일에 묻혔으니 11일 차이). 셰익스피어의 실제 사망일은 gregorian 메서드로 변환하면 확인할 수 있어요:
shakespeare.gregorian
#=> Tue, 03 May 1616 00:00:00 +0000
즉 영국이 지금 그레고리력을 쓰고 있으니 스트랫퍼드어폰에이번에서 4월 23일에 벌어지는 모든 축제는 사실 엉뚱한 날짜라는 주장이 성립해요. 개혁일 경계를 넘어갈 때 왜 그런지 볼 수 있어요:
# start off with the anniversary of Shakespeare's birth in 1751
shakespeare = DateTime.iso8601('1751-04-23', Date::ENGLAND)
#=> Tue, 23 Apr 1751 00:00:00 +0000
# add 366 days since 1752 is a leap year and April 23 is after February 29
shakespeare + 366
#=> Thu, 23 Apr 1752 00:00:00 +0000
# add another 365 days to take us to the anniversary in 1753
shakespeare + 366 + 365
#=> Fri, 04 May 1753 00:00:00 +0000
보시다시피, 셰익스피어 생일 이후 태양년 수를 정확히 추적한다면 정확한 기념일은 4월 23일이 아니라 5월 4일이 돼요.
그럼 Ruby에서 언제 DateTime을 쓰고 언제 Time을 써야 할까요? 거의 확실히 Time을 쓰게 될 거예요 — 여러분의 앱은 대개 현재의 날짜와 시간을 다루니까요. 하지만 역사적 맥락의 날짜와 시간을 다뤄야 한다면, 유네스코 같은 실수를 피하기 위해 DateTime을 쓰면 좋아요. 시간대까지 다뤄야 한다면 행운을 빌어요 — 현지 태양시(local solar time)를 다루게 될 테니까요. 표준시와 시간대가 등장한 건 19세기 철도가 도입되면서부터니까요.
클래스 메서드
-
_strptime(string[, format='%FT%T%z']) → hash— 날짜/시간 표현을 주어진 템플릿으로 파싱해서 파싱된 요소들의 해시를 돌려줘요._strptime은strftime과 달리 플래그와 폭을 지원하지 않아요.DateTime.new와 동일해요. -
commercial([cwyear=-4712[, cweek=1[, cwday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) → datetime— 주어진 주 날짜(week date)를 나타내는DateTime객체를 만들어요.DateTime.commercial(2001) #=> #<DateTime: 2001-01-01T00:00:00+00:00 ...> DateTime.commercial(2002) #=> #<DateTime: 2001-12-31T00:00:00+00:00 ...> DateTime.commercial(2001,5,6,4,5,6,'+7') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) → datetime— RFC 2616 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT') #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>문자열 길이가
limit보다 길면ArgumentError를 던져요.limit: nil을 넘기면 이 검사를 멈출 수 있는데, 파싱이 오래 걸릴 수 있다는 점 유의하세요. -
iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) → datetime— 전형적인 ISO 8601 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.iso8601('2001-02-03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> DateTime.iso8601('20010203T040506+0700') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> DateTime.iso8601('2001-W05-6T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
jd([jd=0[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]) → datetime— 주어진 역법상 율리우스 일수를 나타내는DateTime객체를 만들어요.DateTime.jd(2451944) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...> DateTime.jd(2451945) #=> #<DateTime: 2001-02-04T00:00:00+00:00 ...> DateTime.jd(Rational('0.5')) #=> #<DateTime: -4712-01-01T12:00:00+00:00 ...> -
jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) → datetime— 전형적인 JIS X 0301 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.jisx0301('H13.02.03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>연호(era)가 없는 구식 형식이면 헤이세이(平成)로 간주해요.
DateTime.jisx0301('13.02.03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
now([start=Date::ITALY]) → datetime— 현재 시각을 나타내는DateTime객체를 만들어요.DateTime.new와 동일해요.DateTime.now #=> #<DateTime: 2011-06-11T21:20:44+09:00 ...> -
ordinal([year=-4712[, yday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]) → datetime— 주어진 서수 날짜(ordinal date)를 나타내는DateTime객체를 만들어요.DateTime.ordinal(2001,34) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...> DateTime.ordinal(2001,34,4,5,6,'+7') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) → datetime— 날짜/시간 표현을 파싱해DateTime객체를 만들어요.이 메서드는 validator가 아니에요. 입력 문자열이 유효한 형식과 엄격히 맞지 않으면 이해하기 어려운 결과가 나올 수 있어요. 가능하면
DateTime.strptime을 쓰는 걸 고려하세요.두 번째 선택 인자가 true이고 감지된 연도가 "00"~"99" 범위면 full 연도로 만들어줘요.
DateTime.parse('2001-02-03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> DateTime.parse('20010203T040506+0700') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> DateTime.parse('3rd Feb 2001 04:05:06 PM') #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...> -
rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) → datetime— 전형적인 RFC 2822 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) → datetime— 전형적인 RFC 3339 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.rfc3339('2001-02-03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> -
strptime([string='-4712-01-01T00:00:00+00:00'[, format='%FT%T%z'[, start=Date::ITALY]]]) → datetime— 날짜/시간 표현을 주어진 템플릿으로 파싱해서DateTime객체를 만들어요.strftime과 달리 플래그와 폭을 지원하지 않아요. strptime(3)과strftime도 함께 보세요.DateTime.strptime('2001-02-03T04:05:06+07:00', '%Y-%m-%dT%H:%M:%S%z') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> DateTime.strptime('03-02-2001 04:05:06 PM', '%d-%m-%Y %I:%M:%S %p') #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...> DateTime.strptime('-1', '%s') #=> #<DateTime: 1969-12-31T23:59:59+00:00 ...> -
xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) → datetime— 전형적인 XML Schema 형식의 문자열에서 파싱해 새DateTime객체를 만들어요.DateTime.xmlschema('2001-02-03T04:05:06+07:00') #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
인스턴스 메서드
-
deconstruct_keys(array_of_names_or_nil) → hash— 패턴 매칭에 쓰는 이름/값 쌍의 해시를 돌려줘요. 가능한 키는:year,:month,:day,:wday,:yday,:hour,:min,:sec,:sec_fraction,:zone이에요.dt = DateTime.new(2022, 10, 5, 13, 30) if d in wday: 1..5, hour: 10..18 # uses deconstruct_keys underneath puts "Working time" end #=> prints "Working time" -
hour → integer— 시(0..23)를 돌려줘요:DateTime.new(2001, 2, 3, 4, 5, 6).hour # => 4 -
iso8601([n=0]) → string—strftime('%FT%T%:z')와 동일해요. 선택 인자n은 분수 초의 자릿수예요.DateTime.parse('2001-02-03T04:05:06.123456789+07:00').iso8601(9) #=> "2001-02-03T04:05:06.123456789+07:00" -
jisx0301([n=0]) → string— JIS X 0301 형식 문자열을 돌려줘요.n은 분수 초 자릿수예요.DateTime.parse('2001-02-03T04:05:06.123456789+07:00').jisx0301(9) #=> "H13.02.03T04:05:06.123456789+07:00" -
min → integer— 분(0..59)을 돌려줘요:DateTime.new(2001, 2, 3, 4, 5, 6).min # => 5 -
new_offset([offset=0]) → date—self를 복제하고 offset을 재설정해요.d = DateTime.new(2001,2,3,4,5,6,'-02:00') #=> #<DateTime: 2001-02-03T04:05:06-02:00 ...> d.new_offset('+09:00') #=> #<DateTime: 2001-02-03T15:05:06+09:00 ...> -
offset → rational— offset을 돌려줘요:DateTime.parse('04pm+0730').offset #=> (5/16) -
rfc3339([n=0]) → string—strftime('%FT%T%:z')와 동일해요.n은 분수 초 자릿수예요. -
sec → integer— 초(0..59)를 돌려줘요:DateTime.new(2001, 2, 3, 4, 5, 6).sec # => 6 -
sec_fraction → rational— 초의 분수 부분(Rational(0,1)…Rational(1,1))을 돌려줘요:DateTime.new(2001, 2, 3, 4, 5, 6.5).sec_fraction # => (1/2) -
strftime(format = '%FT%T%:z') → string— 주어진format에 따라self의 문자열 표현을 돌려줘요:DateTime.now.strftime # => "2022-07-01T11:03:19-05:00" -
to_date → date—self를 나타내는Date객체를 돌려줘요. -
to_datetime → self—self를 그대로 돌려줘요. -
to_s → string— ISO 8601 형식 문자열을 돌려줘요(확장 표현은 쓰지 않아요):DateTime.new(2001,2,3,4,5,6,'-7').to_s #=> "2001-02-03T04:05:06-07:00" -
to_time → time—self를 나타내는Time객체를 돌려줘요. -
xmlschema([n=0]) → string—strftime('%FT%T%:z')와 동일해요.n은 분수 초 자릿수예요. -
zone → string— 시간대를 돌려줘요:DateTime.parse('04pm+0730').zone #=> "+07:30"
JSON 직렬화
DateTime#as_json과 DateTime.json_create 메서드로 DateTime 객체를 직렬화/역직렬화할 수 있어요; 자세한 건 Marshal을 보세요. as_json은 self를 2-요소 해시로 돌려줘요:
require 'json/add/datetime'
x = DateTime.now.as_json
# => {"json_class"=>"DateTime", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}