Class: NSDate

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

Constant Summary collapse

SugarCubeFormats =

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

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_components(components) ⇒ Object


11
12
13
14
15
16
17
18
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 11

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

.nowObject

Time.now is defined, but not NSDate.now.


21
22
23
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 21

def self.now
  NSDate.new
end

.todayObject


25
26
27
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 25

def self.today
  NSDate.new.start_of_day
end

.tomorrowObject


29
30
31
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 29

def self.tomorrow
  NSDate.new.delta(days: 1).start_of_day
end

.yesterdayObject


33
34
35
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 33

def self.yesterday
  NSDate.new.delta(days: -1).start_of_day
end

Instance Method Details

#date_arrayObject

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


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

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]


165
166
167
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 165

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

#days_in_monthObject


231
232
233
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 231

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

#days_in_yearObject


235
236
237
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 235

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/cocoa/sugarcube-nsdate/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


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 98

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


193
194
195
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 193

def end_of_day
  return self.delta(days: 1).start_of_day
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


227
228
229
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 227

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

#end_of_week(start_day = nil) ⇒ Object

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


210
211
212
213
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 210

def end_of_week(start_day=nil)
  result = self - days_to_week_start(start_day).days + 6.days
  result.end_of_day
end

#eraObject


119
120
121
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 119

def era
  return _calendar_components(NSEraCalendarUnit).era
end

#leap_year?Boolean

Returns:

  • (Boolean)

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

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

#same_day?(other) ⇒ Boolean

Returns:

  • (Boolean)

128
129
130
131
132
133
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 128

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


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 173

def start_of_day
  date_components = NSDateComponents.new
  date_components.hour = 0
  date_components.minute = 0
  date_components.second = 0
  date_components.day = self.day
  date_components.month = self.month
  date_components.year = self.year

  calendar = NSCalendar.alloc.initWithCalendarIdentifier(NSGregorianCalendar)
  calendar.timeZone = NSTimeZone.timeZoneForSecondsFromGMT(self.utc_offset)
  date = calendar.dateFromComponents(date_components)

  return date
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


219
220
221
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 219

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

#start_of_week(start_day = nil) ⇒ Object

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


201
202
203
204
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 201

def start_of_week(start_day=nil)
  result = self - days_to_week_start(start_day).days
  result.start_of_day
end

#string_with_format(format, options = {}) ⇒ Object


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
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 54

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

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


37
38
39
40
41
42
43
44
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 37

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]


157
158
159
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 157

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

#timezoneObject Also known as: timeZone


114
115
116
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 114

def timezone
  return _calendar_components(NSTimeZoneCalendarUnit).timeZone
end

#today?Boolean

Returns:

  • (Boolean)

123
124
125
126
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 123

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

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


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 82

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.


137
138
139
# File 'lib/cocoa/sugarcube-nsdate/nsdate.rb', line 137

def utc_offset
  return self.timezone.secondsFromGMT
end