fuzzy-date 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +21 -0
  4. data/demo.rb +6 -0
  5. data/lib/fuzzy-date.rb +241 -0
  6. metadata +48 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 264e777d85860b2076170b396de54ed3df1fa26a
4
+ data.tar.gz: c80cc54168d8ab911d08d97d34208f9fd7f2ba36
5
+ SHA512:
6
+ metadata.gz: 72b5a5a0623ebbf7908295b0bf04291765630d1317ce61b2fad77cf36cfddc55a80e9a2f56aee226c020a123b5e3c9fd81caeb6b8120e604b6b2aca0cfcab43d
7
+ data.tar.gz: 2701bc6fa8ae4a648fe89c1b2aedba5adaf65b534569420023390e797a107823c9e4e32622df669d8605278830445bef8687c7695f63a317ff940373070fc4b7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) <year> <copyright holders>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ ## fuzzy-date
2
+
3
+ The fuzzy-date gem provides a way to parse and use incomplete dates, like those found in history or genealogy.
4
+
5
+ For example, if you know your great-great-great grandmother was born in April, 1836, but you don't know the day of the month, that's not going to work if you try to parse it as a Date object.
6
+
7
+ With fuzzy-date, when you parse an incomplete date, you'll be given a hash of information about the date that can be stored as a string.
8
+
9
+ ## Installation
10
+
11
+ `gem install fuzzy-date`
12
+
13
+ ## Contributing
14
+
15
+ Please contribute to fuzzy-date!
16
+
17
+ TODO: Support for ranges.
18
+
19
+ ## License
20
+
21
+ fuzzy-date is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/demo.rb ADDED
@@ -0,0 +1,6 @@
1
+ require( 'rubygems' )
2
+ require( 'fuzzy-date' )
3
+
4
+ fuzzy_date = FuzzyDate::parse_date( '15 March 1971' )
5
+ puts fuzzy_date.inspect
6
+
data/lib/fuzzy-date.rb ADDED
@@ -0,0 +1,241 @@
1
+ require 'date'
2
+
3
+ class FuzzyDate
4
+
5
+ @month_names = {
6
+ 1 => 'January',
7
+ 2 => 'February',
8
+ 3 => 'March',
9
+ 4 => 'April',
10
+ 5 => 'May',
11
+ 6 => 'June',
12
+ 7 => 'July',
13
+ 8 => 'August',
14
+ 9 => 'September',
15
+ 10 => 'October',
16
+ 11 => 'November',
17
+ 12 => 'December'
18
+ }
19
+
20
+ @month_abbreviations = {
21
+ 'Jan' => 'January',
22
+ 'Feb' => 'February',
23
+ 'Mar' => 'March',
24
+ 'Apr' => 'April',
25
+ 'May' => 'May',
26
+ 'Jun' => 'June',
27
+ 'Jul' => 'July',
28
+ 'Aug' => 'August',
29
+ 'Sep' => 'September',
30
+ 'Oct' => 'October',
31
+ 'Nov' => 'November',
32
+ 'Dec' => 'December'
33
+ }
34
+
35
+ @days_in_month = {
36
+ 1 => 31,
37
+ 2 => 28,
38
+ 3 => 31,
39
+ 4 => 30,
40
+ 5 => 31,
41
+ 6 => 30,
42
+ 7 => 31,
43
+ 8 => 31,
44
+ 9 => 30,
45
+ 10 => 31,
46
+ 11 => 30,
47
+ 12 => 31
48
+ }
49
+
50
+ @range_words = [
51
+ 'Between',
52
+ 'Bet',
53
+ 'Bet.',
54
+ 'From'
55
+ ]
56
+
57
+ @middle_range_words = [
58
+ # '-', - Not used because it is more commonly used as a delimiter
59
+ 'To',
60
+ 'And'
61
+ ]
62
+
63
+ @circa_words = [
64
+ 'Circa',
65
+ 'About',
66
+ 'Abt',
67
+ 'Abt.',
68
+ '~'
69
+ ]
70
+
71
+ @era_words = [
72
+ 'AD',
73
+ 'BC',
74
+ 'CE',
75
+ 'BCE'
76
+ ]
77
+
78
+ # *Note*: This is only for single dates - not ranges.
79
+ #
80
+ # Possible incoming date formats:
81
+ # * YYYY-MM-DD - starts with 3 or 4 digit year, and month and day may be 1 or 2 digits
82
+ # * YYYY-MM - 3 or 4 digit year, then 1 or 2 digit month
83
+ # * YYYY - 3 or 4 digit year
84
+ # * MM-DD-YYYY - 1 or 2 digit month, then 1 or 2 digit day, then 1 to 4 digit year
85
+ # * DD-MM-YYYY - 1 or 2 digit day, then 1 or 2 digit month, then 1 to 4 digit year if is_euro is true
86
+ # * MM-YYYY - 1 or 2 digit month, then 1 to 4 digit year
87
+ # * DD-MMM - 1 or 2 digit day, then month name or abbreviation
88
+ # * DD-MMM-YYYY - 1 or 2 digit day, then month name or abbreviation, then 1 to 4 digit year
89
+ # * MMM-YYYY - month name or abbreviation, then 1 to 4 digit year
90
+ # * MMM-DD-YYYY - month name or abbreviation, then 1 or 2 digit day, then 1 to 4 digit year
91
+ #
92
+ # Notes:
93
+ # - Commas are optional.
94
+ # - Delimiters can be most anything non-alphanumeric.
95
+ # - All dates may be suffixed with the era (AD, BC, CE, BCE). AD is assumed.
96
+ # - Dates may be prefixed by circa words (Circa, About, Abt).
97
+
98
+ def self.parse_date( date, is_euro = false )
99
+
100
+ date = clean_parameter date
101
+
102
+ return '' if date == ''
103
+
104
+ date_parts = {}
105
+ date_parts[ :original ] = date
106
+ date_parts[ :circa ] = false
107
+ date_parts[ :year ] = nil
108
+ date_parts[ :month ] = nil
109
+ date_parts[ :day ] = nil
110
+ date_parts[ :era ] = 'AD'
111
+
112
+ date_in_parts = []
113
+
114
+ date_separator = Regexp.new '[^A-Za-z0-9]', true
115
+
116
+ #- Split the string
117
+
118
+ date_in_parts = date.split date_separator
119
+ date_in_parts.delete_if { |d| d.to_s.empty? }
120
+ if date_in_parts.first.match Regexp.new( @circa_words.join( '|' ), true )
121
+ date_parts[ :circa ] = true
122
+ date_in_parts.shift
123
+ end
124
+ if date_in_parts.last.match Regexp.new( @era_words.join( '|' ), true )
125
+ date_parts[ :era ] = date_in_parts.pop.upcase.strip
126
+ end
127
+
128
+ date = date_in_parts.join '-'
129
+ date_parts[ :fixed ] = date
130
+
131
+ #- Takes care of YYYY
132
+ if date =~ /^(\d{1,4})$/
133
+ year = $1.to_i.to_s
134
+ month = nil
135
+ day = nil
136
+
137
+ #- Takes care of YYYY-MM-DD and YYYY-MM
138
+ elsif date =~ /^(\d{3,4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?$/
139
+ year = $1.to_i.to_s
140
+ month = $2 ? $2.to_i.to_s : nil
141
+ day = $3 ? $3.to_i.to_s : nil
142
+
143
+ #- Takes care of DD-MM-YYYY
144
+ elsif date =~ /^(\d{1,2})-(\d{1,2})-(\d{1,4})$/ and is_euro
145
+ day = $1.to_i.to_s
146
+ month = $2.to_i.to_s
147
+ year = $3.to_i.to_s
148
+
149
+ #- Takes care of MM-DD-YYYY
150
+ elsif date =~ /^(\d{1,2})-(\d{1,2})-(\d{1,4})$/
151
+ month = $1.to_i.to_s
152
+ day = $2.to_i.to_s
153
+ year = $3.to_i.to_s
154
+
155
+ #- Takes care of MM-YYYY
156
+ elsif date =~ /^(\d{1,2})-(\d{1,4})?$/
157
+ month = $1.to_i.to_s
158
+ day = nil
159
+ year = $2.to_i.to_s
160
+
161
+ #- Takes care of DD-MMM-YYYY and DD-MMM
162
+ elsif date =~ /^(\d{1,2})(?:-(#{ @month_abbreviations.keys.join( '|' ) }).*?(?:-(\d{1,4}))?)?$/i
163
+ month_text = $2.to_s.capitalize
164
+ month = @month_names.key( @month_abbreviations[ month_text ] ).to_i.to_s
165
+ day = $1.to_i.to_s
166
+ year = $3 ? $3.to_i.to_s : nil
167
+
168
+ #- Takes care of MMM-DD-YYYY
169
+ elsif date =~ /^(#{ @month_abbreviations.keys.join( '|' ) }).*?-(\d{1,2})-(\d{1,4})$/i
170
+ month_text = $1.to_s.capitalize
171
+ month = @month_names.key( @month_abbreviations[ month_text ] ).to_i.to_s
172
+ day = $2.to_i.to_s
173
+ year = $3 ? $3.to_i.to_s : nil
174
+
175
+ #- Takes care of MMM-YYYY and MMM
176
+ elsif date =~ /^(#{ @month_abbreviations.keys.join( '|' ) }).*?(?:-(\d{1,4}))?$/i
177
+ month_text = $1.to_s.capitalize
178
+ month = @month_names.key( @month_abbreviations[ month_text ] ).to_i.to_s
179
+ day = nil
180
+ year = $2 ? $2.to_i.to_s : nil
181
+
182
+ else
183
+ raise ArgumentError.new( 'Cannot parse date.' )
184
+ end
185
+
186
+ date_parts[ :year ] = year ? year.to_i : nil
187
+ date_parts[ :month ] = month ? month.to_i : nil
188
+ date_parts[ :day ] = day ? day.to_i : nil
189
+ #return { :circa => "day: #{ day }, month: #{ month }, year: #{ year }" }
190
+
191
+ #- Some error checking at this point
192
+ if month.to_i > 13
193
+ raise ArgumentError.new( 'Month cannot be greater than 12.' )
194
+ elsif month and day and day.to_i > @days_in_month[ month.to_i ]
195
+ unless month.to_i == 2 and year and Date.parse( '1/1/' + year ).leap? and day.to_i == 29
196
+ raise ArgumentError.new( 'Too many days in this month.' )
197
+ end
198
+ elsif month and month.to_i < 1
199
+ raise ArgumentError.new( 'Month cannot be less than 1.' )
200
+ elsif day and day.to_i < 1
201
+ raise ArgumentError.new( 'Day cannot be less than 1.' )
202
+ end
203
+
204
+ month_name = @month_names[ month.to_i ]
205
+ date_parts[ :month_name ] = month_name
206
+
207
+ # ----------------------------------------------------------------------
208
+
209
+ show_era = ' ' + date_parts[ :era ]
210
+ show_circa = date_parts[ :circa ] == true ? 'About ' : ''
211
+
212
+ if year and month and day
213
+ date_parts[ :short ] = show_circa + month + '/' + day + '/' + year + show_era
214
+ date_parts[ :long ] = show_circa + month_name + ' ' + day + ', ' + year + show_era
215
+ modified_long = show_circa + month_name + ' ' + day + ', ' + year.rjust( 4, "0" ) + show_era
216
+ date_parts[ :full ] = show_circa + Date.parse( modified_long ).strftime( '%A,' ) + Date.parse( day + ' ' + month_name + ' ' + year.rjust( 4, "0" ) ).strftime( ' %B %-1d, %Y' ) + show_era
217
+ elsif year and month
218
+ date_parts[ :short ] = show_circa + month + '/' + year + show_era
219
+ date_parts[ :long ] = show_circa + month_name + ', ' + year + show_era
220
+ date_parts[ :full ] = date_parts[ :long ]
221
+ elsif month and day
222
+ month_text = @month_abbreviations.key(month_text) || month_text
223
+ date_parts[ :short ] = show_circa + day + '-' + month_text
224
+ date_parts[ :long ] = show_circa + day + ' ' + month_name
225
+ date_parts[ :full ] = date_parts[ :long ]
226
+ elsif year
227
+ date_parts[ :short ] = show_circa + year + show_era
228
+ date_parts[ :long ] = date_parts[ :short ]
229
+ date_parts[ :full ] = date_parts[ :long ]
230
+ end
231
+
232
+ return date_parts
233
+
234
+ end
235
+
236
+ private
237
+
238
+ def self.clean_parameter( date )
239
+ date.to_s.strip if date.respond_to? :to_s
240
+ end
241
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fuzzy-date
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - David Cole
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: The fuzzy-date gem provides a way to parse and use incomplete dates,
14
+ like those found in history or genealogy.
15
+ email: david.cole@digitalcharleston.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - LICENSE
22
+ - demo.rb
23
+ - lib/fuzzy-date.rb
24
+ homepage: https://github.com/davidcole/fuzzy-date
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.1.11
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: fuzzy-date provides a way to use partial and incomplete dates.
48
+ test_files: []