Class: FuzzyDate

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/fuzzy_date.rb

Overview

A fuzzy date is a representation of a date which may well be incomplete or imprecise. You can enter the exact date or for example just the day of the week or the year or a combination thereof. One can also add the Circa prefix to any date. The FuzzyDate object is immutable so if you wish to change a FuzzyDate value it is neccessary to create a new FuzzyDate.

Defined Under Namespace

Classes: FDate

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(year = nil, month = nil, day = nil, wday = nil, circa = nil) ⇒ FuzzyDate

create a new FuzzyDate object. There are no checks here to validate if the date is valid or not.. eg there is no check that the day of the week actually corresponds to the actual date if completely specified.



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/fuzzy_date.rb', line 60

def initialize(year=nil,month=nil,day=nil,wday=nil,circa=nil)
  raise "FuzzyDate: invalid month"   if month && ((month>12) || (month<1))
  raise "FuzzyDate: invalid day"     if day   && ((day>31)   || (day<1))
  raise "FuzzyDate: invalid weekday" if wday  && ((wday>6)   || (wday<0))
  raise "FuzzyDate: year too big !"  if year  && (year.abs > 99999999999 )
  @year  = year && year.abs
  @month = month
  @day   = day
  @wday  = wday
  @circa = circa
  @bce   = year && (year.to_i < 0)
end

Class Method Details

.new_from_date(date) ⇒ Object

create a FuzzyDate object from a Date object



209
210
211
# File 'lib/fuzzy_date.rb', line 209

def self.new_from_date( date)
  new(date.year,date.month,date.day)
end

.new_from_db(i) ⇒ Object

create a new FuzzyDate object from the database formatted integer.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/fuzzy_date.rb', line 164

def self.new_from_db(i)
  return nil unless i
  str = i.to_s
  return nil if str == '0'
  bce = false
  raise "Invalid Fuzzy Time String - #{str}" unless str =~/^[+-]?\d{6,}$/
  str.sub!(/^-/){|m| bce= true;""} 
  str = ("000000" + str)[-7,7] if str.length < 7
  circa   =  (str[-1,1] == '1')
  year    =  (str[-2,1] == '1' ? nil : 0)
  wday    = str[-3,1].to_i - 1
  day     = str[-5,2].to_i
  month   = str[-7,2].to_i
  wday    = nil if ((wday<0) || (wday>6))
  day     = nil if ((day==0) || (day>31))
  month   = nil if ((month==0) || (month > 12))
  year    = (str[0..-8].to_i > 0 ? str[0..-8].to_i : year )
  year = -year if year && bce
  new(year,month,day,wday,circa)
end

.parse(str) ⇒ Object

create a new date object by parsing a string



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/fuzzy_date.rb', line 214

def self.parse( str )
  
  return unless str && str.length > 0
  
  continue = true
  circa    = false
  bce      = false
  unknown  = false
  
  #filter out 'c' or 'circa'
  str.sub!(/CIRCA/i){|m| circa=true;continue=nil} if continue
  str.sub!(/^CA /i){|m| circa=true;continue=nil} if continue
  str.sub!(/^C /i){|m| circa=true;continue=nil} if continue
  str.sub!(/ABOUT/i){|m| circa=true;continue=nil} if continue
  str.sub!(/AROUND/i){|m| circa=true;continue=nil} if continue
  str.sub!(/ROUND/i){|m| circa=true;continue=nil} if continue
  str.sub!(/APPROX/i){|m| circa=true;continue=nil} if continue
  str.sub!(/APPROXIMATELY/i){|m| circa=true;continue=nil} if continue
  
  #filter out 'bc' 'bce'
  continue = true
  str.sub!(/BCE/i){|m| bce=true;continue=nil}
  str.sub!(/BC/i){|m| bce=true;continue=nil} if continue
  
  #filter out 'unknown'
  continue = true
  str.sub!(/UNKNOWN/i){|m| unknown=true;continue=nil}
  
  # if date is unknown then return an empty FuzzyDate
  return self.new if unknown
  
  # now try to parse the remaining string with the Date parse
  # method.
  
  components= FDate._parse(str,false)
  year  = components[:year]
  month = components[:mon]
  day   = components[:mday]
  wday  = components[:wday]
  
  # fudge the results a bit
  year,day = day,nil if (day && !month && !year) || (!year && (day.to_i > 31))
  if year && year < 0 
    year = year.abs
    bce  = true
  end
  
  year = -year if bce
  self.new(year,month,day,wday,circa)
