fuzzy-date 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []