class NSDate

Constants

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

Public Class Methods

from_components(components) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 12
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
now() click to toggle source

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

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 22
def self.now
  NSDate.new
end
today() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 26
def self.today
  NSDate.new.start_of_day
end
tomorrow() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 30
def self.tomorrow
  NSDate.new.delta(days: 1).start_of_day
end
yesterday() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 34
def self.yesterday
  NSDate.new.delta(days: -1).start_of_day
end

Public Instance Methods

_string_with_nsdate_format(format, timezone, locale) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 100
def _string_with_nsdate_format(format, timezone, locale)
  locale_name = locale.localeIdentifier
  @@sugarcube_date_formatters ||= {}
  @@sugarcube_date_formatters[locale_name] ||= {}
  @@sugarcube_date_formatters[locale_name][format] ||= begin
    format_template = NSDateFormatter.dateFormatFromTemplate(format, options: 0,
                                                      locale: locale)
    date_formatter = NSDateFormatter.new
    date_formatter.setDateFormat(format_template)
    date_formatter
  end

  date_formatter = @@sugarcube_date_formatters[locale_name][format]
  date_formatter.setTimeZone(timezone)
  return date_formatter.stringFromDate(self)
end
_string_with_sugarcube_format(format, timezone) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 84
def _string_with_sugarcube_format(format, timezone)
  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
end
_string_with_unicode_format(format, timezone) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 117
def _string_with_unicode_format(format, timezone)
  @@sugarcube_unicode_formatters ||= {}
  @@sugarcube_unicode_formatters[format] ||= begin
    date_formatter = NSDateFormatter.new
    date_formatter.setDateFormat(format)
    date_formatter
  end

  date_formatter = @@sugarcube_unicode_formatters[format]
  date_formatter.setTimeZone(timezone)
  return date_formatter.stringFromDate(self)
end
date_array() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.time_array

> [2012, 9, 27]

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 207
def date_array
  return [self.year, self.month, self.day]
end
datetime_array() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.time_array

> [2012, 9, 12, 11, 29, 12]

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 223
def datetime_array
  return [self.year, self.month, self.day, self.hour, self.min, self.sec]
end
days_in_month() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 289
def days_in_month
  NSCalendar.currentCalendar.rangeOfUnit(NSDayCalendarUnit, inUnit:NSMonthCalendarUnit, forDate:self).length
end
days_in_year() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 293
def days_in_year
  leap_year? ? 366 : 365
end
delta(_components) click to toggle source
# 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 * 60
  delta += h * 3600

  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 * 3600 * 24

        # 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 * 3600 * 24
          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) * 3600 * 24
          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) * 3600 * 24).days_in_month
        return_date -= delta * 3600 * 24
        # same correction as above
        if correct_day_of_month
          if return_date.day < 28
            return_date -= return_date.day * 3600 * 24
          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) * 3600 * 24
          end
        end
      end
    end
  end

  delta = 0
  delta += d * 3600 * 24
  delta += w * 3600 * 24 * 7
  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 += 3600
    else
      return_date -= 3600
    end
  end

  return return_date
end
downto(last_date, delta={days: -1}, &block) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 146
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_day() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.end_of_day

> 2012-09-28 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 251
def end_of_day
  return self.delta(days: 1).start_of_day
end
end_of_month() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.end_of_month

> 2012-10-01 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 285
def end_of_month
  return self.end_of_day.delta(days:days_in_month - day)
end
end_of_week(start_day=nil) click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.start_of_week

> 2012-09-30 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 268
def end_of_week(start_day=nil)
  result = self - days_to_week_start(start_day).days + 6.days
  result.end_of_day
end
era() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 167
def era
  return _calendar_components(NSEraCalendarUnit).era
end
leap_year?() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 199
def leap_year?
  self.year % 4 == 0 and self.year % 100 != 0 or self.year % 400 == 0
end
same_day?(other) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 186
def same_day?(other)
  return other.day == self.day &&
         other.month == self.month &&
         other.year == self.year &&
         other.era == self.era
end
start_of_day() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.start_of_day

> 2012-09-27 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 231
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_month() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.start_of_month

> 2012-09-01 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 277
def start_of_month
  return self.start_of_day.delta(days:1 - day)
end
start_of_week(start_day=nil) click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.start_of_week

> 2012-09-23 00:00:00 +0900

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 259
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={}) click to toggle source

Pass in a format string or a Symbol. If you use a Symbol, it must exist in NSDate::SugarCubeFormats.

This method accepts some options:

timezone: String or NSTimeZone. Strings get converted using NSTimeZone.timeZoneWithName unicode: if true, this method will use Unicode date format (TR35) instead of converting to a locale specific format locale: Strings or NSLocale. Strings get converted using NSLocale.localeWithLocaleIdentifier

See <developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW1> and <www.unicode.org/reports/tr35/tr35-19.html#Date_Field_Symbol_Table> for more information about date format strings.

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 61
def string_with_format(format, options={})
  timezone = options[:timezone] || NSTimeZone.defaultTimeZone
  if timezone.is_a?(NSString)
    timezone = NSTimeZone.timeZoneWithName(timezone)
  end

  if format.is_a?(Symbol)
    return _string_with_sugarcube_format(format, timezone)
  else
    unicode = options[:unicode]
    if unicode
      return _string_with_unicode_format(format, timezone)
    else
      locale = options[:locale] || NSLocale.currentLocale
      if locale.is_a?(NSString)
        locale = NSLocale.localeWithLocaleIdentifier(locale)
      end

      return _string_with_nsdate_format(format, timezone, locale)
    end
  end
end
string_with_style(date_style=NSDateFormatterMediumStyle, time_style=NSDateFormatterNoStyle) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 38
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
timeZone()
Alias for: timezone
time_array() click to toggle source

(main)> t = Time.new

> 2012-09-27 11:29:12 +0900

(main)> t.time_array

> [11, 29, 12]

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 215
def time_array
  return [self.hour, self.min, self.sec]
end
timezone() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 162
def timezone
  return _calendar_components(NSTimeZoneCalendarUnit).timeZone
end
Also aliased as: timeZone
today?() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 171
def today?
  today = self.class.new
  return same_day?(today)
end
tomorrow?() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 176
def tomorrow?
  tomorrow = self.class.tomorrow
  return same_day?(tomorrow)
end
upto(last_date, delta={days: 1}, &block) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 130
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_offset() click to toggle source

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

# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 195
def utc_offset
  return self.timezone.secondsFromGMT
end
yesterday?() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 181
def yesterday?
  yesterday = self.class.yesterday
  return same_day?(yesterday)
end

Private Instance Methods

_calendar_components(components) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 298
def _calendar_components(components)
  return NSCalendar.currentCalendar.components(components, fromDate:self)
end
days_to_week_start(start_day=nil) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 302
def days_to_week_start(start_day=nil)
  start_day_number = week_day_index(start_day) || local_week_start
  current_day_number = _calendar_components(NSWeekdayCalendarUnit).weekday
  (current_day_number - start_day_number) % 7
end
local_week_start() click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 308
def local_week_start
  NSCalendar.currentCalendar.firstWeekday
end
week_day_index(week_day=:monday) click to toggle source
# File lib/cocoa/sugarcube-nsdate/nsdate.rb, line 312
def week_day_index(week_day=:monday)
  day = week_day.to_s.capitalizedString
  index = NSDateFormatter.new.weekdaySymbols.index(day)
  return nil unless index
  index + 1
end