복붙노트

[RUBY-ON-RAILS] 루비 : 날짜의 범위를 만들기

RUBY-ON-RAILS

루비 : 날짜의 범위를 만들기

나는 예 : 날짜 시간의 범위를 만들기 위해 우아한 방법을 찾고 있어요 :

def DateRange(start_time, end_time, period)
  ...
end

>> results = DateRange(DateTime.new(2013,10,10,12), DateTime.new(2013,10,10,14), :hourly)
>> puts results
2013-10-10:12:00:00
2013-10-10:13:00:00
2013-10-10:14:00:00

구성하는 단계는, 예를 들어 있어야 시간별, 일별, 월별.

나는 시간이 포함 싶습니다, 즉 END_TIME을 포함한다.

추가 요구 사항은 다음과 같습니다 :

우아한 해결책이 있습니까?

해결법

  1. ==============================

    1.CaptainPete의 기본 @ 사용하여, 나는 ActiveSupport :: 날짜 시간 # 사전 전화를 사용하도록 수정했습니다. 달 "과": 년 "시간 간격은`로, 균일하지 않은 경우의 차이는 발효

    CaptainPete의 기본 @ 사용하여, 나는 ActiveSupport :: 날짜 시간 # 사전 전화를 사용하도록 수정했습니다. 달 "과": 년 "시간 간격은`로, 균일하지 않은 경우의 차이는 발효

    require 'active_support/all'
    class RailsDateRange < Range
      # step is similar to DateTime#advance argument
      def every(step, &block)
        c_time = self.begin.to_datetime
        finish_time = self.end.to_datetime
        foo_compare = self.exclude_end? ? :< : :<=
    
        arr = []
        while c_time.send( foo_compare, finish_time) do 
          arr << c_time
          c_time = c_time.advance(step)
        end
    
        return arr
      end
    end
    
    # Convenience method
    def RailsDateRange(range)
      RailsDateRange.new(range.begin, range.end, range.exclude_end?)
    end
    

    내 방법은 배열을 반환합니다. 비교를 위해, 또한 배열을 반환하는 CaptainPete의 대답 @ 변경 :

    RailsDateRange((4.years.ago)..Time.now).every(years: 1)
    => [Tue, 13 Oct 2009 11:30:07 -0400,
     Wed, 13 Oct 2010 11:30:07 -0400,
     Thu, 13 Oct 2011 11:30:07 -0400,
     Sat, 13 Oct 2012 11:30:07 -0400,
     Sun, 13 Oct 2013 11:30:07 -0400]
    
    
    DateRange((4.years.ago)..Time.now).every(1.year)
    => [2009-10-13 11:30:07 -0400,
     2010-10-13 17:30:07 -0400,
     2011-10-13 23:30:07 -0400,
     2012-10-13 05:30:07 -0400,
     2013-10-13 11:30:07 -0400]
    
    RailsDateRange((5.months.ago)..Time.now).every(months: 1)
    => [Mon, 13 May 2013 11:31:55 -0400,
     Thu, 13 Jun 2013 11:31:55 -0400,
     Sat, 13 Jul 2013 11:31:55 -0400,
     Tue, 13 Aug 2013 11:31:55 -0400,
     Fri, 13 Sep 2013 11:31:55 -0400,
     Sun, 13 Oct 2013 11:31:55 -0400]
    
    DateRange((5.months.ago)..Time.now).every(1.month)
    => [2013-05-13 11:31:55 -0400,
     2013-06-12 11:31:55 -0400,
     2013-07-12 11:31:55 -0400,
     2013-08-11 11:31:55 -0400,
     2013-09-10 11:31:55 -0400,
     2013-10-10 11:31:55 -0400]
    
    RailsDateRange((4.years.ago)..Time.now).every(years: 1)
    
    => [Tue, 13 Oct 2009 11:30:07 -0400,
     Wed, 13 Oct 2010 11:30:07 -0400,
     Thu, 13 Oct 2011 11:30:07 -0400,
     Sat, 13 Oct 2012 11:30:07 -0400,
     Sun, 13 Oct 2013 11:30:07 -0400]
    
    DateRange((4.years.ago)..Time.now).every(1.year)
    
    => [2009-10-13 11:30:07 -0400,
     2010-10-13 17:30:07 -0400,
     2011-10-13 23:30:07 -0400,
     2012-10-13 05:30:07 -0400,
     2013-10-13 11:30:07 -0400]
    
  2. ==============================

    2.오류를 반올림, 범위는 당신이 원하는하지 않은, 순서를 열거 할 .succ 메소드를 호출하지 않습니다.

    오류를 반올림, 범위는 당신이 원하는하지 않은, 순서를 열거 할 .succ 메소드를 호출하지 않습니다.

    아니 한 줄하지만, 짧은 도우미 기능은 충분합니다 :

    def datetime_sequence(start, stop, step)
      dates = [start]
      while dates.last < (stop - step)
        dates << (dates.last + step)
      end 
      return dates
    end 
    
    datetime_sequence(DateTime.now, DateTime.now + 1.day, 1.hour)
    
    # [Mon, 30 Sep 2013 08:28:38 -0400, Mon, 30 Sep 2013 09:28:38 -0400, ...]
    

    참고 그러나,이 격렬하게 비효율적 인 메모리 현명한 큰 범위에 대해 수 있습니다.

    또는, 당신은 시대 이후 초를 사용할 수 있습니다 :

    start = DateTime.now
    stop  = DateTime.now + 1.day
    (start.to_i..stop.to_i).step(1.hour)
    
    # => #<Enumerator: 1380545483..1380631883:step(3600 seconds)>
    

    당신은 정수의 범위를해야합니다,하지만 당신은 쉽게 날짜 시간으로 다시 변환 할 수 있습니다 :

    Time.at(i).to_datetime
    
  3. ==============================

    3.

    module RangeWithStepTime
      def step(step_size = 1, &block)
        return to_enum(:step, step_size) unless block_given?
    
        # Defer to Range for steps other than durations on times
        return super unless step_size.kind_of? ActiveSupport::Duration
    
        # Advance through time using steps
        time = self.begin
        op = exclude_end? ? :< : :<=
        while time.send(op, self.end)
          yield time
          time = step_size.parts.inject(time) { |t, (type, number)| t.advance(type => number) }
        end
    
        self
      end
    end
    
    Range.prepend(RangeWithStepTime)
    

    이는 기존의 API를 사용하여 범위에 대한 우리의 기간에 대한 지원을 추가합니다. 그것은 당신이 우리가 단순히 "단지 작품"으로 기대하는 스타일로 일반 범위를 사용할 수 있습니다.

    # Now the question's invocation becomes even
    # simpler and more flexible
    
    step = 2.months + 4.days + 22.3.seconds
    ( Time.now .. 7.months.from_now ).step(step) do |time|
      puts "It's #{time} (#{time.to_f})"
    end
    
    # It's 2013-10-17 13:25:07 +1100 (1381976707.275407)
    # It's 2013-12-21 13:25:29 +1100 (1387592729.575407)
    # It's 2014-02-25 13:25:51 +1100 (1393295151.8754072)
    # It's 2014-04-29 13:26:14 +1000 (1398741974.1754072)
    

    ... 단계 초 그들을 통해 스테핑, 내부적으로 정수로 시간을 다음 개체에 DATERANGE <범위 클래스 + DATERANGE "생성자"를 사용하여 #every를 추가로 변환하는 것이 었습니다. 이것은 원래 시간대에 작동하지 않았다. 시간대에 대한 지원이 추가되었다하지만 또 다른 문제는 몇 가지 단계 기간은 동적 사실 (예를 들어 1.month)와 함께 발견되었다.

    읽기 Rubinius '범위의 구현은 누군가가 ActiveSupport :: 기간에 대한 지원을 추가 할 수있는 방법을 명확하게되었다; 그래서 접근 방식은 다시 작성했다. #advance 팁과이 문제를 디버깅, 아름답게 기록되기위한 범위 # 단계의 Rubinius '구현에 댄 구엔에 많은 감사합니다 : D

  4. ==============================

    4.당신이 갖고 싶어 N 경우 값이 고르게 두 날짜 당신은 할 수 사이에 확산

    당신이 갖고 싶어 N 경우 값이 고르게 두 날짜 당신은 할 수 사이에 확산

    n = 8
    beginning = Time.now - 1.day
    ending = Time.now
    diff = ending - beginning
    result = []
    (n).times.each do | x |
      result << (beginning + ((x*diff)/(n-1)).seconds)
    end
    result
    

    이는 제공

    2015-05-20 16:20:23 +0100
    2015-05-20 19:46:05 +0100
    2015-05-20 23:11:48 +0100
    2015-05-21 02:37:31 +0100
    2015-05-21 06:03:14 +0100
    2015-05-21 09:28:57 +0100
    2015-05-21 12:54:40 +0100
    2015-05-21 16:20:23 +0100
    
  5. ==============================

    5.당신은 반복에 대한 잠금을 얻기 위해 시작 시간과 종료 시간에 DateTime.parse를 사용하여 배열을 채울 필요가있다. 예를 들어;

    당신은 반복에 대한 잠금을 얻기 위해 시작 시간과 종료 시간에 DateTime.parse를 사용하여 배열을 채울 필요가있다. 예를 들어;

    #Seconds
    ((DateTime.parse(@startdt) - DateTime.now) * 24 * 60 * 60).to_i.abs
    
    #Minutes
    ((DateTime.parse(@startdt) - DateTime.now) * 24 * 60).to_i.abs
    

    등등. 이 값이 있으면, 할 수 있습니다 당신이 원하는 시간의 어떤 조각의 배열을 채우기 통해 루프. 나는, 당신은 아마 이것에 대한 배열을 구체화 할 필요가 없습니다 불구하고 @fotanus에 동의하지만, 나는 당신의 목표는 정말 말할 수 있도록 그렇게 무엇인지 모른다.

  6. ==============================

    6.아이스 큐브 - 루비 날짜 재발 도서관은 여기에 도움 또는 구현을 보일 것? 그것은 성공적으로 RFC 5545 규격의 모든 예를 커버하고있다.

    아이스 큐브 - 루비 날짜 재발 도서관은 여기에 도움 또는 구현을 보일 것? 그것은 성공적으로 RFC 5545 규격의 모든 예를 커버하고있다.

    schedule = IceCube::Schedule.new(2013,10,10,12)
    schedule.add_recurrence_rule IceCube::Rule.hourly.until(Time.new(2013,10,10,14))
    schedule.all_occurrences
    # => [
    #  [0] 2013-10-10 12:00:00 +0100,
    #  [1] 2013-10-10 13:00:00 +0100,
    #  [2] 2013-10-10 14:00:00 +0100
    #]
    
  7. ==============================

    7.또 다른 해결책은 UNIQ 방법을 사용하는 것입니다. 예를 고려해

    또 다른 해결책은 UNIQ 방법을 사용하는 것입니다. 예를 고려해

    date_range = (Date.parse('2019-01-05')..Date.parse('2019-03-01'))
    date_range.uniq { |d| d.month }
    # => [Sat, 05 Jan 2019, Fri, 01 Feb 2019]
    date_range.uniq { |d| d.cweek }
    # => [Sat, 05 Jan 2019, Mon, 07 Jan 2019, Mon, 14 Jan 2019, Mon, 21 Jan 2019, Mon, 28 Jan 2019, Mon, 04 Feb 2019, Mon, 11 Feb 2019, Mon, 18 Feb 2019, Mon, 25 Feb 2019]
    

    참고 이러한 접근 점은 최소 및 최대 범위 것을

  8. ==============================

    8.당신이 할 수 있도록 한 시간은 하루 1 / 24입니다

    당신이 할 수 있도록 한 시간은 하루 1 / 24입니다

    d1 = DateTime.now
    d2 = d1 + 1
    d1.step(d2, 1/24r){|d| p d}
    

    1 / 24R은 합리적이고보다 정확한보다 플로트이다.

  9. from https://stackoverflow.com/questions/19093487/ruby-create-range-of-dates by cc-by-sa and MIT license