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 — 날짜/시간 표현을 주어진 템플릿으로 파싱해서 파싱된 요소들의 해시를 돌려줘요. _strptimestrftime과 달리 플래그와 폭을 지원하지 않아요. 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]) → stringstrftime('%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]) → dateself를 복제하고 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]) → stringstrftime('%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 → dateself를 나타내는 Date 객체를 돌려줘요.

  • to_datetime → selfself를 그대로 돌려줘요.

  • 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 → timeself를 나타내는 Time 객체를 돌려줘요.

  • xmlschema([n=0]) → stringstrftime('%FT%T%:z')와 동일해요. n은 분수 초 자릿수예요.

  • zone → string — 시간대를 돌려줘요: DateTime.parse('04pm+0730').zone #=> "+07:30"

JSON 직렬화

DateTime#as_jsonDateTime.json_create 메서드로 DateTime 객체를 직렬화/역직렬화할 수 있어요; 자세한 건 Marshal을 보세요. as_jsonself를 2-요소 해시로 돌려줘요:

require 'json/add/datetime'
x = DateTime.now.as_json
# => {"json_class"=>"DateTime", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}

출처: Ruby 4.0 API - DateTime

더 알아보기

  • Date — 날짜만 다루는 부모 클래스
  • Time — deprecated된 DateTime 대신 권장되는 클래스