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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +21 -0
- data/demo.rb +6 -0
- data/lib/fuzzy-date.rb +241 -0
- 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
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: []
|