Class: NSDate

Inherits:
Object show all
Defined in:
lib/sugarcube/nsdate.rb,
lib/sugarcube/nsdate_delta.rb

Constant Summary collapse

SugarCubeFormats =

these formatters are used in string_with_format. Symbols are convertd to a string using string_with_format's templating, and strings are concatenated as-is

{
  iso8601: [:yyyy, '-', :MM, '-', :dd, ' ', :HH, ':', :mm, ':', :ss, '.', :SSS]
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_components(components) ⇒ Object



9
10
11
12
13
14
15
16
# File 'lib/sugarcube/nsdate.rb', line 9

def self.from_components(components)
  date_components = NSDateComponents.new
  components.each do |property,value|
    date_components.send("#{property}=", value)
  end
  calendar = NSCalendar.alloc.initWithCalendarIdentifier(NSGregorianCalendar)
  return calendar.dateFromComponents(date_components)
end

Instance Method Details

#date_arrayObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.time_array => [2012, 9, 27]



125
126
127
# File 'lib/sugarcube/nsdate.rb', line 125

def date_array
  return [self.year, self.month, self.day]
end

#datetime_arrayObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.time_array => [2012, 9, 12, 11, 29, 12]



141
142
143
# File 'lib/sugarcube/nsdate.rb', line 141

def datetime_array
  return [self.year, self.month, self.day, self.hour, self.min, self.sec]
end

#days_in_monthObject



179
180
181
# File 'lib/sugarcube/nsdate.rb', line 179

def days_in_month
  NSCalendar.currentCalendar.rangeOfUnit(NSDayCalendarUnit, inUnit:NSMonthCalendarUnit, forDate:self).length
end

#days_in_yearObject



183
184
185
# File 'lib/sugarcube/nsdate.rb', line 183

def days_in_year
  leap_year? ? 366 : 365
end

#delta(_components) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/sugarcube/nsdate_delta.rb', line 3

def delta(_components)
  components = {}.update(_components)
  is_very_specific = components.has_key?(:seconds)
  is_very_specific ||= components.has_key?(:minutes)
  is_very_specific ||= components.has_key?(:hours)

  y = components.delete(:years) || 0
  mo = components.delete(:months) || 0
  d = components.delete(:days) || 0
  h = components.delete(:hours) || 0
  mi = components.delete(:minutes) || 0
  s = components.delete(:seconds) || 0
  w = components.delete(:weeks) || 0
  raise "Unknown arguments #{components.keys}" unless components.empty?

  is_dst = self.dst?

  delta = s
  # todo: leap second adjustment?  can leap seconds be detected?
  delta += mi.minutes
  delta += h.hours

  return_date = self + delta

  # using days_in_month, this is pretty easy.  12 mos per year IS a constant,
  # and then we just keep adding the number of days in the month (or last month
  # if we're going backwards).  The curve ball is that when we are in day
  # 29,30,31, we might jump forward a month and "fall off" to the next month.
  # In this case, we add a correction.  We will always move forwards or
  # backwards until the return_date.day is correct.
  mo += y * 12
  if mo != 0
    if return_date.day > 28
      # we will try and preserve this day
      correct_day_of_month = return_date.day
    else
      correct_day_of_month = nil
    end

    if mo > 0
      mo.times do
        delta = return_date.days_in_month
        return_date += delta.days

        # if the day_of_month is wrong, it must be because we either added PAST
        # the correct month (so roll back), or because we WERE rolled back and
        # when we moved forward a month, we were left back at the smaller day.
        if correct_day_of_month
          if return_date.day < 28
            return_date -= return_date.day.days
          elsif return_date.day < correct_day_of_month
            fix = correct_day_of_month > return_date.days_in_month ? return_date.days_in_month : correct_day_of_month
            return_date += (fix - return_date.day).days
          end
        end
      end
    else  # mo < 0
      (-mo).times do
        # subtract *last* months number of days.
        # there is a REALLY rare case where subtracting return_date.day is one
        # hour short of "last month" and so you end up with THIS month.  there
        # is NEVER a case when subtracting return_date.day+1 days is NOT
        # "previous month".  dates. :-|  f-em.
        delta = (return_date - (return_date.day+1).days).days_in_month
        return_date -= delta.days
        # same correction as above
        if correct_day_of_month
          if return_date.day < 28
            return_date -= return_date.day.days
          elsif return_date.day < correct_day_of_month
            fix = correct_day_of_month > return_date.days_in_month ? return_date.days_in_month : correct_day_of_month
            return_date += (fix - return_date.day).days
          end
        end
      end
    end
  end

  delta = 0
  delta += d.days
  delta += w.weeks
  return_date += delta

  # DST adjustment, unless minutes, hours, or seconds were specified.
  #
  # the thinking here is that if they WERE specified, the delta should be
  # accurate to that granularity.  if they were omitted, the hour component
  # should not change, even though an off-by-one adjustment is needed
  #
  # for instance.  3/10/2012 is not in DST.  3/11/2012 IS.
  # Time.at(3/10/2012)  # => 2012-03-10 17:00:00 -0700
  # Time.at(3/10/2012).delta(days:1)  # => 2012-03-11 17:00:00 -0600
  #
  # notice the time is the SAME, even though the time zone is different.  BUT:
  # Time.at(3/10/2012).delta(hours:24)  # => 2012-03-11 17:00:00 -0600
  # Time.at(3/10/2012).delta(hours:25)  # => 2012-03-11 18:00:00 -0600
  unless return_date.dst? == is_dst or is_very_specific
    if is_dst
      return_date += 1.hour
    else
      return_date -= 1.hour
    end
  end

  return return_date
end

#downto(last_date, delta = {days: -1}, &block) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/sugarcube/nsdate.rb', line 74

def downto(last_date, delta={days: -1}, &block)
  return if last_date > self

  date = self
  while date >= last_date
    if block.arity == 0
      block.call
    else
      block.call(date)
    end
    new_date = date.delta(delta)
    break if new_date >= date
    date = new_date
  end
end

#end_of_dayObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.end_of_day => 2012-09-28 00:00:00 +0900



158
159
160
161
# File 'lib/sugarcube/nsdate.rb', line 158

def end_of_day
  time_interval = (23 - self.hour).hours + (59 - self.min).minutes - self.sec + 60
  return self + time_interval
end

#end_of_monthObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.end_of_month => 2012-10-01 00:00:00 +0900



175
176
177
# File 'lib/sugarcube/nsdate.rb', line 175

def end_of_month
  return self.end_of_day.delta(days:days_in_month - day)
end

#eraObject



95
96
97
# File 'lib/sugarcube/nsdate.rb', line 95

def era
  return _calendar_components(NSEraCalendarUnit).era
end

#leap_year?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/sugarcube/nsdate.rb', line 117

def leap_year?
  self.year % 4 == 0 and self.year % 100 != 0 or self.year % 400 == 0
end

#same_day?(other) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
107
108
109
# File 'lib/sugarcube/nsdate.rb', line 104

def same_day?(other)
  return other.day == self.day &&
         other.month == self.month &&
         other.year == self.year &&
         other.era == self.era
end

#start_of_dayObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.start_of_day => 2012-09-27 00:00:00 +0900



149
150
151
152
# File 'lib/sugarcube/nsdate.rb', line 149

def start_of_day
  time_interval = self.hour.hours + self.min.minutes + self.sec
  return self - time_interval
end

#start_of_monthObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.start_of_month => 2012-09-01 00:00:00 +0900



167
168
169
# File 'lib/sugarcube/nsdate.rb', line 167

def start_of_month
  return self.start_of_day.delta(days:1 - day)
end

#string_with_format(format) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/sugarcube/nsdate.rb', line 35

def string_with_format(format)
  if format.is_a?(Symbol)
    formatters = SugarCubeFormats[format]
    raise "No format found for #{format.inspect}" unless formatters
    retval = ''
    formatters.each do |formatter|
      case formatter
      when Symbol
        retval << string_with_format(formatter.to_s)
      when String
        retval << formatter
      end
    end
    return retval
  else
    format_template = NSDateFormatter.dateFormatFromTemplate(format, options:0,
                                                      locale:NSLocale.currentLocale)
    date_formatter = NSDateFormatter.new
    date_formatter.setDateFormat(format_template)
    return date_formatter.stringFromDate(self)
  end
end

#string_with_style(date_style = NSDateFormatterMediumStyle, time_style = NSDateFormatterNoStyle) ⇒ Object



18
19
20
21
22
23
24
25
# File 'lib/sugarcube/nsdate.rb', line 18

def string_with_style(date_style=NSDateFormatterMediumStyle,time_style=NSDateFormatterNoStyle)
  date_formatter = NSDateFormatter.new
  date_style = date_style.nsdatestyle if date_style.is_a? Symbol
  time_style = time_style.nsdatestyle if time_style.is_a? Symbol
  date_formatter.setDateStyle(date_style)
  date_formatter.setTimeStyle(time_style)
  date_formatter.stringFromDate(self)
end

#time_arrayObject

(main)> t = Time.new => 2012-09-27 11:29:12 +0900 (main)> t.time_array => [11, 29, 12]



133
134
135
# File 'lib/sugarcube/nsdate.rb', line 133

def time_array
  return [self.hour, self.min, self.sec]
end

#timezoneObject Also known as: timeZone



90
91
92
# File 'lib/sugarcube/nsdate.rb', line 90

def timezone
  return _calendar_components(NSTimeZoneCalendarUnit).timeZone
end

#today?Boolean

Returns:

  • (Boolean)


99
100
101
102
# File 'lib/sugarcube/nsdate.rb', line 99

def today?
  today = self.class.new
  return same_day?(today)
end

#upto(last_date, delta = {days: 1}, &block) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sugarcube/nsdate.rb', line 58

def upto(last_date, delta={days: 1}, &block)
  return if last_date < self

  date = self
  while date <= last_date
    if block.arity == 0
      block.call
    else
      block.call(date)
    end
    new_date = date.delta(delta)
    break if new_date <= date
    date = new_date
  end
end

#utc_offsetObject

In the rare case you actually get an NSDate object - not a Time object - this method is actually useful.



113
114
115
# File 'lib/sugarcube/nsdate.rb', line 113

def utc_offset
  return self.timezone.secondsFromGMT
end