Module: RiCal::CoreExtensions::Time::Calculations

Included in:
Date, DateTime, Time
Defined in:
lib/ri_cal/core_extensions/time/calculations.rb

Overview

  • ©2009 Rick DeNatale

  • All rights reserved. Refer to the file README.txt for the license

Provide calculation methods for use by the RiCal gem This module is included by Time, Date, and DateTime

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.convert_wday(wday) ⇒ Object

Convert the receivers wday to RFC 2445 format. Whereas the Ruby time/date classes use 0 to represent Sunday, RFC 2445 uses 7.


79
80
81
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 79

def self.convert_wday(wday)
  wday == 0 ? 7 : wday
end

.iso_week_one(year, wkst) ⇒ Object

Return the date on which the first iso week with a given starting week day occurs for a given iso year

From RFC 2445 page 43: A week is defined as a seven day period, starting on the day of the week defined to be the week start (see WKST). Week number one of the calendar year is the first week which contains at least four (4) days in that calendar year.

parameters

year

the iso year

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 35

def self.iso_week_one(year, wkst)
  # 
  # Note that wkst uses the ruby definition, with Sunday = 0 
  #
  # A good article about calculating ISO week number is at
  # http://www.boyet.com/Articles/PublishedArticles/CalculatingtheISOweeknumb.html
  #
  # RFC 2445 generalizes the notion of ISO week by allowing the start of the week to vary.
  # In order to adopt the algorithm in the referenced article, we must determine, for each
  # wkst value, the day in January which must be contained in week 1 of the year.
  # 
  # For a given wkst week 1 for a year is the first week which
  #   1) Starts with a day with a wday of wkst
  #   2) Contains a majority (4 or more) of days in that year
  # 
  # If end of prior Dec, start of Jan          Week 1 starts on For WKST =

  # MO TU WE TH FR SA SU MO TU WE TH FR SA SU  MO    TU    WE    TH    FR    SA    SU      
  # 01 02 03 04 05 06 07 08 09 10 11 12 13 14 01-07 02-08 03-09 04-10 05-11 06-12 07-13
  # 31 01 02 03 04 05 06 07 08 09 10 11 12 13 31-06 01-07 02-08 03-09 04-10 05-11 06-12
  # 30 31 01 02 03 04 05 06 07 08 09 10 11 12 30-05 31-06 01-07 02-08 03-09 04-10 05-11
  # 29 30 31 01 02 03 04 05 06 07 08 09 10 11 29-04 30-05 31-06 01-07 02-08 03-09 04-10
  # 28 29 30 31 01 02 03 04 05 06 07 08 09 10 04-10 29-04 30-05 31-06 01-07 02-08 03-09
  # 27 28 29 30 31 01 02 03 04 05 06 07 08 09 03-09 04-10 29-04 30-05 31-06 01-07 02-08
  # 26 27 28 29 30 31 01 02 03 04 05 06 07 08 02-08 03-09 04-10 29-04 30-05 31-06 01-07
  # 25 26 27 28 29 30 31 01 02 03 04 05 06 07 01-07 02-08 03-09 04-10 29-04 30-05 31-06
  #                     Week 1 must contain     4     4     4     4     ?     ?     ?  
  #
  # So for a wkst of FR, SA, or SU, there is no date which MUST be contained in the 1st week
  # We'll have to brute force that
  if (1..4).include?(wkst)
    # return the date of the wkst day which is less than or equal to jan4th
    jan4th = ::Date.new(year, 1, 4)
    result = jan4th - (convert_wday(jan4th.wday) - convert_wday(wkst))
  else
    # return the date of the wkst day which is greater than or equal to Dec 31 of the prior year
    dec29th = ::Date.new(year-1, 12, 29)
    result = dec29th + convert_wday(wkst) - convert_wday(dec29th.wday)
  end
  result
end

Instance Method Details

#days_in_monthObject

Return the number of days in the month which includes the receiver


16
17
18
19
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 16

def days_in_month
  raw = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][self.month]
  self.month == 2 && leap_year? ? raw + 1 : raw
end

#iso_week_num(wkst) ⇒ Object

return the iso week number of the receiver

parameter

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


131
132
133
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 131

def iso_week_num(wkst)
  iso_year_and_week_num(wkst)[1]
end

#iso_weeks_in_year(wkst) ⇒ Object

return the number of weeks in the the iso year containing the receiver

parameter

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


117
118
119
120
121
122
123
124
125
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 117

def iso_weeks_in_year(wkst)
  iso_year, week_one_start = *iso_year_and_week_one_start(wkst)
  probe_date = week_one_start + (7*52)
  if probe_date.iso_year(wkst) == iso_year
    53
  else
    52
  end
end

#iso_year(wkst) ⇒ Object

return the iso year of the receiver

parameter

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


139
140
141
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 139

def iso_year(wkst)
  iso_year_and_week_num(wkst)[0]
end

#iso_year_and_week_num(wkst) ⇒ Object

:nodoc:


108
109
110
111
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 108

def iso_year_and_week_num(wkst) #:nodoc:
  iso_year, week_one_start = *iso_year_and_week_one_start(wkst)
  [iso_year, (::Date.new(self.year, self.month, self.mday) - week_one_start).to_i / 7 + 1]
end

#iso_year_and_week_one_start(wkst) ⇒ Object

Return an array containing the iso year and iso week number for the receiver. Note that the iso year may be the year before or after the calendar year containing the receiver.

parameter

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 88

def iso_year_and_week_one_start(wkst)
  iso_year = self.year
  date = ::Date.new(self.year, self.month, self.mday)
  if (date >= ::Date.new(iso_year, 12, 29))
    week_one_start =  Calculations.iso_week_one(iso_year + 1, wkst)
    if date < week_one_start
      week_one_start = Calculations.iso_week_one(iso_year, wkst)
    else
      iso_year += 1
    end
  else
    week_one_start = Calculations.iso_week_one(iso_year, wkst)
    if (date < week_one_start)
      iso_year -= 1
      week_one_start = Calculations.iso_week_one(iso_year, wkst)
    end
  end
  [iso_year, week_one_start]
end

#iso_year_start(wkst) ⇒ Object

return the first day of the iso year of the receiver

parameter

wkst

an integer representing the day of the week on which weeks are deemed to start. This uses

the ruby convention where 0 represents Sunday.


147
148
149
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 147

def iso_year_start(wkst)
  iso_year_and_week_one_start(wkst)[1]
end

#leap_year?Boolean

A predicate method used to determine if the receiver is within a leap year


11
12
13
# File 'lib/ri_cal/core_extensions/time/calculations.rb', line 11

def leap_year?
  year % 4 == 0 && (year % 400 == 0 || year % 100 != 0)
end