class PartialDate::Date
Public: A class for handling partial dates. Year (including negative values), month and day are optional although month must be set before day. Partial dates are stored internally in a single integer bit store using bitmask and bitwise operations to set and get year, month, and day values.
A numerical (human readable) date value can be retrieved via the d.value accessor.
Use d.value to get and set the numerical date value as well as for storing dates as a single value in a persistence store.
Examples
date = PartialDate::Date.new date.value = 20121201 # => 2012-12-01 date = PartialDate::Date.load 20121201 # => 2012-12-01 date = PartialDate::Date.new date.year = 2012 date.month = 12 date.day = 1 date = PartialDate::Date.new {|d| d.year = 2012; d.month = 12; d.day = 1} date = PartialDate::Date.new {|d| d.year = 2012 }
Attributes
Public: Readonly accessor for the raw bit integer date value.
Returns the single Integer backing store with ‘bits’ flipped for year, month and day.
Public Class Methods
# File lib/partial-date/date.rb, line 364 def self.get_date(register) date = (get_year(register) * 10000).abs + (get_month(register) * 100) + get_day(register) if get_sign(register) == 1 date * -1 else date end end
# File lib/partial-date/date.rb, line 410 def self.get_day(register) register & DAY_MASK end
# File lib/partial-date/date.rb, line 402 def self.get_month(register) (register & MONTH_MASK) >> MONTH_SHIFT end
# File lib/partial-date/date.rb, line 380 def self.get_sign(register) (register & SIGN_MASK) >> SIGN_SHIFT end
# File lib/partial-date/date.rb, line 388 def self.get_year(register) year = (register & YEAR_MASK) >> YEAR_SHIFT if get_sign(register) == 1 year * -1 else year end end
Public: Loads an 8 digit date value into a date object. Can be used when rehydrating a date object from a persisted partial date value.
value - an 8 digit value in partial date format.
Examples
date = PartialDate::Date.load 201212201 date.value # => 20121200 date.year # => 2012 date.month # => 12 date.day # => 0
Returns date object
# File lib/partial-date/date.rb, line 141 def self.load(value) PartialDate::Date.new {|d| d.value = value} end
Public: Create a new partial date class from a block of integers or strings.
Examples
# From integers date = PartialDate::Date.new {|d| d.year = 2012; d.month = 12; d.day = 1} date.value # => 20121201 # From strings date = PartialDate::Date.new {|d| d.year = "2012"; d.month = "12"; d.day = "1"} date.value # => 20121201
Returns a date object.
# File lib/partial-date/date.rb, line 118 def initialize @bits = 0 yield self if block_given? end
# File lib/partial-date/date.rb, line 373 def self.set_date(register, value) register = (value < 0) ? set_sign(register, 1) : set_sign(register, 0) register = set_year(register, (value.abs / 10000).abs) register = set_month(register, ((value - (value / 10000).abs * 10000) / 100).abs) register = set_day(register, value - (value / 100).abs * 100) end
# File lib/partial-date/date.rb, line 414 def self.set_day(register, value) register = (register & ZERO_DAY_MASK) | value end
# File lib/partial-date/date.rb, line 406 def self.set_month(register, value) register = (register & ZERO_MONTH_MASK) | (value << MONTH_SHIFT) end
# File lib/partial-date/date.rb, line 384 def self.set_sign(register, value) register = (register & ZERO_SIGN_MASK) | (value << SIGN_SHIFT) end
# File lib/partial-date/date.rb, line 397 def self.set_year(register, value) register = (value < 0) ? set_sign(register, 1) : set_sign(register, 0) register = (register & ZERO_YEAR_MASK) | (value.abs << YEAR_SHIFT) end
Public Instance Methods
Public: Spaceship operator for date comparisons. Comparisons are made by cascading down from year, to month to day. This should be faster than passing to self.value <=> other_date.value since the integer value attribute requires multiplication to calculate.
Returns -1, 1, or 0
# File lib/partial-date/date.rb, line 339 def <=>(other_date) if self.year < other_date.year return -1 elsif self.year > other_date.year return 1 else if self.month < other_date.month return -1 elsif self.month > other_date.month return 1 else if self.day < other_date.day return -1 elsif self.day > other_date.day return 1 else return 0 end end end end
Public: Get the day from a partial date.
# File lib/partial-date/date.rb, line 267 def day self.class.get_day(@bits) end
Public: Set the day portion of a partial date. Day is optional so zero, nil and empty strings are allowed.
# File lib/partial-date/date.rb, line 240 def day=(value) value = 0 if value.nil? if value.is_a?(String) if value.length == 0 value = 0 elsif value =~ /\A\d{1,2}\z/ value = value.to_i else raise DayError, "Day must be a valid one or two digit string or integer between 0 and 31" end end if (value >= 0 && value <= 31) raise DayError, "A month must be set before a day" if month == 0 && value !=0 begin date = ::Date.civil(self.year, self.month, value) if value > 0 @bits = self.class.set_day(@bits, value) rescue raise DayError, "Day must be a valid day for the given month" end else raise DayError, "Day must be an integer between 0 and 31" end end
Public: Get the month from a partial date.
# File lib/partial-date/date.rb, line 233 def month self.class.get_month(@bits) end
Public: Set the month of a partial date.
# File lib/partial-date/date.rb, line 211 def month=(value) value = 0 if value.nil? if value.is_a?(String) if value.length == 0 value = 0 elsif value =~ /\A\d{1,2}\z/ value = value.to_i else raise MonthError, "Month must be a valid one or two digit string or integer between 0 and 12" end end if (value >= 0 && value <= 12) @bits = self.class.set_month(@bits, value) @bits = self.class.set_day(@bits, 0) if value == 0 else raise MonthError, "Month must an be integer between 0 and 12" end end
Here for the moment for benchmark comparisons
# File lib/partial-date/date.rb, line 316 def old_to_s(format = :default) format = FORMATS[format] if format.is_a?(Symbol) result = format.dup FORMAT_METHODS.each_pair do |key, value| result.gsub!( key, value.call( self )) if result.include? key end # Remove any leading "/-," chars. # Remove double white spaces. # Remove any duplicate "/-," chars and replace with the single char. # Remove any trailing "/-," chars. # Anything else - you're on your own ;-) lead_trim = (year != 0 && format.lstrip.start_with?("%Y")) ? /\A[\/\,\s]+/ : /\A[\/\,\-\s]+/ result = result.gsub(lead_trim, '').gsub(/\s\s/, ' ').gsub(/[\/\-\,]([\/\-\,])/, '\1').gsub(/[\/\,\-\s]+\z/, '') end
Public: Returns a formatted string representation of date. A subset of date formatters have been implemented including: %Y - Year with century (can be negative, and will be padded to 4 digits at least)
-0001, 0000, 1995, 2009, 14292, etc.
%m - Month of the year, zero-padded (01..12) %B - The full month name (‘January’) %b - The abbreviated month name (‘Jan’) %d - Day of the month, zero-padded (01..31) %e - Day of the month, blank-padded ( 1..31)
Examples
date = PartialDate::Date.new {|d| d.year = 2012, d.month = 12, d.day = 31} date.to_s # => "2012-12-31"
Returns string representation of date.
# File lib/partial-date/date.rb, line 289 def to_s(format = :default) format = FORMATS[format] if format.is_a?(Symbol) s = format.dup n = b = 0 a = 1 while n < s.length if s[n] == "%" && FORMAT_METHODS.include?(s[n..n+1]) t = FORMAT_METHODS[s[n..n+1]].call( self ) if t.length == 0 if n >= 0 && n < s.length - 2 a = a + 1 if s[n+2] =~ REMOVALS else b = n - 1 if s[n-1] =~ REMOVALS end end s.slice!(b..n+a) s.insert(b, t) n = b = b + t.length a = 1 else n = b += 1 end end s end
Public: Get the integer date value in partial date format.
Examples
date.year = "2012" date.value # => 20120000
Returns an integer representation of a partial date.
# File lib/partial-date/date.rb, line 155 def value self.class.get_date(@bits) end
Public: Set a date value using an interger in partial date format.
Examples
date.value = 20121200
Returns nothing
# File lib/partial-date/date.rb, line 166 def value=(value) if value.is_a?(Integer) && (value >= -10485761231 && value <= 10485761231) @bits = self.class.set_date(@bits, value) else raise PartialDateError, "Date value must be an integer betwen -10485761231 and 10485761231" end end
Public: Get the year from a partial date.
# File lib/partial-date/date.rb, line 206 def year self.class.get_year(@bits) end
Public: Sets the year portion of a partial date.
value - The string or integer value for a year.
Examples
date.year = "2000" date.value # => 20000000
Returns nothing
# File lib/partial-date/date.rb, line 185 def year=(value) value = 0 if value.nil? if value.is_a?(String) if value.length == 0 value = 0 elsif value =~ /\A\-?\d{1,7}\z/ value = value.to_i else raise YearError, "Year must be a valid string or integer from -1048576 to 1048576" end end if (value >= -1048576 && value <= 1048576) @bits = self.class.set_year(@bits, value) else raise YearError, "Year must be an integer from -1048576 to 1048576" end end