datet 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/datet.rb ADDED
@@ -0,0 +1,1102 @@
1
+ #This class handels various time- and date-specific behaviour in a friendly way.
2
+ #===Examples
3
+ # datet = Datet.new #=> 2012-05-03 20:35:16 +0200
4
+ # datet = Datet.new(Time.now) #=> 2012-05-03 20:35:16 +0200
5
+ # datet.months + 5 #=> 2012-10-03 20:35:16 +0200
6
+ # datet.days + 64 #=> 2012-12-06 20:35:16 +010
7
+ class Datet
8
+ @@months_lcase = {
9
+ "jan" => 1,
10
+ "january" => 1,
11
+ "feb" => 2,
12
+ "february" => 2,
13
+ "mar" => 3,
14
+ "march" => 3,
15
+ "apr" => 4,
16
+ "april" => 4,
17
+ "may" => 5,
18
+ "jun" => 6,
19
+ "june" => 6,
20
+ "jul" => 7,
21
+ "july" => 7,
22
+ "aug" => 8,
23
+ "august" => 8,
24
+ "sep" => 9,
25
+ "september" => 9,
26
+ "oct" => 10,
27
+ "october" => 11,
28
+ "nov" => 11,
29
+ "november" => 11,
30
+ "dec" => 12,
31
+ "december" => 12
32
+ }
33
+
34
+ #Initializes the object. Default is the current time. A time-object can be given.
35
+ def initialize(time = Time.now, *args)
36
+ if time.is_a?(Time)
37
+ self.update_from_time(time)
38
+ else
39
+ begin
40
+ time = Time.new(*([time] | args))
41
+ self.update_from_time(time)
42
+ rescue ArgumentError => e
43
+ days_left = 0
44
+ months_left = 0
45
+ hours_left = 0
46
+ mins_left = 0
47
+ secs_left = 0
48
+ usecs_left = 0
49
+
50
+ #Check larger month the allowed.
51
+ if args[0] and args[0] > 12
52
+ months_left = args[0] - 12
53
+ args[0] = 12
54
+ end
55
+
56
+ #Check larger date than allowed.
57
+ datet = Datet.new(time, args[0], 1)
58
+ dim = datet.days_in_month
59
+
60
+ if args[1] and args[1] > dim
61
+ days_left = args[1] - dim
62
+ args[1] = dim if days_left > 0
63
+ end
64
+
65
+ #Check larger hour than allowed.
66
+ if args[2] and args[2] >= 24
67
+ hours_left = args[2] + 1
68
+ args[2] = 0
69
+ end
70
+
71
+ #Check larger minute than allowed.
72
+ if args[3] and args[3] >= 60
73
+ mins_left = args[3] + 1
74
+ args[3] = 0
75
+ end
76
+
77
+ #Check larger secs than allowed.
78
+ if args[4] and args[4] >= 60
79
+ secs_left = args[4] + 1
80
+ args[4] = 0
81
+ end
82
+
83
+ #Check larger usecs than allowed.
84
+ if args[5] and args[5] >= 60
85
+ usecs_left = args[5] + 1
86
+ args[5] = 0
87
+ end
88
+
89
+ #Generate new stamp.
90
+ time = Time.new(*([time] | args))
91
+ self.update_from_time(time)
92
+
93
+ self.mins + mins_left if mins_left > 0
94
+ self.hours + hours_left if hours_left > 0
95
+ self.days + days_left if days_left > 0
96
+ self.months + months_left if months_left > 0
97
+ self.secs + secs_left if secs_left > 0
98
+ self.usecs + usecs_left if usecs_left > 0
99
+ end
100
+ end
101
+ end
102
+
103
+ #Updates the current variables to the given time.
104
+ #===Examples
105
+ # datet.update_from_time(Time.now)
106
+ def update_from_time(time)
107
+ @t_year = time.year
108
+ @t_month = time.month
109
+ @t_day = time.day
110
+ @t_hour = time.hour
111
+ @t_min = time.min
112
+ @t_sec = time.sec
113
+ @t_usec = time.usec
114
+
115
+ nil
116
+ end
117
+
118
+ #Returns a new Time-object based on the data of the Datet-object.
119
+ def time
120
+ return Time.new(@t_year, @t_month, @t_day, @t_hour, @t_min, @t_sec)
121
+ end
122
+
123
+ #Goes forward day-by-day and stops at a date matching the criteria given.
124
+ #
125
+ #===Examples
126
+ # datet.time #=> 2012-05-03 19:36:08 +0200
127
+ #
128
+ #Try to find next saturday.
129
+ # datet.find(:day, :day_in_week => 5) #=> 2012-05-05 19:36:08 +0200
130
+ #
131
+ #Try to find next wednesday by Time's wday-method.
132
+ # datet.find(:day, :wday => 3) #=> 2012-05-09 19:36:08 +0200
133
+ def find(incr, args)
134
+ count = 0
135
+ while true
136
+ if args[:day_in_week] and self.day_in_week == args[:day_in_week]
137
+ return self
138
+ elsif args[:wday] and self.time.wday == args[:wday].to_i
139
+ return self
140
+ end
141
+
142
+ if incr == :day
143
+ self.add_days(1)
144
+ elsif incr == :month
145
+ self.add_months(1)
146
+ else
147
+ raise "Invalid increment: #{incr}."
148
+ end
149
+
150
+ count += 1
151
+ raise "Endless loop?" if count > 999
152
+ end
153
+ end
154
+
155
+ #Add a given amount of seconds to the object.
156
+ def add_usecs(usecs = 1)
157
+ usecs = usecs.to_i
158
+ cur_usecs = @t_usec
159
+ next_usec = cur_usecs + usecs
160
+
161
+ if next_usec >= 60
162
+ @t_usec = 0
163
+ self.add_secs(1)
164
+ usecs_left = (usecs - 1) - (60 - cur_usecs)
165
+ self.add_usecs(usecs_left) if usecs_left > 0
166
+ elsif next_usec < 0
167
+ @t_usec = 59
168
+ self.add_secs(-1)
169
+ usecs_left = usecs + cur_usecs + 1
170
+ self.add_usecs(usecs_left) if usecs_left > 0
171
+ else
172
+ time = self.stamp(:datet => false, :usec => next_usec)
173
+ end
174
+
175
+ return self
176
+ end
177
+
178
+ #Add a given amount of seconds to the object.
179
+ def add_secs(secs = 1)
180
+ secs = secs.to_i
181
+ cur_secs = @t_sec
182
+ next_sec = cur_secs + secs
183
+
184
+ if next_sec >= 60
185
+ @t_sec = 0
186
+ self.add_mins(1)
187
+ secs_left = (secs - 1) - (60 - cur_secs)
188
+ return self.add_secs(secs_left) if secs_left > 0
189
+ elsif next_sec < 0
190
+ @t_sec = 59
191
+ self.add_mins(-1)
192
+ secs_left = secs + cur_secs + 1
193
+ self.add_secs(secs_left) if secs_left > 0
194
+ else
195
+ @t_sec = next_sec
196
+ end
197
+
198
+ return self
199
+ end
200
+
201
+ #Add a given amount of minutes to the object.
202
+ #===Examples
203
+ # datet = Datet.new #=> 2012-05-03 17:39:45 +0200
204
+ # datet.add_mins(30)
205
+ # datet.time #=> 2012-05-03 18:08:45 +0200
206
+ def add_mins(mins = 1)
207
+ mins = mins.to_i
208
+ cur_mins = @t_min
209
+ next_min = cur_mins + mins
210
+
211
+ if next_min >= 60
212
+ @t_min = 0
213
+ self.add_hours(1)
214
+ mins_left = (mins - 1) - (60 - cur_mins)
215
+ self.add_mins(mins_left) if mins_left > 0
216
+ elsif next_min < 0
217
+ @t_min = 59
218
+ self.add_hours(-1)
219
+ mins_left = mins + cur_mins + 1
220
+ self.add_mins(mins_left) if mins_left > 0
221
+ else
222
+ @t_min = next_min
223
+ end
224
+
225
+ return self
226
+ end
227
+
228
+ #Adds a given amount of hours to the object.
229
+ #===Examples
230
+ # datet = Datet.new
231
+ # datet.add_hours(2)
232
+ def add_hours(hours = 1)
233
+ hours = hours.to_i
234
+ cur_hour = @t_hour
235
+ next_hour = cur_hour + hours
236
+
237
+ if next_hour >= 24
238
+ @t_hour = 0
239
+ self.add_days(1)
240
+ hours_left = (hours - 1) - (24 - cur_hour)
241
+ self.add_hours(hours_left) if hours_left > 0
242
+ elsif next_hour < 0
243
+ @t_hour = 23
244
+ .add_days(-1)
245
+ hours_left = hours + cur_hour + 1
246
+ self.add_hours(hours_left) if hours_left < 0
247
+ else
248
+ @t_hour = next_hour
249
+ end
250
+
251
+ return self
252
+ end
253
+
254
+ #Adds a given amount of days to the object.
255
+ #===Examples
256
+ # datet = Datet.new #=> 2012-05-03 17:42:27 +0200
257
+ # datet.add_days(29)
258
+ # datet.time #=> 2012-06-01 17:42:27 +0200
259
+ def add_days(days = 1)
260
+ days = days.to_i
261
+ return self if days == 0
262
+ dim = self.days_in_month
263
+ cur_day = @t_day
264
+ next_day = cur_day + days
265
+
266
+ if next_day > dim
267
+ @t_day = 1
268
+ self.add_months(1)
269
+ days_left = (days - 1) - (dim - cur_day)
270
+ self.add_days(days_left) if days_left > 0
271
+ elsif next_day <= 0
272
+ self.date = 1
273
+ self.add_months(-1)
274
+
275
+ @t_day = self.days_in_month
276
+ days_left = days + 1
277
+ self.add_days(days_left) if days_left != 0
278
+ else
279
+ @t_day = next_day
280
+ end
281
+
282
+ return self
283
+ end
284
+
285
+ #Adds a given amount of months to the object.
286
+ #===Examples
287
+ # datet.time #=> 2012-06-01 17:42:27 +0200
288
+ # datet.add_months(2)
289
+ # datet.time #=> 2012-08-01 17:42:27 +0200
290
+ def add_months(months = 1)
291
+ months = months.to_i
292
+ cur_month = @t_month
293
+ cur_day = @t_day
294
+ next_month = cur_month + months.to_i
295
+
296
+ if next_month > 12
297
+ @t_month = 1
298
+ @t_day = 1
299
+ self.add_years(1)
300
+ months_left = (months - 1) - (12 - cur_month)
301
+ self.add_months(months_left) if months_left > 0
302
+ elsif next_month < 1
303
+ @t_month = 12
304
+ self.add_years(-1)
305
+ else
306
+ @t_month = next_month
307
+ @t_day = 1
308
+ end
309
+
310
+ dim = self.days_in_month
311
+
312
+ if dim < cur_day
313
+ @t_day = dim
314
+ else
315
+ @t_day = cur_day
316
+ end
317
+
318
+ return self
319
+ end
320
+
321
+ #Adds a given amount of years to the object.
322
+ #===Examples
323
+ # datet.time #=> 2012-08-01 17:42:27 +0200
324
+ # datet.add_years(3)
325
+ # datet.time #> 2014-08-01 17:42:27 +0200
326
+ def add_years(years = 1)
327
+ next_year = @t_year + years.to_i
328
+ @t_year = next_year
329
+ return self
330
+ end
331
+
332
+ #Is a year a leap year in the Gregorian calendar? Copied from Date-class.
333
+ #===Examples
334
+ # if Datet.gregorian_leap?(2005)
335
+ # print "2005 is a gregorian-leap year."
336
+ # else
337
+ # print "2005 is not a gregorian-leap year."
338
+ # end
339
+ def self.gregorian_leap?(y)
340
+ if Date.respond_to?(:gregorian_leap?)
341
+ return Date.gregorian_leap?(y)
342
+ elsif y % 4 == 0 && y % 100 != 0
343
+ return true
344
+ elsif y % 400 == 0
345
+ return true
346
+ else
347
+ return false
348
+ end
349
+ end
350
+
351
+ #Returns the number of days in the month.
352
+ #===Examples
353
+ # datet = Datet.new
354
+ # print "There are #{datet.days_in_month} days in the current month."
355
+ def days_in_month
356
+ return 29 if month == 2 and Datet.gregorian_leap?(self.year)
357
+
358
+ #Thanks to ActiveSupport: http://rubydoc.info/docs/rails/2.3.8/ActiveSupport/CoreExtensions/Time/Calculations
359
+ days_in_months = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
360
+ return days_in_months[@t_month]
361
+ end
362
+
363
+ #Returns the day in the week. Monday being 1 and sunday being 6.
364
+ def day_in_week
365
+ diw = self.time.strftime("%w").to_i
366
+ if diw == 0
367
+ diw = 6
368
+ else
369
+ diw -= 1
370
+ end
371
+
372
+ return diw
373
+ end
374
+
375
+ #Returns the days name as a string.
376
+ def day_name
377
+ return self.time.strftime("%A")
378
+ end
379
+
380
+ #Returns the months name as a string.
381
+ def month_name
382
+ return self.time.strftime("%B")
383
+ end
384
+
385
+ #Returns the year as an integer.
386
+ def year
387
+ return @t_year
388
+ end
389
+
390
+ #Returns the hour as an integer.
391
+ def hour
392
+ return @t_hour
393
+ end
394
+
395
+ #Returns the minute as an integer.
396
+ def min
397
+ return @t_min
398
+ end
399
+
400
+ #Returns the seconds as an integer.
401
+ def sec
402
+ return @t_sec
403
+ end
404
+
405
+ #Returns the microsecond as an integer.
406
+ def usec
407
+ return @t_usec
408
+ end
409
+
410
+ #Changes the year to the given year.
411
+ # datet = Datet.now #=> 2014-05-03 17:46:11 +0200
412
+ # datet.year = 2005
413
+ # datet.time #=> 2005-05-03 17:46:11 +0200
414
+ def year=(newyear)
415
+ @t_year = newyear.to_i
416
+ end
417
+
418
+ #Returns the month as an integer.
419
+ def month
420
+ Thread.current[:datet_mode] = :months
421
+ return @t_month
422
+ end
423
+
424
+ #Returns the day in month as an integer.
425
+ def date
426
+ Thread.current[:datet_mode] = :days
427
+ return @t_day
428
+ end
429
+
430
+ #Returns the weekday of the week as an integer. Monday being the first and sunday being the last.
431
+ def wday_mon
432
+ wday = self.time.wday
433
+ return 0 if wday == 6
434
+ return wday - 1
435
+ end
436
+
437
+ #Changes the date to a given date.
438
+ #===Examples
439
+ # datet.time #=> 2005-05-03 17:46:11 +0200
440
+ # datet.date = 8
441
+ # datet.time #=> 2005-05-08 17:46:11 +0200
442
+ def date=(newday)
443
+ newday = newday.to_i
444
+
445
+ if newday <= 0
446
+ self.add_days(newday - 1)
447
+ else
448
+ @t_day = newday
449
+ end
450
+
451
+ return self
452
+ end
453
+
454
+ #Changes the hour to a given new hour.
455
+ #===Examples
456
+ # datet.time #=> 2012-05-09 19:36:08 +0200
457
+ # datet.hour = 5
458
+ # datet.time #=> 2012-05-09 05:36:08 +0200
459
+ def hour=(newhour)
460
+ newhour = newhour.to_i
461
+ day = @t_day
462
+
463
+ loop do
464
+ break if newhour >= 0
465
+ day += -1
466
+ newhour += 24
467
+ end
468
+
469
+ loop do
470
+ break if newhour < 24
471
+ day += 1
472
+ newhour += -24
473
+ end
474
+
475
+ @t_hour = newhour
476
+
477
+ self.date = day if day != @t_day
478
+ return self
479
+ end
480
+
481
+ #Changes the minute to a given new minute.
482
+ #===Examples
483
+ # datet.time #=> 2012-05-09 05:36:08 +0200
484
+ # datet.min = 35
485
+ # datet.time #=> 2012-05-09 05:35:08 +0200
486
+ def min=(newmin)
487
+ @t_min = newmin.to_i
488
+ end
489
+
490
+ #Changes the second to a given new second.
491
+ #===Examples
492
+ # datet.time #=> 2012-05-09 05:35:08 +0200
493
+ # datet.sec = 20
494
+ # datet.time #=> 2012-05-09 05:35:20 +0200
495
+ def sec=(newsec)
496
+ @t_sec = newsec.to_i
497
+ end
498
+
499
+ alias :day :date
500
+
501
+ #Changes the month to a given new month.
502
+ #===Examples
503
+ # datet.time #=> 2012-05-09 05:35:20 +0200
504
+ # datet.month = 7
505
+ # datet.time #=> 2012-07-09 05:35:20 +0200
506
+ def month=(newmonth)
507
+ @t_month = newmonth
508
+ end
509
+
510
+ #Turns the given argument into a new Time-object.
511
+ #===Examples
512
+ # time = Datet.arg_to_time(datet) #=> <Time>-object
513
+ # time = Datet.arg_to_time(Time.now) #=> <Time>-object
514
+ def self.arg_to_time(datet)
515
+ if datet.is_a?(Datet)
516
+ return datet.time
517
+ elsif datet.is_a?(Time)
518
+ return datet
519
+ else
520
+ raise "Could not handle object of class: '#{datet.class.name}'."
521
+ end
522
+ end
523
+
524
+ include Comparable
525
+ def <=>(timeobj)
526
+ secs = Datet.arg_to_time(timeobj).to_i
527
+
528
+ if secs > self.to_i
529
+ return -1
530
+ elsif secs < self.to_i
531
+ return 1
532
+ else
533
+ return 0
534
+ end
535
+ end
536
+
537
+ #This method is used for adding values to the object based on the current set mode.
538
+ #===Examples
539
+ #Add two months to the datet.
540
+ # datet.months
541
+ # datet.add_something(2)
542
+ def add_something(val)
543
+ val = -val if Thread.current[:datet_addmode] == "-"
544
+ return self.add_years(val) if Thread.current[:datet_mode] == :years
545
+ return self.add_hours(val) if Thread.current[:datet_mode] == :hours
546
+ return self.add_days(val) if Thread.current[:datet_mode] == :days
547
+ return self.add_months(val) if Thread.current[:datet_mode] == :months
548
+ return self.add_mins(val) if Thread.current[:datet_mode] == :mins
549
+ return self.add_secs(val) if Thread.current[:datet_mode] == :secs
550
+ return self.add_usecs(val) if Thread.current[:datet_mode] == :usecs
551
+ raise "No such mode: '#{Thread.current[:datet_mode]}'."
552
+ end
553
+
554
+ #Minus something.
555
+ #===Examples
556
+ # datet.months - 5
557
+ # datet.years - 2
558
+ def -(val)
559
+ Thread.current[:datet_addmode] = "-"
560
+ self.add_something(val)
561
+ end
562
+
563
+ #Add something.
564
+ #===Examples
565
+ # datet.months + 5
566
+ # datet.months + 2
567
+ def +(val)
568
+ Thread.current[:datet_addmode] = "+"
569
+ self.add_something(val)
570
+ end
571
+
572
+ #Sets the mode to hours and gets ready to plus or minus.
573
+ #===Examples
574
+ # datet.time #=> 2005-05-08 17:46:11 +0200
575
+ # datet.hours + 5
576
+ # datet.time #=> 2005-05-08 22:46:11 +0200
577
+ def hours
578
+ Thread.current[:datet_mode] = :hours
579
+ return self
580
+ end
581
+
582
+ #Sets the mode to minutes and gets ready to plus or minus.
583
+ #===Examples
584
+ # datet.time #=> 2005-05-08 22:46:11 +0200
585
+ # datet.mins + 5
586
+ # datet.mins #=> 2005-05-08 22:51:11 +0200
587
+ def mins
588
+ Thread.current[:datet_mode] = :mins
589
+ return self
590
+ end
591
+
592
+ #Sets the mode to seconds and gets ready to plus or minus.
593
+ def secs
594
+ Thread.current[:datet_mode] = :secs
595
+ return self
596
+ end
597
+
598
+ #Sets the mode to mili-seconds and gets ready to plus or minus.
599
+ def usecs
600
+ Thread.current[:datet_mode] = :usecs
601
+ return self
602
+ end
603
+
604
+ #Sets the mode to days and gets ready to plus or minus.
605
+ #===Examples
606
+ # datet.time #=> 2005-05-08 22:51:11 +0200
607
+ # datet.days + 26
608
+ # datet.time #=> 2005-06-03 22:51:11 +0200
609
+ def days
610
+ Thread.current[:datet_mode] = :days
611
+ return self
612
+ end
613
+
614
+ #Sets the mode to months and gets ready to plus or minus.
615
+ #===Examples
616
+ # datet.time #=> 2005-06-03 22:51:11 +0200
617
+ # datet.months + 14
618
+ # datet.time #=> 2006-08-01 22:51:11 +0200
619
+ def months
620
+ Thread.current[:datet_mode] = :months
621
+ return self
622
+ end
623
+
624
+ #Sets the mode to years and gets ready to plus or minus.
625
+ #===Examples
626
+ # datet.time #=> 2006-08-01 22:51:11 +0200
627
+ # datet.years + 5
628
+ # datet.time #=> 2011-08-01 22:51:11 +0200
629
+ def years
630
+ Thread.current[:datet_mode] = :years
631
+ return self
632
+ end
633
+
634
+ #Returns a new Datet- or Time-object based on the arguments.
635
+ #===Examples
636
+ # time = datet.stamp(:datet => false, :min => 15, :day => 5) #=> 2012-07-05 05:15:20 +0200
637
+ def stamp(args)
638
+ vars = {:year => @t_year, :month => @t_month, :day => @t_day, :hour => @t_hour, :min => @t_min, :sec => @t_sec, :usec => @t_usec}
639
+
640
+ args.each do |key, value|
641
+ vars[key.to_sym] = value.to_i if key != :datet
642
+ end
643
+
644
+ time = Time.local(vars[:year], vars[:month], vars[:day], vars[:hour], vars[:min], vars[:sec], vars[:usec])
645
+
646
+ if !args.key?(:datet) or args[:datet]
647
+ return Datet.new(time)
648
+ end
649
+
650
+ return time
651
+ end
652
+
653
+ #Returns the time as a database-valid string.
654
+ #===Examples
655
+ # datet.time #=> 2011-08-01 22:51:11 +0200
656
+ # datet.dbstr #=> "2011-08-01 22:51:11"
657
+ # datet.dbstr(:time => false) #=> "2011-08-01"
658
+ def dbstr(args = {})
659
+ str = "#{"%04d" % @t_year}-#{"%02d" % @t_month}-#{"%02d" % @t_day}"
660
+
661
+ if !args.key?(:time) or args[:time]
662
+ str << " #{"%02d" % @t_hour}:#{"%02d" % @t_min}:#{"%02d" % @t_sec}"
663
+ end
664
+
665
+ return str
666
+ end
667
+
668
+ #Parses the date from a database-format.
669
+ #===Examples
670
+ # datet = Datet.from_dbstr("2011-08-01 22:51:11")
671
+ # datet.time #=> 2011-08-01 22:51:11 +0200
672
+ def self.from_dbstr(date_string)
673
+ if date_string.is_a?(Time)
674
+ return Datet.new(date_string)
675
+ elsif date_string.is_a?(Date)
676
+ return Datet.new(date_string.to_time)
677
+ end
678
+
679
+ return false if Datet.is_nullstamp?(date_string)
680
+ return Datet.new(Time.local(*Date.parse(date_string.to_s)))
681
+ end
682
+
683
+ #Alias for 'from_dbstr'.
684
+ def self.parse(str)
685
+ return Datet.from_dbstr(str)
686
+ end
687
+
688
+ #Returns true of the given stamp is a 'nullstamp'.
689
+ #===Examples
690
+ # Datet.is_nullstamp?("0000-00-00") #=> true
691
+ # Datet.is_nullstamp?("0000-00-00 00:00:00") #=> true
692
+ # Datet.is_nullstamp?("") #=> true
693
+ # Datet.is_nullstamp?("1985-06-17") #=> false
694
+ def self.is_nullstamp?(stamp)
695
+ return true if !stamp or stamp == "0000-00-00" or stamp == "0000-00-00 00:00:00" or stamp.to_s.strip == ""
696
+ return false
697
+ end
698
+
699
+ #Returns the day of the year (0-365) as an integer.
700
+ #===Examples
701
+ # Datet.new.day_of_year #=> 123
702
+ def day_of_year
703
+ return self.time.strftime("%j").to_i
704
+ end
705
+
706
+ #Returns the day as a localized string.
707
+ #===Examples
708
+ # Datet.new.day_str #=> "Monday"
709
+ # Datet.new.day_str(:short => true) #=> "Mon"
710
+ def day_str(args = nil)
711
+ ret = Datet.days_arr[self.time.strftime("%w").to_i]
712
+ if args.is_a?(Hash) and args[:short]
713
+ ret = ret.slice(0, 3)
714
+ end
715
+
716
+ return ret
717
+ end
718
+
719
+ #Returns how many days there is between the two timestamps given as an integer.
720
+ #===Examples
721
+ # d1 = Datet.new #=> 2012-05-03 18:04:12 +0200
722
+ # d2 = Datet.new #=> 2012-05-03 18:04:16 +0200
723
+ # d2.months + 5 #=> 2012-10-03 18:04:16 +0200
724
+ # Datet.days_between(d1, d2) #=> 153
725
+ def self.days_between(t1, t2)
726
+ raise "Timestamp 2 should be larger than timestamp 1." if t2 < t1
727
+
728
+ doy1 = t1.day_of_year
729
+ doy2 = t2.day_of_year
730
+
731
+ yot1 = t1.year
732
+ yot2 = t2.year
733
+
734
+ if yot1 == yot2
735
+ days_between = doy2 - doy1
736
+ return days_between
737
+ end
738
+
739
+ upto = 365 - doy1
740
+ after = doy2
741
+
742
+ return upto + after
743
+ end
744
+
745
+ #Returns a string based on the date and time.
746
+ #===Examples
747
+ # datet.out #=> "03/05 2012 - 18:04"
748
+ # datet.out(:time => false) #=> "03/05 2012"
749
+ # datet.out(:date => false) #=> "18:04"
750
+ def out(args = {})
751
+ str = ""
752
+ date_shown = false
753
+ time_shown = false
754
+
755
+ if !args.key?(:date) or args[:date]
756
+ date_shown = true
757
+ str << "#{"%02d" % @t_day}/#{"%02d" % @t_month}"
758
+
759
+ if !args.key?(:year) or args[:year]
760
+ str << " #{"%04d" % @t_year}"
761
+ end
762
+ end
763
+
764
+ if !args.key?(:time) or args[:time]
765
+ show_time = true
766
+
767
+ if args.key?(:zerotime) and !args[:zerotime]
768
+ if @t_hour == 0 and @t_min == 0
769
+ show_time = false
770
+ end
771
+ end
772
+
773
+ if show_time
774
+ time_shown = true
775
+ str << " - " if date_shown
776
+ str << "#{"%02d" % @t_hour}:#{"%02d" % @t_min}"
777
+ end
778
+ end
779
+
780
+ return str
781
+ end
782
+
783
+ #Parses various objects into Datet-objects.
784
+ #===Examples
785
+ # datet = Datet.in("1985-06-17") #=> 1985-06-17 00:00:00 +0200
786
+ # datet = Datet.in("1985-06-17 10:00:00") #=> 1985-06-17 10:00:00 +0200
787
+ # datet = Datet.in("17/06 1985 10:00") #=> 1985-06-17 10:00:00 +0200
788
+ def self.in(timestr)
789
+ if timestr.is_a?(Time)
790
+ return Datet.new(timestr)
791
+ elsif timestr.is_a?(Date)
792
+ return Datet.new(timestr.to_time)
793
+ elsif timestr.is_a?(Datet)
794
+ return timestr
795
+ end
796
+
797
+ if match = timestr.to_s.match(/^(\d+)\/(\d+) (\d+)/)
798
+ #MySQL date format
799
+ timestr = timestr.gsub(match[0], "")
800
+ date = match[1]
801
+ month = match[2]
802
+ year = match[3]
803
+
804
+ if match = timestr.match(/\s*(\d+):(\d+)/)
805
+ #MySQL datetime format
806
+ timestr = timestr.gsub(match[0], "")
807
+ hour = match[1]
808
+ minute = match[2]
809
+ end
810
+
811
+ return Datet.new(Time.local(year, month, date, hour, minute))
812
+ elsif match = timestr.to_s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/)
813
+ return Datet.new(Time.local(match[3], match[2], match[1]))
814
+ elsif match = timestr.to_s.match(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{5,6})$/)
815
+ #Datet.code format
816
+ return Datet.new(Time.local(match[1], match[2], match[3], match[4], match[5], match[6], match[7]))
817
+ elsif match = timestr.to_s.match(/^\s*(\d{4})-(\d{1,2})-(\d{1,2})(|\s+(\d{2}):(\d{2}):(\d{2})(|\.\d+)\s*)(|\s+(UTC))(|\s+(\+|\-)(\d{2})(\d{2}))$/)
818
+ #Database date format (with possibility of .0 in the end - microseconds? -knj.
819
+
820
+ if match[11] and match[13] and match[14]
821
+ if match[12] == "+" or match[12] == "-"
822
+ sign = match[12]
823
+ else
824
+ sign = "+"
825
+ end
826
+
827
+ utc_str = "#{sign}#{match[13]}:#{match[14]}"
828
+ elsif match[8]
829
+ utc_str = match[8].to_i
830
+ else
831
+ utc_str = nil
832
+ end
833
+
834
+ time = Time.local(match[1].to_i, match[2].to_i, match[3].to_i, match[5].to_i, match[6].to_i, match[7].to_i, utc_str)
835
+ return Datet.new(time)
836
+ elsif match = timestr.to_s.match(/^\s*(\d{2,4})-(\d{1,2})-(\d{1,2})(|\s+(\d{1,2}):(\d{1,2}):(\d{1,2})(:(\d{1,2})|)\s*)$/)
837
+ time = Time.local(match[1].to_i, match[2].to_i, match[3].to_i, match[5].to_i, match[6].to_i, match[7].to_i)
838
+ return Datet.new(time)
839
+ end
840
+
841
+ raise Knj::Errors::InvalidData.new("Wrong format: '#{timestr}', class: '#{timestr.class.name}'")
842
+ end
843
+
844
+ #Returns a hash with the month-no as key and month-name as value. It uses the method "_" to translate the months names. So GetText or another method has to be defined.
845
+ def self.months_arr(args = {})
846
+ ret = {
847
+ 1 => _("January"),
848
+ 2 => _("February"),
849
+ 3 => _("March"),
850
+ 4 => _("April"),
851
+ 5 => _("May"),
852
+ 6 => _("June"),
853
+ 7 => _("July"),
854
+ 8 => _("August"),
855
+ 9 => _("September"),
856
+ 10 => _("October"),
857
+ 11 => _("November"),
858
+ 12 => _("December")
859
+ }
860
+
861
+ if args["short"]
862
+ ret_short = {}
863
+ ret.each do |key, val|
864
+ ret_short[key] = val[0..2]
865
+ end
866
+
867
+ return ret_short
868
+ end
869
+
870
+ return ret
871
+ end
872
+
873
+ #Returns a hash with the day-number as value (starting with 1 for monday). It uses the method "_" to translate the months names.
874
+ def self.days_arr(args = {})
875
+ ret = {
876
+ 1 => _("Monday"),
877
+ 2 => _("Tuesday"),
878
+ 3 => _("Wednesday"),
879
+ 4 => _("Thursday"),
880
+ 5 => _("Friday"),
881
+ 6 => _("Saturday"),
882
+ 0 => _("Sunday")
883
+ }
884
+
885
+ if args["short"]
886
+ ret_short = {}
887
+ ret.each do |key, val|
888
+ ret_short[key] = val[0..2]
889
+ end
890
+
891
+ return ret_short
892
+ end
893
+
894
+ return ret
895
+ end
896
+
897
+ #Returns the month-number for a given string (starting with 1 for january).
898
+ #===Examples
899
+ # Datet.month_str_to_no("JaNuArY") #=> 1
900
+ # Datet.month_str_to_no("DECEMBER") #=> 12
901
+ # Datet.month_str_to_no("kasper") #=> <Error>-raised
902
+ def self.month_str_to_no(str)
903
+ str = str.to_s.downcase.strip
904
+ return @@months_lcase[str] if @@months_lcase.key?(str)
905
+ raise "No month to return from that string: '#{str}'."
906
+ end
907
+
908
+ def loc_wday
909
+ return _(self.time.strftime("%A"))
910
+ end
911
+
912
+ def loc_wday_small
913
+ return _(self.time.strftime("%a"))
914
+ end
915
+
916
+ def loc_month
917
+ return _(self.time.strftime("%B"))
918
+ end
919
+
920
+ def to_s
921
+ return self.time.to_s
922
+ end
923
+
924
+ #This returns a code-string that can be used to recreate the Datet-object.
925
+ #===Examples
926
+ # code = datet.code #=> "1985061710000000000"
927
+ # newdatet = Datet.in(code) #=> 1985-06-17 10:00:00 +0200
928
+ def code
929
+ return "#{"%04d" % @t_year}#{"%02d" % @t_month}#{"%02d" % @t_day}#{"%02d" % @t_hour}#{"%02d" % @t_min}#{"%02d" % @t_sec}#{"%05d" % @t_usec}"
930
+ end
931
+
932
+ #Returns the unix timestamp for this object.
933
+ #===Examples
934
+ # datet.unixt #=> 487843200
935
+ # datet.to_i #=> 487843200
936
+ def unixt
937
+ return self.time.to_i
938
+ end
939
+
940
+ alias :to_i :unixt
941
+
942
+ #Returns the HTTP-date that can be used in headers and such.
943
+ #===Examples
944
+ # datet.httpdate #=> "Mon, 17 Jun 1985 08:00:00 GMT"
945
+ def httpdate
946
+ require "time"
947
+ return self.time.httpdate
948
+ end
949
+
950
+ #Returns various information about the offset as a hash.
951
+ #===Examples
952
+ # datet.time #=> 1985-06-17 10:00:00 +0200
953
+ # datet.offset_info #=> {:sign=>"+", :hours=>2, :mins=>0, :secs=>0}
954
+ def offset_info
955
+ offset_secs = self.time.gmt_offset
956
+
957
+ offset_hours = (offset_secs.to_f / 3600.0).floor
958
+ offset_secs -= offset_hours * 3600
959
+
960
+ offset_minutes = (offset_secs.to_f / 60.0).floor
961
+ offset_secs -= offset_minutes * 60
962
+
963
+ if offset_hours > 0
964
+ sign = "+"
965
+ else
966
+ sign = ""
967
+ end
968
+
969
+ return {
970
+ :sign => sign,
971
+ :hours => offset_hours,
972
+ :mins => offset_minutes,
973
+ :secs => offset_secs
974
+ }
975
+ end
976
+
977
+ #Returns the offset as a string.
978
+ #===Examples
979
+ # datet.offset_str #=> "+0200"
980
+ def offset_str
981
+ offset_info_data = self.offset_info
982
+ return "#{offset_info_data[:sign]}#{"%02d" % offset_info_data[:hours]}#{"%02d" % offset_info_data[:mins]}"
983
+ end
984
+
985
+ #Returns 'localtime' as of 1.9 - even in 1.8 which does it different.
986
+ #===Examples
987
+ # datet.localtime_str #=> "1985-06-17 10:00:00 +0200"
988
+ def localtime_str
989
+ return "#{"%04d" % @t_year}-#{"%02d" % @t_month}-#{"%02d" % @t_day} #{"%02d" % @t_hour}:#{"%02d" % @t_min}:#{"%02d" % @t_sec} #{self.offset_str}"
990
+ end
991
+
992
+ #Returns a human readable string based on the difference from the current time and date.
993
+ #===Examples
994
+ # datet.time #=> 1985-06-17 10:00:00 +0200
995
+ # datet.ago_str #=> "27 years ago"
996
+ # datet = Datet.new #=> 2012-05-03 20:31:58 +0200
997
+ # datet.ago_str #=> "18 seconds ago"
998
+ def ago_str(args = {})
999
+ args = {
1000
+ :year_ago_str => "%s year ago",
1001
+ :years_ago_str => "%s years ago",
1002
+ :month_ago_str => "%s month ago",
1003
+ :months_ago_str => "%s months ago",
1004
+ :day_ago_str => "%s day ago",
1005
+ :days_ago_str => "%s days ago",
1006
+ :hour_ago_str => "%s hour ago",
1007
+ :hours_ago_str => "%s hours ago",
1008
+ :min_ago_str => "%s minute ago",
1009
+ :mins_ago_str => "%s minutes ago",
1010
+ :sec_ago_str => "%s second ago",
1011
+ :secs_ago_str => "%s seconds ago",
1012
+ :right_now_str => "right now"
1013
+ }.merge(args)
1014
+
1015
+ secs_ago = Time.now.to_i - self.to_i
1016
+
1017
+ mins_ago = secs_ago.to_f / 60.0
1018
+ hours_ago = mins_ago / 60.0
1019
+ days_ago = hours_ago / 24.0
1020
+ months_ago = days_ago / 30.0
1021
+ years_ago = months_ago / 12.0
1022
+
1023
+ if years_ago > 0.9 and years_ago < 1.5
1024
+ return sprintf(args[:year_ago_str], years_ago.to_i)
1025
+ elsif years_ago >= 1.5
1026
+ return sprintf(args[:years_ago_str], years_ago.to_i)
1027
+ elsif months_ago > 0.9 and months_ago < 1.5
1028
+ return sprintf(args[:month_ago_str], months_ago.to_i)
1029
+ elsif months_ago >= 1.5
1030
+ return sprintf(args[:months_ago_str], months_ago.to_i)
1031
+ elsif days_ago > 0.9 and days_ago < 1.5
1032
+ return sprintf(args[:day_ago_str], days_ago.to_i)
1033
+ elsif days_ago >= 1.5
1034
+ return sprintf(args[:days_ago_str], days_ago.to_i)
1035
+ elsif hours_ago > 0.9 and hours_ago < 1.5
1036
+ return sprintf(args[:hour_ago_str], hours_ago.to_i)
1037
+ elsif hours_ago >= 1.5
1038
+ return sprintf(args[:hours_ago_str], hours_ago.to_i)
1039
+ elsif mins_ago > 0.9 and mins_ago < 1.5
1040
+ return sprintf(args[:min_ago_str], mins_ago.to_i)
1041
+ elsif mins_ago >= 1.5
1042
+ return sprintf(args[:mins_ago_str], mins_ago.to_i)
1043
+ elsif secs_ago >= 0.1 and secs_ago < 1.5
1044
+ return sprintf(args[:sec_ago_str], secs_ago.to_i)
1045
+ elsif secs_ago >= 1.5
1046
+ return sprintf(args[:secs_ago_str], secs_ago.to_i)
1047
+ end
1048
+
1049
+ return args[:right_now_str]
1050
+ end
1051
+
1052
+ #Returns the object as a human understandable string.
1053
+ #===Examples
1054
+ # datet.time #=> 2012-05-03 20:31:58 +0200
1055
+ # datet.human_str #=> "20:31"
1056
+ def human_str(args = {})
1057
+ args = {
1058
+ :time => true,
1059
+ :number_endings => {
1060
+ 0 => "th",
1061
+ 1 => "st",
1062
+ 2 => "nd",
1063
+ 3 => "rd",
1064
+ 4 => "th",
1065
+ 5 => "th",
1066
+ 6 => "th",
1067
+ 7 => "th",
1068
+ 8 => "th",
1069
+ 9 => "th"
1070
+ }
1071
+ }.merge(args)
1072
+
1073
+ now = Time.now
1074
+
1075
+ #Generate normal string.
1076
+ date_str = ""
1077
+
1078
+ if now.day != @t_day and now.month == @t_month and now.year == @t_year
1079
+ last_digit = @t_day.to_s[-1, 1].to_i
1080
+
1081
+ if ending = args[:number_endings][last_digit]
1082
+ #ignore.
1083
+ else
1084
+ ending = "."
1085
+ end
1086
+
1087
+ date_str << "#{@t_day}#{ending} "
1088
+ elsif now.day != @t_day or now.month != @t_month or now.year != @t_year
1089
+ date_str << "#{@t_day}/#{@t_month} "
1090
+ end
1091
+
1092
+ if now.year != @t_year
1093
+ date_str << "#{@t_year} "
1094
+ end
1095
+
1096
+ if args[:time]
1097
+ date_str << "#{@t_hour}:#{"%02d" % @t_min}"
1098
+ end
1099
+
1100
+ return date_str
1101
+ end
1102
+ end