nickel 0.0.3 → 0.0.4

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.
@@ -0,0 +1,26 @@
1
+ # Ruby Nickel Library
2
+ # Copyright (c) 2008-2011 Lou Zell, lzell11@gmail.com, http://hazelmade.com
3
+ # MIT License [http://www.opensource.org/licenses/mit-license.php]
4
+
5
+ module Nickel
6
+ module NLPQueryConstants
7
+ DATE_DD = %r{\b((?:0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th)?)\b}
8
+ DATE_DD_NB_ON_SUFFIX = %r{\b(0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th)?\b}
9
+ DATE_DD_NB = %r{\b(?:0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th)?\b}
10
+ DATE_DD_WITH_SUFFIX = %r{\b((?:0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th))\b}
11
+ DATE_DD_WITHOUT_SUFFIX = %r{\b(0?[1-9]|[12][0-9]|3[01])\b}
12
+ DATE_DD_WITH_SUFFIX_NB = %r{\b(?:0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th)\b}
13
+ DATE_DD_WITHOUT_SUFFIX_NB = %r{\b(?:0?[1-9]|[12][0-9]|3[01])\b}
14
+ DATE_DD_WITH_SUFFIX_NB_ON_SUFFIX = %r{\b(0?[1-9]|[12][0-9]|3[01])(?:st|nd|rd|th)\b}
15
+ DATE_MM_SLASH_DD = %r{\b(?:0?[1-9]|[1][0-2])\/(?:0?[1-9]|[12][0-9]|3[01])}
16
+ DAY_OF_WEEK = %r{\b(mon|tue|wed|thu|fri|sat|sun)\b}
17
+ DAY_OF_WEEK_NB = %r{\b(?:mon|tue|wed|thu|fri|sat|sun)\b} #no backreference
18
+ MONTH_OF_YEAR = %r{\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\b}
19
+ MONTH_OF_YEAR_NB = %r{\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\b}
20
+ TIME_24HR = %r{[01]?[0-9]|2[0-3]:[0-5][0-9]}
21
+ TIME_12HR = %r{(?:0?[1-9]|1[0-2])(?::[0-5][0-9])?(?:am|pm)?} # note 12 passes, as does 12:00
22
+ TIME = %r{(#{TIME_12HR}|#{TIME_24HR})}
23
+ YEAR = %r{((?:20)?0[789](?:\s|\n)|(?:20)[1-9][0-9])}
24
+ WEEK_OF_MONTH = %r{(1st|2nd|3rd|4th|5th)}
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ module Kernel
2
+ private
3
+ def method_name
4
+ caller[0] =~ /`([^']*)'/ and $1
5
+ end
6
+ def calling_method
7
+ md = caller[1].match(/(\d+):in `([^']*)'/)
8
+ md && "#{md[2]}:#{md[1]}"
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class Fixnum
2
+ def to_s2
3
+ str = self.to_s
4
+ str.sub(/^(\d)$/,'0\1')
5
+ end
6
+ end
7
+
8
+ class String
9
+ def to_s2
10
+ sub(/^(\d)$/,'0\1')
11
+ end
12
+ end
@@ -0,0 +1,503 @@
1
+ # Ruby Nickel Library
2
+ # Copyright (c) 2008-2011 Lou Zell, lzell11@gmail.com, http://hazelmade.com
3
+ # MIT License [http://www.opensource.org/licenses/mit-license.php]
4
+
5
+ module Nickel
6
+
7
+ # TODO: get methods should accept dayname or dayindex
8
+ class ZDate
9
+ @days_of_week = ["mon","tue","wed","thu","fri","sat","sun"]
10
+ @full_days_of_week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
11
+ @months_of_year = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]
12
+ @full_months_of_year = ["january","february","march","april","may","june","july","august","september","october","november","december"]
13
+ @days_in_common_year_months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
14
+ @days_in_leap_year_months = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
15
+
16
+ MON = 0
17
+ TUE = 1
18
+ WED = 2
19
+ THU = 3
20
+ FRI = 4
21
+ SAT = 5
22
+ SUN = 6
23
+
24
+ class << self
25
+ attr_reader :days_of_week, :months_of_year, :days_in_common_year_months, :days_in_leap_year_months, :full_days_of_week, :full_months_of_year
26
+ end
27
+
28
+ # Don't use attr_accessor for date, year, month, day; we want to validate on change.
29
+ def initialize(yyyymmdd = nil)
30
+ d = yyyymmdd ? yyyymmdd.dup : ::Time.new.strftime("%Y%m%d")
31
+ d.gsub!(/-/,'') # remove any hyphens, so a user can initialize with something like "2008-10-23"
32
+ self.date = d
33
+ end
34
+
35
+ def date
36
+ @date
37
+ end
38
+
39
+ def date=(yyyymmdd)
40
+ @date = yyyymmdd
41
+ validate
42
+ end
43
+
44
+ def year_str
45
+ @date[0..3]
46
+ end
47
+
48
+ def month_str
49
+ @date[4..5]
50
+ end
51
+
52
+ def day_str
53
+ @date[6..7]
54
+ end
55
+
56
+ def year
57
+ year_str.to_i
58
+ end
59
+
60
+ def month
61
+ month_str.to_i
62
+ end
63
+
64
+ def day
65
+ day_str.to_i
66
+ end
67
+
68
+ def readable
69
+ month_str + '/' + day_str + '/' + year_str
70
+ end
71
+
72
+ def fmt(txt)
73
+ txt.gsub!(/%Y/, self.year_str)
74
+ txt.gsub!(/%m/, self.month_str)
75
+ txt.gsub!(/%d/, self.day_str)
76
+ end
77
+
78
+ def <(d2)
79
+ (self.year < d2.year) || (self.year == d2.year && (self.month < d2.month || (self.month == d2.month && self.day < d2.day)))
80
+ end
81
+
82
+ def <=(d2)
83
+ (self.year < d2.year) || (self.year == d2.year && (self.month < d2.month || (self.month == d2.month && self.day <= d2.day)))
84
+ end
85
+
86
+ def >(d2)
87
+ (self.year > d2.year) || (self.year == d2.year && (self.month > d2.month || (self.month == d2.month && self.day > d2.day)))
88
+ end
89
+
90
+ def >=(d2)
91
+ (self.year > d2.year) || (self.year == d2.year && (self.month > d2.month || (self.month == d2.month && self.day >= d2.day)))
92
+ end
93
+
94
+ def ==(d2)
95
+ self.year == d2.year && self.month == d2.month && self.day == d2.day
96
+ end
97
+
98
+ def <=>(d2)
99
+ if self < d2
100
+ -1
101
+ elsif self > d2
102
+ 1
103
+ else
104
+ 0
105
+ end
106
+ end
107
+
108
+ # returns true if self is today
109
+ def is_today?
110
+ self == ZDate.new
111
+ end
112
+
113
+ # for example, "1st friday", uses self as the reference month
114
+ def ordinal_dayindex(num, day_index)
115
+ # create a date object at the first occurrence of day_index
116
+ first_occ_date = ZDate.new(year_str + month_str + "01").this(day_index)
117
+ # if num is 1 through 4, we can just add (num-1) weeks
118
+ if num <= 4
119
+ d = first_occ_date.add_weeks(num - 1)
120
+ else
121
+ # we want the last occurrence of this month
122
+ # add 4 weeks to first occurrence, see if we are in the same month, subtract 1 week if we are not
123
+ d = first_occ_date.add_weeks(4)
124
+ if d.month != self.month then d = d.sub_weeks(1) end
125
+ end
126
+ d
127
+ end
128
+
129
+ # for example, "this friday"
130
+ def this(day)
131
+ x_weeks_from_day(0, day)
132
+ end
133
+
134
+ # for example, "next friday"
135
+ def next(day)
136
+ x_weeks_from_day(1, day)
137
+ end
138
+
139
+ # for example, "previous friday"
140
+ def prev(day)
141
+ (self.dayindex == day) ? self.dup : x_weeks_from_day(-1,day)
142
+ end
143
+
144
+ # returns a new date object
145
+ def x_weeks_from_day(weeks_away, day2index)
146
+ day1index = self.dayindex
147
+ if day1index > day2index
148
+ days_away = 7 * (weeks_away + 1) - (day1index - day2index)
149
+ elsif day1index < day2index
150
+ days_away = (weeks_away * 7) + (day2index - day1index)
151
+ elsif day1index == day2index
152
+ days_away = 7 * weeks_away
153
+ end
154
+ self.add_days(days_away) # returns a new date object
155
+ end
156
+
157
+ # add_ methods return new ZDate object, they DO NOT modify self
158
+ def add_days(number)
159
+ if number < 0 then return sub_days(number.abs) end
160
+ o = self.dup # new ZDate object
161
+ # Let's see what month we are going to end in
162
+ while number > 0
163
+ if o.days_left_in_month >= number
164
+ o.date = o.year_str + o.month_str + (o.day + number).to_s2
165
+ number = 0
166
+ else
167
+ number = number - 1 - o.days_left_in_month #it costs 1 day to increment the month
168
+ o.increment_month!
169
+ end
170
+ end
171
+ o
172
+ end
173
+
174
+ def add_weeks(number)
175
+ self.add_days(7*number)
176
+ end
177
+
178
+ def add_months(number)
179
+ new_month = 1 + ((month - 1 + number) % 12)
180
+ if number > months_left_in_year # are we going to change year?
181
+ years_to_increment = 1 + ((number - months_left_in_year) / 12) # second term adds years if user entered a large number of months (e.g. date.add_months(50))
182
+ else
183
+ years_to_increment = 0
184
+ end
185
+ new_year = year + years_to_increment
186
+ new_day = get_day_or_max_day_in_month(self.day, new_month, new_year)
187
+ ZDate.new(new_year.to_s + new_month.to_s2 + new_day.to_s2)
188
+ end
189
+
190
+ def add_years(number)
191
+ new_year = year + number
192
+ new_day = get_day_or_max_day_in_month(self.day, self.month, new_year)
193
+ ZDate.new(new_year.to_s + self.month_str + new_day.to_s2)
194
+ end
195
+
196
+ # DEPRECATED, change_ methods in ZTime modify self, this was confusing,
197
+ # change_ methods return new ZDate object, they DO NOT modify self
198
+ # def change_year_to(y)
199
+ # o = ZDate.new(y.to_s + self.month_str + self.day_str)
200
+ # o
201
+ # end
202
+ # def change_day_to(d)
203
+ # o = ZDate.new(self.year_str + self.month_str + d.to_s2)
204
+ # o
205
+ # end
206
+
207
+ # returns new ZDate object, note this is the MONTH NUMBER, not MONTH INDEX from ZDate.months_of_year
208
+ # returns the first day of the month
209
+ def jump_to_month(month_number)
210
+ # find difference in months
211
+ if month_number >= self.month
212
+ ZDate.new(year_str + (month_number).to_s2 + "01")
213
+ else
214
+ ZDate.new((year + 1).to_s + (month_number).to_s2 + "01")
215
+ end
216
+ end
217
+
218
+ # beginning and end of month both return new ZDate objects
219
+ def beginning_of_month
220
+ ZDate.new(year_str + month_str + "01")
221
+ end
222
+
223
+ def end_of_month
224
+ ZDate.new(year_str + month_str + self.days_in_month.to_s)
225
+ end
226
+
227
+ def beginning_of_next_month
228
+ o = self.dup
229
+ o.increment_month!
230
+ o
231
+ end
232
+
233
+ # sub_ methods return new ZDate object, they do not modify self.
234
+ def sub_days(number)
235
+ o = self.dup
236
+ while number > 0
237
+ if (o.day - 1) >= number
238
+ o.date = o.year_str + o.month_str + (o.day - number).to_s2
239
+ number = 0
240
+ else
241
+ number = number - o.day
242
+ o.decrement_month!
243
+ end
244
+ end
245
+ o
246
+ end
247
+
248
+ def sub_weeks(number)
249
+ self.sub_days(7 * number)
250
+ end
251
+
252
+ def sub_months(number)
253
+ o = self.dup
254
+ number.times do
255
+ o.decrement_month!
256
+ end
257
+ o
258
+ end
259
+
260
+ # Gets the absolute difference in days between self and date_to_compare, order is not important.
261
+ def diff_in_days(date_to_compare)
262
+ # d1 will be the earlier date, d2 the later
263
+ if date_to_compare > self
264
+ d1, d2 = self.dup, date_to_compare.dup
265
+ elsif self > date_to_compare
266
+ d1, d2 = date_to_compare.dup, self.dup
267
+ else
268
+ return 0 # same date
269
+ end
270
+
271
+ total = 0
272
+ while d1.year != d2.year
273
+ total += d1.days_left_in_year + 1 # need one extra day to push us to jan 1
274
+ d1 = ZDate.new((d1.year + 1).to_s + "0101")
275
+ end
276
+ total += d2.day_of_year - d1.day_of_year
277
+ total
278
+ end
279
+
280
+ def diff_in_days_to_this(closest_day_index)
281
+ if closest_day_index >= self.dayindex
282
+ closest_day_index - self.dayindex # could be 0
283
+ else # day_num < self.dayindex
284
+ 7 - (self.dayindex - closest_day_index)
285
+ end
286
+ end
287
+
288
+ # We need days_in_months and diff_in_months to be available at the class level as well.
289
+ class << self
290
+ def new_first_day_in_month(month, year)
291
+ ZDate.new(year.to_s + month.to_s2 + "01")
292
+ end
293
+
294
+ def new_last_day_in_month(month, year)
295
+ day = days_in_month(month, year)
296
+ ZDate.new(year.to_s + month.to_s2 + day.to_s2)
297
+ end
298
+
299
+ def days_in_month(month, year)
300
+ if year % 400 == 0 || year % 4 == 0 && year % 100 != 0
301
+ ZDate.days_in_leap_year_months[month - 1]
302
+ else
303
+ ZDate.days_in_common_year_months[month - 1]
304
+ end
305
+ end
306
+
307
+ # Gets the difference FROM month1, year1 TO month2, year2
308
+ # don't use it the other way around, it won't work
309
+ def diff_in_months(month1, year1, month2, year2)
310
+ # first get the difference in months
311
+ if month2 >= month1
312
+ diff_in_months = month2 - month1
313
+ else
314
+ diff_in_months = 12 - (month1 - month2)
315
+ year2 -= 1 # this makes the next line nice
316
+ end
317
+ diff_in_months += (year2 - year1) * 12
318
+ end
319
+ end
320
+
321
+ # difference in months FROM self TO date2, for instance, if self is oct 1 and date2 is nov 14, will return 1
322
+ # if self is nov 14 and date2 is oct 1, will return -1
323
+ def diff_in_months(date2)
324
+ if date2 > self
325
+ ZDate.diff_in_months(self.month, self.year, date2.month, date2.year)
326
+ else
327
+ ZDate.diff_in_months(date2.month, date2.year, self.month, self.year) * -1
328
+ end
329
+ end
330
+
331
+ def days_in_month
332
+ if leap_year?
333
+ ZDate.days_in_leap_year_months[self.month - 1]
334
+ else
335
+ ZDate.days_in_common_year_months[self.month - 1]
336
+ end
337
+ end
338
+
339
+ def days_left_in_month
340
+ self.days_in_month - self.day
341
+ end
342
+
343
+ def months_left_in_year
344
+ 12 - self.month
345
+ end
346
+
347
+ def dayname
348
+ # well this is going to be a hack, I need an algo for finding the day
349
+ # Ruby's Time.local is the fastest way to create a Ruby Time object
350
+ t = ::Time.local(self.year, ZDate.months_of_year[self.month - 1], self.day)
351
+ t.strftime("%a").downcase
352
+ end
353
+
354
+ def dayindex
355
+ ZDate.days_of_week.index(self.dayname)
356
+ end
357
+
358
+ def full_dayname
359
+ ZDate.full_days_of_week[self.dayindex]
360
+ end
361
+
362
+ def full_monthname
363
+ Z.full_months_of_year[self.month - 1]
364
+ end
365
+
366
+ def leap_year?
367
+ self.year % 400 == 0 || self.year % 4 == 0 && self.year % 100 != 0
368
+ end
369
+
370
+ def day_of_year
371
+ doy = self.day
372
+ # iterate through days in months arrays, summing up the days
373
+ if leap_year?
374
+ doy = (1...self.month).to_a.inject(doy) {|sum, n| sum += ZDate.days_in_leap_year_months[n-1]}
375
+ else
376
+ doy = (1...self.month).to_a.inject(doy) {|sum, n| sum += ZDate.days_in_common_year_months[n-1]}
377
+ end
378
+ doy
379
+ end
380
+
381
+ def days_left_in_year
382
+ leap_year? ? 366 - self.day_of_year : 365 - self.day_of_year
383
+ end
384
+
385
+ def get_date_from_day_and_week_of_month(day_num, week_num)
386
+ # This method is extremely sloppy, clean it up
387
+ # Get the index of the first day of this month
388
+ first_day_of_month = self.beginning_of_month
389
+ first_day_index = first_day_of_month.dayindex
390
+
391
+ diff_in_days_to_first_occ = first_day_of_month.diff_in_days_to_this(day_num)
392
+
393
+ # now find the number of days to the correct occurrence; REMEMBER TO CHECK FOR LAST MONTH
394
+ if week_num == -1
395
+ total_diff_in_days = diff_in_days_to_first_occ + 21 # 7 * 3 weeks; are already at the first ocurrence, so this is total diff in days to 4th occurrence; may not be the last!!
396
+ else
397
+ total_diff_in_days = diff_in_days_to_first_occ + 7 * (week_num - 1)
398
+ end
399
+
400
+ # there is a chance that the last occurrence is not the 4th week of the month; if that is the case, add an extra 7 days
401
+ if (week_num == -1) && (self.month == self.beginning_of_month.add_days(total_diff_in_days + 7).month)
402
+ total_diff_in_days += 7
403
+ end
404
+
405
+ # Now we have the number of days FROM THE START OF THE CURRENT MONTH; if we are not past that date, then we have found the first occurrence
406
+ if (total_diff_in_days + 1) >= self.day
407
+ # Create occ_date_str and make sure it has two digits
408
+ occ_date_str = (total_diff_in_days + 1).to_s.gsub(/^(\d)$/,'0\1')
409
+ return self.beginning_of_month.add_days(total_diff_in_days)
410
+ else # We have already past the date; calculate the occurrence next month!
411
+ # Get the index of the first day next month
412
+ first_day_index = self.add_months(1).beginning_of_month.dayindex
413
+
414
+ # Find the number of days away to the day of interest (NOT the week)
415
+ if day_num > first_day_index
416
+ diff_in_days_to_first_occ = day_num - first_day_index
417
+ elsif day_num < first_day_index
418
+ diff_in_days_to_first_occ = 7 - (first_day_index - day_num)
419
+ else # first_day_index == day_num
420
+ diff_in_days_to_first_occ = 0
421
+ end
422
+
423
+ # now find the number of days to the correct occurrence; REMEMBER TO CHECK FOR LAST MONTH
424
+ if week_num == -1
425
+ total_diff_in_days = diff_in_days_to_first_occ + 21 # 7 * 3 weeks
426
+ else
427
+ total_diff_in_days = diff_in_days_to_first_occ + 7 * (week_num - 1)
428
+ end
429
+
430
+ # there is a chance that the last occurrence is not the 4th week of the month; if that is the case, add an extra 7 days
431
+ if (week_num == -1) && (self.add_months(1).month == self.add_months(1).beginning_of_month.add_days(total_diff_in_days + 7).month)
432
+ total_diff_in_days += 7
433
+ end
434
+
435
+ # Now we have the number of days FROM THE START OF THE NEXT MONTH;
436
+ # Create occ_date_str and make sure it has two digits
437
+ return self.add_months(1).beginning_of_month.add_days(total_diff_in_days)
438
+ end # END if (total_diff_in_days + 1) ...
439
+ end
440
+
441
+ # returns a new ZDate object, NOTE! this returns nil if that date does not exist (sept 31st)
442
+ def get_next_date_from_date_of_month(date_of_month)
443
+ o = self.dup
444
+ if day == date_of_month
445
+ o
446
+ else
447
+ if day > date_of_month
448
+ o.increment_month!
449
+ end
450
+ ZDate.new(o.year_str + o.month_str + date_of_month.to_s2) rescue nil
451
+ end
452
+ end
453
+
454
+ protected
455
+ # Modifies self.
456
+ # bumps self to first day of next month
457
+ def increment_month!
458
+ if month != 12
459
+ # just bump up a number
460
+ self.date = year_str + (month + 1).to_s2 + "01"
461
+ else
462
+ self.date = (year + 1).to_s + "0101"
463
+ end
464
+ end
465
+
466
+ def decrement_month!
467
+ if month != 1
468
+ # just bump down a number and set days to the last day in the month
469
+ self.date = year_str + (month - 1).to_s2 + ZDate.days_in_month(month - 1, year).to_s2
470
+ else
471
+ self.date = (year - 1).to_s + "1231" # dec has 31 days
472
+ end
473
+ end
474
+
475
+ private
476
+ def validate
477
+ raise "ZDate says: invalid date" if !valid
478
+ end
479
+
480
+ def valid
481
+ # It is important that valid_day is last because we have to do the days_in_month calculation!!!
482
+ @date.length == 8 && @date !~ /\D/ && valid_year && valid_month && valid_day
483
+ end
484
+
485
+ def valid_year
486
+ year >= 1900
487
+ end
488
+
489
+ def valid_month
490
+ month >= 1 and month <= 12
491
+ end
492
+
493
+ def valid_day
494
+ day >= 1 and day <= days_in_month
495
+ end
496
+
497
+ def get_day_or_max_day_in_month(day, month, year)
498
+ # if day exists in month/year then use it, if it is not then use the last day of the month
499
+ dm = ZDate.days_in_month(month, year)
500
+ dm >= day ? day : dm
501
+ end
502
+ end
503
+ end