Ruby에서 DateTime과 시간의 차이


219

Ruby의 클래스 DateTimeTime클래스 의 차이점은 무엇이며 어떤 요소를 선택해야합니까?


문서에는 을 사용하는시기를 설명 하는 섹션 이 있습니다.
x-yuri

답변:


177

최신 버전의 Ruby (2.0+)는 실제로 두 클래스간에 큰 차이가 없습니다. 일부 라이브러리는 역사적 이유로 하나 또는 다른 라이브러리를 사용하지만 새로운 코드가 반드시 필요한 것은 아닙니다. 일관성을 위해 하나를 선택하는 것이 가장 좋을 것이므로 라이브러리가 기대하는 것과 맞물려보십시오. 예를 들어 ActiveRecord는 DateTime을 선호합니다.

Ruby 1.9 이전 버전과 많은 시스템에서 Time은 POSIX 표준 time_t값을 기준으로 한 얇은 랩퍼 인 UTC 1970 년 1 월 1 일 이후의 시간 (초)을 설명하는 32 비트 부호있는 값으로 표시됩니다 .

Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901

최신 버전의 Ruby는 오류없이 더 큰 값을 처리 할 수 ​​있습니다.

DateTime은 연도, 월, 일,시, 분 및 초가 개별적으로 저장되는 달력 기반 접근 방식입니다. 이것은 SQL 표준 DATETIME 필드에 대한 랩퍼 역할을하는 Ruby on Rails 구성입니다. 여기에는 임의의 날짜가 포함되며 일반적으로 표현 범위가 매우 크기 때문에 거의 모든 시점을 나타낼 수 있습니다.

DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000

따라서 DateTime이 Aristotle의 블로그 게시물을 처리 할 수 ​​있다는 것이 안심입니다.

하나를 선택할 때, 차이점은 이제 다소 주관적입니다. 역사적으로 DateTime은 달력 방식으로이를 조작하기위한 더 나은 옵션을 제공했지만 이러한 방법 중 다수는 적어도 Rails 환경 내에서 Time으로 이식되었습니다.


6
항상 DateTime을 사용해야합니까?
Tom Lehman

4
날짜로 작업하는 경우 DateTime을 사용하십시오. 시간은 현재 시간 또는 10.minutes.from_now와 같은 가까운 미래의 지점과 같은 것을 나타내는 데 편리합니다. 언급 된 것처럼 DateTime은 훨씬 더 넓은 범위의 값을 나타낼 수 있지만 둘은 공통점이 있습니다.
tadman

3
Ruby가 오버플로가 발생하면 32 비트 Fixnum에서 임의 길이의 Bignum으로 전환하기 때문이라고 생각합니다. 해당 범위를 벗어난 숫자는 외부 응용 프로그램에서 지원하지 않을 수 있습니다. 그렇습니다 .2038 년에 우리는 모두 적절한 64 비트 시간 형식에 동의 할 때까지 기본적으로 망했습니다. 배심원은 아직 외출 중입니다
tadman

28
이 답변은 1.9.2 이전입니다. Time의 posix 한계에 대해 말하는 모든 것을 무시하고 Time 및 DateTime의 API를 기반으로 선택하십시오.
Ben Nagy

8
이 답변은 구식입니까? 이제 Time 또는 Rail의 ActiveSupport :: WithTimeZone을 사용하는 것이 좋습니다. 더 이상 DateTime을 사용할 필요가 없습니다. 이전 버전과의 호환성을 위해 존재합니다.
Donato 2016 년

103

[2018 년 7 월 편집]

아래의 모든 사항은 Ruby 2.5.1에서도 여전히 유효합니다. 로부터 참조 문서 :

DateTime은 윤초를 고려하지 않으며 하계 시간 규칙을 추적하지 않습니다.

전에이 스레드에서 언급되지 않은 것은 몇 가지 장점 중 하나입니다. DateTime달력 개혁을 알고 있지만 Time그렇지는 않습니다.

[…] 루비의 Time 클래스는 다독 그레고리력을 구현하며 달력 개혁의 개념은 없습니다.

참조 문서는 Time과거, 현재 또는 미래의 날짜 / 시간을 독점적으로 다룰 때 사용할 것을 권장하고 DateTime, 예를 들어 셰익스피어의 생일을 정확하게 변환해야하는 경우 에만 사용하도록 권장 합니다. (강조 추가)

그렇다면 Ruby에서 DateTime을 언제 사용해야하고 언제 Time을 사용해야합니까? 앱이 현재 날짜와 시간을 처리하고 있기 때문에 시간을 사용하는 것이 좋습니다. 그러나 과거 상황에서 날짜와 시간을 처리해야하는 경우 DateTime […] 을 사용하는 것이 좋습니다 . 시간대를 다뤄야한다면 운이 좋을 것입니다. 19 세기까지는 철도 도입으로 표준시가 필요하지 않았기 때문에 현지 태양 시간을 다룰 것입니다. 그리고 결국 시간대.

[/ 2018 년 7 월 편집]

루비 2.0부터 다른 답변의 정보 대부분이 오래되었습니다.

특히, Time지금은 사실상 약속이 없습니다. Epoch에서 63 비트 이상 떨어져있을 수 있습니다.

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> Time.at(2**62-1).utc # within Integer range
=> 146138514283-06-19 07:44:38 UTC
irb(main):003:0> Time.at(2**128).utc # outside of Integer range
=> 10783118943836478994022445751222-08-06 08:03:51 UTC
irb(main):004:0> Time.at(-2**128).utc # outside of Integer range
=> -10783118943836478994022445747283-05-28 15:55:44 UTC

더 큰 값을 사용하는 유일한 결과는 성능이어야합니다. 이는 Integers (vs. Bignum( Integer범위 밖의 값 ) 또는 Rationals (나노초를 추적 할 때 )를 사용할 때 더 좋습니다 ) :

Ruby 1.9.2부터 Time 구현은 부호있는 63 비트 정수, Bignum 또는 Rational을 사용합니다. 정수는 1823-11-12에서 2116-02-20을 나타낼 수있는 Epoch 이후 수 나노초입니다. Bignum 또는 Rational을 사용하면 (1823 이전, 2116 후, 나노초 미만), 정수를 사용할 때와 같이 시간이 느리게 작동합니다. ( http://www.ruby-doc.org/core-2.1.0/Time.html )

다시 말해, 내가 이해하는 한, DateTime더 이상 광범위한 잠재적 가치를 다루지 않습니다Time .

또한 앞에서 언급하지 않은 두 가지 제한 사항에 DateTime유의해야합니다.

DateTime은 윤초를 고려하지 않으며 하계 시간 규칙을 추적하지 않습니다. ( http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime )

첫째, DateTime윤초의 개념이 없습니다.

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.new(2012,6,30,23,59,60,0)
=> 2012-06-30 23:59:60 +0000
irb(main):004:0> dt = t.to_datetime; dt.to_s
=> "2012-06-30T23:59:59+00:00"
irb(main):005:0> t == dt.to_time
=> false
irb(main):006:0> t.to_i
=> 1341100824
irb(main):007:0> dt.to_time.to_i
=> 1341100823

위의 예제가 (으)로 작동 Time하려면 OS가 윤초를 지원해야하고 시간대 정보를 올바르게 설정해야합니다 (예 : TZ=right/UTC irb많은 유닉스 시스템에서).

둘째, DateTime시간대에 대한 이해가 매우 제한적이고 특히 일광 절약의 개념이 없습니다 . 시간대를 간단한 UTC + X 오프셋으로 처리합니다.

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.local(2012,7,1)
=> 2012-07-01 00:00:00 +0200
irb(main):004:0> t.zone
=> "CEST"
irb(main):005:0> t.dst?
=> true
irb(main):006:0> dt = t.to_datetime; dt.to_s
=> "2012-07-01T00:00:00+02:00"
irb(main):007:0> dt.zone
=> "+02:00"
irb(main):008:0> dt.dst?
NoMethodError: undefined method `dst?' for #<DateTime:0x007f34ea6c3cb8>

시간이 DST로 입력 된 후 DateTime자체 외부의 올바른 오프셋을 추적하지 않고 DST가 아닌 시간대로 변환 될 때 문제가 발생할 수 있습니다 (많은 운영 체제가 실제로이를 대신 할 수 있음).

전반적으로 요즘 Time은 대부분의 응용 프로그램에 더 나은 선택 이라고 말하고 싶습니다 .

또한 더하기의 중요한 차이점에 유의하십시오. Time 객체에 숫자를 추가하면 초 단위로 계산되지만 DateTime에 숫자를 추가하면 일 단위로 계산됩니다.


2018 년에도 이것이 사실입니까?
Qqwy

2
Ruby 2.5.1에서는 여전히 그렇습니다. ruby-doc.org/stdlib-2.5.1/libdoc/date/rdoc/DateTime.html : "DateTime은 윤초를 고려하지 않으며 하계 규칙을 추적하지 않습니다." 그러나 DateTime은 그레고리력 이전의 달력 날짜 / 시간을 처리해야 할 때 이점이 있습니다. 이것에 대해서는 이전에 언급하지 않았지만 참조 문서에 문서화되어 있습니다 : "[…] 루비의 Time 클래스는 그레고리력 달력을 구현하고 달력 개혁의 개념은 없습니다 [… 자세한 내용을 제공하기 위해 답변을 편집하겠습니다.
Niels Ganser

Time윤초의 개념이 없으므로와 다릅니다 DateTime. 나는 당신이 당신의 예제를 어디에서 실행했는지 모르지만 Time.new(2012,6,30,23,59,60,0)2.0에서 2.7까지 다른 루비 버전으로 시도했지만 항상 얻었습니다 2012-07-01 00:00:00 +0000.
michau

@michau Time윤초 지원 여부 는 OS 및 시간대 구성에 따라 다릅니다. 예를 들어 TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-06-30 23:59:60 +0000반면 TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-07-01 00:00:00 +0000입니다.
Niels Ganser

@NielsGanser 감사합니다! 이 점을 명확히하기 위해 편집을 제안했습니다.
michau

44

"차이점"에 대한 대답은 Ruby 표준 라이브러리에서이 질문에 대한 불행한 일반적인 답변 중 하나라고 생각합니다. 두 클래스 / lib는 다른 시간에 다른 사람들에 의해 다르게 만들어졌습니다. Java와 같은 신중하게 계획된 개발과 비교할 때 Ruby의 진화로 인한 커뮤니티 특성의 불행한 결과 중 하나입니다. 개발자는 새로운 기능을 원하지만 기존 API를 밟고 싶지 않기 때문에 새로운 클래스를 만들면됩니다. 최종 사용자에게는이 둘이 존재해야 할 이유가 없습니다.

이것은 일반적으로 소프트웨어 라이브러리의 경우에 해당합니다. 종종 일부 코드 또는 API가 논리적이 아닌 역사적으로 밝혀지는 방식입니다.

유혹은 더 일반적인 것처럼 보이기 때문에 DateTime으로 시작하는 것입니다. 날짜와 시간? 잘못된. 시간은 또한 날짜를 더 잘 수행하며 실제로 DateTime이 할 수없는 시간대를 구문 분석 할 수 있습니다. 또한 더 잘 수행합니다.

나는 어디에서나 시간을 사용하게되었습니다.

안전하기 위해 DateTime 인수를 Timey API로 전달하고 변환하는 경향이 있습니다. 또한 둘 다 내가 관심있는 방법을 가지고 있음을 알고 있다면,이 방법과 같이 시간을 XML로 변환하기 위해 작성한 방법 (XMLTV 파일의 경우)

# Will take a date time as a string or as a Time or DateTime object and
# format it appropriately for xmtlv. 
# For example, the 22nd of August, 2006 at 20 past midnight in the British Summertime
# timezone (i.e. GMT plus one hour for DST) gives: "20060822002000 +0100"
def self.format_date_time(date_time)
  if (date_time.respond_to?(:rfc822)) then
    return format_time(date_time)
  else 
    time = Time.parse(date_time.to_s)
    return format_time(time)
  end    
end

# Note must use a Time, not a String, nor a DateTime, nor Date.
# see format_date_time for the more general version
def self.format_time(time)
  # The timezone feature of DateTime doesn't work with parsed times for some reason
  # and the timezone of Time is verbose like "GMT Daylight Saving Time", so the only
  # way I've discovered of getting the timezone in the form "+0100" is to use 
  # Time.rfc822 and look at the last five chars
  return "#{time.strftime( '%Y%m%d%H%M%S' )} #{time.rfc822[-5..-1]}"
end

8
또한 Time.new와 DateTime.new는 시간대를 다르게 처리합니다. 그래서, GMT + 7에있어 Time.new(2011, 11, 1, 10, 30)생산 2011-11-01 10:30:00 +0700하면서 DateTime.new(2011, 11, 1, 10, 30)생산 Tue, 01 Nov 2011 10:30:00 +0000.
Phương Nguyễn

21
우리 모두 알다시피, 신중하게 계획된 Java 개발은 독점적으로 간단한 논리적 API를 가져 왔습니다.
pje

@ PhươngNguyễn : 당신이 그것을 투표 할 수 있도록 답변으로 추가 하시겠습니까? 이것이 바로 TimeTime보다 DateTime을 선택하기로 결정한 이유입니다.
Senseful

@ 감각 지금까지 메시지를받지 못했습니다. 사람들은 이미 내 의견에 약간의 의견을 제시하므로 여기가 멋지다고 생각합니다.
Phương Nguyễn

@ PhươngNguyễn 안녕하세요, 시간대에 차이가있는 이유 / 어떻게 생각하십니까? 어디에서 오프셋을 취합니까?
Joel_Blum

10

ActiveTime 확장을 사용한다고 가정하면 DateTime으로 다른 시간대의 하루 시작 / 종료를 구문 분석하고 계산하는 것이 더 쉽다 는 것을 알았습니다 .

필자의 경우 문자열로받은 사용자의 현지 시간 (예 : "2012-10-10 10:10 +0300")을 기준으로 사용자 시간대 (임의)에서 하루의 끝을 계산해야했습니다.

DateTime을 사용하면 간단합니다.

irb(main):034:0> DateTime.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 +0300
# it preserved the timezone +0300

이제 Time과 같은 방식으로 시도해 봅시다.

irb(main):035:0> Time.parse('2012-10-10 10:10 +0300').end_of_day
=> 2012-10-10 23:59:59 +0000
# the timezone got changed to the server's default UTC (+0000), 
# which is not what we want to see here.

실제로, 시간은 파싱하기 전에 시간대를 알아야합니다 (또한 Time.zone.parse아닙니다 Time.parse).

irb(main):044:0> Time.zone = 'EET'
=> "EET"
irb(main):045:0> Time.zone.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 EEST +03:00

따라서이 경우 DateTime을 사용하는 것이 훨씬 쉽습니다.


1
현재 프로덕션 환경에서이 기술을 사용하면이 기술의 단점이 있습니까?
Alex Moore-Niemi

1
DateTime은 일광 절약 시간을 고려하지 않습니다. 따라서 시간 변경을 절약하면 올바르게 작동하지 않습니다. DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day생성 Sun, 30 Mar 2014 23:59:59 +0100하지만 Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day생산 Sun, 30 Mar 2014 23:59:59 CEST +02:00(CET = + 01 : 00, CEST = + 02 : 00 - 오프셋 변경 통지). 그러나 이렇게하려면 사용자 시간대에 대한 자세한 정보가 필요합니다 (오프셋뿐만 아니라 시간 절약 사용 여부).
Petr ''Bubák ''

1
그것은 명백한 진술 할 수 있지만 Time.zone.parse입니다 매우 다른 영역과 시간을 구문 분석 할 때 유용합니다 - 그것은 당신이 어떤 영역에 대해 사용되어야한다고 생각 강제로. 때로는 Time.find_zone이 더 잘 작동합니다.
prusswan

1
@ Petr''Bubák''Šedivý DateTime는 당신이 준 오프셋 ( +0100)을 파싱했습니다. Time오프셋을 제공하지 않았지만 시간대 ( "CET"은 오프셋을 설명하지 않으며 시간대 이름입니다). 시간대는 일년 내내 다른 오프셋을 가질 수 있지만 오프셋은 오프셋이며 항상 같습니다.
Mecki

4

사용자 지정 인스턴스화를 사용하여 시간대를 다르게 처리하는 방법을 고려하십시오.

irb(main):001:0> Time.new(2016,9,1)
=> 2016-09-01 00:00:00 -0400
irb(main):002:0> DateTime.new(2016,9,1)
=> Thu, 01 Sep 2016 00:00:00 +0000
irb(main):003:0> Time.new(2016,9,1).to_i
=> 1472702400
irb(main):004:0> DateTime.new(2016,9,1).to_i
=> 1472688000

시간 범위 등을 만들 때 까다로울 수 있습니다.


일반 루비에서만 발생하는 것 같습니다 (예 : Rails 없음)
vemv

1

경우에 따라 동작이 매우 다른 것 같습니다.

Time.parse("Ends from 28 Jun 2018 12:00 BST").utc.to_s

"2018-06-28 09:00:00 UTC"

Date.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s

"2018-06-27 21:00:00 UTC"

DateTime.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s

"2018-06-28 11:00:00 UTC"


흥미로운 관찰이지만 왜 그런지 설명해야합니다. Date(의외로) 날짜 만 구문 분석하고 a Date로 변환 Time하면 항상 현지 시간대의 자정을 시간으로 사용합니다. 시간대가 일반적으로 (예를 들어 일광 절약 시간과 관련하여) 더 정확하게 처리된다는 점을 감안할 때 놀랍게도 BST를 이해하지 못한다 는 사실 Time과 차이가 있습니다. 따라서이 경우 전체 문자열 만 올바르게 구문 분석합니다. TimeDateTimeTimeDateTime
michau

1

Niels Ganser의 답변 외에도 다음과 같은 주장을 고려할 수 있습니다.

참고 루비 스타일 가이드는 아주 명확에서 위치를 상태 :

날짜 시간 없음

기록 일정 변경을 설명해야하는 경우가 아니면 DateTime을 사용하지 마십시오. 그렇게하면 의도를 명확하게 나타 내기 위해 start 인수를 명시 적으로 지정하십시오.

# bad - uses DateTime for current time
DateTime.now

# good - uses Time for current time
Time.now

# bad - uses DateTime for modern date
DateTime.iso8601('2016-06-29')

# good - uses Date for modern date
Date.iso8601('2016-06-29')

# good - uses DateTime with start argument for historical date
DateTime.iso8601('1751-04-23', Date::ENGLAND)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.