end

Instance Method Details

#<=>(other) ⇒ Object



265
# File 'lib/fuzzy_date.rb', line 265

def <=>(other) self.to_db <=> other.to_db end

#bce?Boolean

is the date before the year zero.

Returns:

  • (Boolean)


104
105
106
# File 'lib/fuzzy_date.rb', line 104

def bce?
  @bce
end

#birthdayObject

return an integer representing only the month and day



109
110
111
# File 'lib/fuzzy_date.rb', line 109

def birthday
  month * 100 + day if month && day
end

#circa?Boolean

is the date approximate ?

Returns:

  • (Boolean)


99
100
101
# File 'lib/fuzzy_date.rb', line 99

def circa?
  @circa
end

#complete?Boolean

is the date complete

Returns:

  • (Boolean)


114
115
116
# File 'lib/fuzzy_date.rb', line 114

def complete?
  year && (month  && month < 13) && (day  && day < 32)
end

#dayObject

returns the day of the month ( 1..n ). returns nil if not known.



84
85
86
# File 'lib/fuzzy_date.rb', line 84

def day
  @day
end

#monthObject

returns the month number (1..12). returns nil if not known.



89
90
91
# File 'lib/fuzzy_date.rb', line 89

def month
  @month
end

#to_dateObject

if the date is complete then return a regular Date object



203
204
205
206
# File 'lib/fuzzy_date.rb', line 203

def to_date
  return nil unless complete?
  Date.new(@year,@month,@day)
end

#to_dbObject

convert the fuzzy date into a format which can be stored in a database. The storage format is integer format (BIGINT) with digits having the following positional significance:

(-/+)  (YYYYYYYYYYY.....Y)MMDDd[01][01]

where + is AD or CE
      - is BC or BCE
      1 at end = circa or C otherwise 0
      1 at 2nd from end  = year unknown - otherwise missing year = year 0
      YYYYY is the year number 0 or missing  = year absent OR value is unknown
      MM    is the month number or 13 for unknown
      DD    is the day of the month or 32 for unknown
      d     is the day of the week where 8 = unknown, 1=Sunday .. 7=Saturday

this wierd format has been chosen to allow sorting within the database and to
avoid leading zeros containing information from being stripped from the representation.

save in a  mysql database in format BIGINT


149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/fuzzy_date.rb', line 149

def to_db
  str = ''
  str= str + (@year > 0 ? @year.to_s : '') if @year
  str= str + (@month ? "%02d" % @month.to_s : '13')
  str= str + (@day ? "%02d" % @day.to_s : '32')
  str= str + (@wday ? (@wday+1).to_s : '8')
  str= str + (@year  ? '0' : '1')
  str= str + (@circa  ? '1' : '0')
  i = str.to_i
  i = -i if @bce
  i
end

#to_iObject

convert to integer format



124
125
126
# File 'lib/fuzzy_date.rb', line 124

def to_i
  to_db
end

#to_sObject

convert to a human readable string



186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/fuzzy_date.rb', line 186

def to_s
  if unknown?
    str = "unknown"
  else
    str = ""
    str = "circa "                        if circa?
    str+= FDate::DAYNAMES[wday]    + " "  if wday 
    str+= day.to_s + " "                  if day 
    str+= FDate::MONTHNAMES[month] + " "  if month
    str+= year.to_s                       if year 
    str += " bce"                         if bce?
    str.strip
  end
end

#unknown?Boolean

is the date completely unknown

Returns:

  • (Boolean)


119
120
121
# File 'lib/fuzzy_date.rb', line 119

def unknown?
  !@year && !@month && !@day && !@wday
end

#wdayObject

returns an integer representing the day of the week, 0..6 with Sunday=0. returns nil if not known.



75
76
77
78
79
80
81
# File 'lib/fuzzy_date.rb', line 75

def wday 
  if !@wday && complete? && (@year > 0)
    to_date.wday
  else
    @wday
  end
end

#yearObject

returns the year number (including century)



94
95
96
# File 'lib/fuzzy_date.rb', line 94

def year
  @year
end