Various changes
[clearscm.git] / lib / DateUtils.pm
1 =pod
2
3 =head1 NAME $RCSfile: DateUtils.pm,v $
4
5 Simple date/time utilities
6
7 =head1 VERSION
8
9 =over
10
11 =item Author
12
13 Andrew DeFaria <Andrew@ClearSCM.com>
14
15 =item Revision
16
17 $Revision: 1.32 $
18
19 =item Created
20
21 Thu Jan  5 11:06:49 PST 2006
22
23 =item Modified
24
25 $Date: 2013/02/21 05:01:17 $
26
27 =back
28
29 =head1 SYNOPSIS
30
31 Simple date and time utilities for often used date/time functionality.
32
33   my $ymd = YMD;
34   my $ymdhm = YMDHM;
35   my $timestamp = timestamp;
36
37 =head1 DESCRIPTION
38
39 Often you just want to simply and quickly get date or date and time in
40 a YMD or YMDHM format. Note the YMDHM format defined here is YMD\@H:M
41 and is not well suited for a filename. The timestamp routine returns
42 YMD_HM.
43
44 =head1 ROUTINES
45
46 The following routines are exported:
47
48 =cut
49
50 package DateUtils;
51
52 use strict;
53 use warnings;
54
55 use base 'Exporter';
56
57 use Carp;
58 use Time::Local;
59
60 use Display;
61 use Utils;
62
63 our @EXPORT = qw (
64   Add
65   Age
66   Compare
67   DateToEpoch
68   EpochToDate
69   FormatDate
70   FormatTime
71   MDY
72   SQLDatetime2UnixDatetime
73   SubtractDays
74   Today2SQLDatetime
75   UnixDatetime2SQLDatetime
76   UTCTime
77   YMD
78   YMDHM
79   YMDHMS
80   timestamp
81   ymdhms
82 );
83
84 my @months = (
85   31, # January
86   28, # February
87   31, # March
88   30, # April
89   31, # May
90   30, # June
91   31, # July
92   31, # August
93   30, # September
94   31, # October
95   30, # November
96   31  # Descember
97 );
98
99 my $SECS_IN_MIN  = 60;
100 my $SECS_IN_HOUR = $SECS_IN_MIN * 60; 
101 my $SECS_IN_DAY  = $SECS_IN_HOUR * 24;
102
103 # Forwards
104 sub Today2SQLDatetime ();
105 sub DateToEpoch ($);
106 sub EpochToDate ($);
107
108 sub ymdhms {
109   my ($time) = @_;
110
111   $time ||= time;
112
113   my (
114     $sec,
115     $min,
116     $hour,
117     $mday,
118     $mon,
119     $year,
120     $wday,
121     $yday,
122     $isdst
123   ) = localtime ($time);
124
125   # Adjust month
126   $mon++;
127
128   # Adjust year
129   $year += 1900;
130
131   # Zero preface month, day, hour and minute
132   $mon  = '0' . $mon  if $mon  < 10;
133   $mday = '0' . $mday if $mday < 10;
134   $hour = '0' . $hour if $hour < 10;
135   $min  = '0' . $min  if $min  < 10;
136   $sec  = '0' . $sec  if $sec  < 10;
137
138   return $year, $mon, $mday, $hour, $min, $sec;
139 } # ymdhms
140
141 sub julian ($$$) {
142   my ($year, $month, $day) = @_;
143
144   my $days = 0;
145   my $m    = 1;
146
147   foreach (@months) {
148     last if $m >= $month;
149     $m++;
150     $days += $_;
151   } # foreach
152
153   return $days + $day;
154 } # julian
155
156 sub _is_leap_year ($) {
157   my ($year) = @_;
158   
159   return 0 if $year % 4;
160   return 1 if $year % 100;
161   return 0 if $year % 400;
162   
163   return 1; 
164 } # _is_leap_year
165
166 sub Add ($%) {
167   my ($datetime, %parms) = @_;
168   
169 =pod
170
171 =head2 Add ($datetime, %parms)
172
173 Add to a datetime
174
175 Parameters:
176
177 =for html <blockquote>
178
179 =over
180
181 =item $datetime
182
183 Datetime in SQLDatetime format to manipulate.
184
185 =item %parms
186
187 Hash of parms. Acceptable values are of the following format:
188
189  seconds => $seconds
190  minutes => $minutes
191  hours   => $hours
192  days    => $days
193  month   => $month
194  
195 Note that month will simply increment the month number, adjusting for overflow
196 of year if appropriate. Therefore a date of 2/28/2001 would increase by 1 month
197 to yield 3/28/2001. And, unfortunately, an increase of 1 month to 1/30/2011 
198 would incorrectly yeild 2/30/2011!
199
200 =back
201
202 =for html </blockquote>
203
204 Returns:
205
206 =for html <blockquote>
207
208 =over
209
210 =item New datetime
211
212 =back
213
214 =for html </blockquote>
215
216 =cut
217
218   my @validKeys = (
219     'seconds',
220     'minutes',
221     'hours',
222     'days',
223     'months',
224   );
225   
226   foreach (keys %parms) {
227     unless (InArray ($_, @validKeys)) {
228       croak "Invalid key in DateUtils::Add: $_";
229     } # unless
230   } # foreach
231   
232   my $epochTime = DateToEpoch $datetime;
233   
234   my $amount = 0;
235   
236   $parms{seconds} ||= 0;
237   $parms{minutes} ||= 0;
238   $parms{hours}   ||= 0;
239   $parms{days}    ||= 0;
240   
241   $amount += $parms{days}    * $SECS_IN_DAY;
242   $amount += $parms{hours}   * $SECS_IN_HOUR;
243   $amount += $parms{minutes} * $SECS_IN_MIN;
244   $amount += $parms{seconds};
245     
246   $epochTime += $amount;
247
248   $datetime = EpochToDate $epochTime;
249   
250   if ($parms{month}) {
251     my $years  = $parms{month} / 12;
252     my $months = $parms{month} % 12;
253      
254     my $month = substr $datetime, 5, 2;
255     
256     $years += ($month + $months) / 12;
257     substr ($datetime, 5, 2) = ($month + $months) % 12;
258     
259     substr ($datetime, 0, 4) = substr ($datetime, 0, 4) + $years;
260   } # if
261   
262   return $datetime;
263 } # Add
264
265 sub Age ($) {
266   my ($timestamp) = @_;
267
268 =pod
269
270 =head2 Age ($timestamp)
271
272 Determines how old something is given a timestamp
273
274 Parameters:
275
276 =for html <blockquote>
277
278 =over
279
280 =item $timestamp:
281
282 Timestamp to age from (Assumed to be earlier than today)
283
284 =back
285
286 =for html </blockquote>
287
288 Returns:
289
290 =for html <blockquote>
291
292 =over
293
294 =item Number of days between $timestamp and today
295
296 =back
297
298 =for html </blockquote>
299
300 =cut
301
302   my $today      = Today2SQLDatetime;
303   my $today_year = substr $today, 0, 4;
304   my $month      = substr $today, 5, 2;
305   my $day        = substr $today, 8, 2;
306   my $today_days = julian $today_year, $month, $day;
307
308   my $timestamp_year = substr $timestamp, 0, 4;
309   $month             = substr $timestamp, 5, 2;
310   $day               = substr $timestamp, 8, 2;
311   my $timestamp_days = julian $timestamp_year, $month, $day;
312
313   if ($timestamp_year > $today_year or
314       ($timestamp_days > $today_days and $timestamp_year == $today_year)) {
315     return;
316   } else {
317     my $leap_days = 0;
318
319     for (my $i = $timestamp_year; $i < $today_year; $i++) {
320         
321       $leap_days++ if $i % 4 == 0;
322     } # for
323
324     $today_days += 365 * ($today_year - $timestamp_year) + $leap_days;
325     return $today_days - $timestamp_days;
326   } # if
327 } # Age
328
329 sub Compare ($$) {
330   my ($date1, $date2) = @_;
331   
332 =pod
333
334 =head2 Compare ($date2, $date2)
335
336 Compares two datetimes returning -1 if $date1 < $date2, 0 if equal or 1 if
337 $date1 > $date2
338
339 Parameters:
340
341 =for html <blockquote>
342
343 =over
344
345 =item $date1
346
347 Date 1 to compare
348
349 =item $date2
350
351 Date 2 to compare
352
353 =back
354
355 =for html </blockquote>
356
357 Returns:
358
359 =for html <blockquote>
360
361 =over
362
363 =item -1 if $date1 < $date2, 0 if equal or 1 if $date1 > $date2
364
365 =back
366
367 =for html </blockquote>
368
369 =cut
370
371   return DateToEpoch ($date1) <=> DateToEpoch ($date2);
372 } # Compare
373
374 sub DateToEpoch ($) {
375   my ($date) = @_;
376   
377 =pod
378
379 =head2 DateToEpoch ($datetime)
380
381 Converts a datetime to epoch
382
383 Parameters:
384
385 =for html <blockquote>
386
387 =over
388
389 =item $datetime
390
391 Datetime to convert to an epoch
392
393 =back
394
395 =for html </blockquote>
396
397 Returns:
398
399 =for html <blockquote>
400
401 =over
402
403 =item $epoch
404
405 =back
406
407 =for html </blockquote>
408
409 =cut
410
411   my $year    = substr $date,  0, 4;
412   my $month   = substr $date,  5, 2;
413   my $day     = substr $date,  8, 2;
414   my $hour    = substr $date, 11, 2;
415   my $minute  = substr $date, 14, 2;
416   my $seconds = substr $date, 17, 2;
417   
418   my $days;
419
420   for (my $i = 1970; $i < $year; $i++) {
421     $days += _is_leap_year ($i) ? 366 : 365;
422   } # for
423   
424   my @monthDays = (
425     0,
426     31, 
427     59,
428     90,
429     120,
430     151,
431     181,
432     212,
433     243,
434     273,
435     304,
436     334,
437   );
438   
439   $days += $monthDays[$month - 1];
440   
441   $days++
442     if _is_leap_year ($year) and $month > 2;
443     
444  $days += $day - 1;
445   
446   return ($days   * $SECS_IN_DAY)
447        + ($hour   * $SECS_IN_HOUR)
448        + ($minute * $SECS_IN_MIN)
449        + $seconds;
450 } # DateToEpoch
451
452 sub EpochToDate ($) {
453   my ($epoch) = @_;
454   
455 =pod
456
457 =head2 EpochToDate ($epoch)
458
459 Converts an epoch to a datetime
460
461 Parameters:
462
463 =for html <blockquote>
464
465 =over
466
467 =item $epoch
468
469 Epoch to convert to a datetime
470
471 =back
472
473 =for html </blockquote>
474
475 Returns:
476
477 =for html <blockquote>
478
479 =over
480
481 =item $datetime
482
483 =back
484
485 =for html </blockquote>
486
487 =cut
488
489   my $year = 1970;
490   my ($month, $day, $hour, $minute, $seconds);
491   my $leapYearSecs = 366 * $SECS_IN_DAY;
492   my $yearSecs     = $leapYearSecs - $SECS_IN_DAY;
493   
494   while () {
495     my $amount = _is_leap_year ($year) ? $leapYearSecs : $yearSecs;
496     
497     last
498       if $amount > $epoch;
499       
500     $epoch -= $amount;
501     $year++;
502   } # while
503   
504   my $leapYearAdjustment = _is_leap_year ($year) ? 1 : 0;
505   
506   if ($epoch >= (334 + $leapYearAdjustment) * $SECS_IN_DAY) {
507     $month = '12';
508     $epoch -= (334 + $leapYearAdjustment) * $SECS_IN_DAY;
509   } elsif ($epoch >= (304 + $leapYearAdjustment) * $SECS_IN_DAY) {
510     $month = '11';
511     $epoch -= (304 + $leapYearAdjustment) * $SECS_IN_DAY;
512   } elsif ($epoch >= (273 + $leapYearAdjustment) * $SECS_IN_DAY) {
513     $month = '10';
514     $epoch -= (273 + $leapYearAdjustment) * $SECS_IN_DAY;
515   } elsif ($epoch >= (243 + $leapYearAdjustment) * $SECS_IN_DAY) {
516     $month = '09';
517     $epoch -= (243 + $leapYearAdjustment) * $SECS_IN_DAY;
518   } elsif ($epoch >= (212 + $leapYearAdjustment) * $SECS_IN_DAY) {
519     $month = '08';
520     $epoch -= (212 + $leapYearAdjustment) * $SECS_IN_DAY;
521   } elsif ($epoch >= (181 + $leapYearAdjustment) * $SECS_IN_DAY) {
522     $month = '07';
523     $epoch -= (181 + $leapYearAdjustment) * $SECS_IN_DAY;
524   } elsif ($epoch >= (151 + $leapYearAdjustment) * $SECS_IN_DAY) {
525     $month = '06';
526     $epoch -= (151 + $leapYearAdjustment) * $SECS_IN_DAY;
527   } elsif ($epoch >= (120 + $leapYearAdjustment) * $SECS_IN_DAY) {
528     $month = '05';
529     $epoch -= (120 + $leapYearAdjustment) * $SECS_IN_DAY;
530   } elsif ($epoch >= (90 + $leapYearAdjustment) * $SECS_IN_DAY) {
531     $month = '04';
532     $epoch -= (90 + $leapYearAdjustment) * $SECS_IN_DAY;
533   } elsif ($epoch >= (59 + $leapYearAdjustment) * $SECS_IN_DAY) {
534     $month = '03';
535     $epoch -= (59 + $leapYearAdjustment) * $SECS_IN_DAY;
536   } elsif ($epoch >= 31 * $SECS_IN_DAY) {
537     $month = '02';
538     $epoch -= 31 * $SECS_IN_DAY;
539   } else {
540     $month = '01';
541   } # if
542
543   $day     = int (($epoch / $SECS_IN_DAY) + 1);
544   $epoch   = $epoch % $SECS_IN_DAY;
545   $hour    = int ($epoch / $SECS_IN_HOUR);
546   $epoch   = $epoch % $SECS_IN_HOUR;
547   $minute  = int ($epoch / $SECS_IN_MIN);
548   $seconds = $epoch % $SECS_IN_MIN;
549   
550   $day     = "0$day"     if $day     < 10;
551   $hour    = "0$hour"    if $hour    < 10;
552   $minute  = "0$minute"  if $minute  < 10;
553   $seconds = "0$seconds" if $seconds < 10;
554   
555   return "$year-$month-$day $hour:$minute:$seconds";
556 } # EpochToDate
557
558 sub UTCTime ($) {
559   my ($datetime) = @_;
560   
561 =pod
562
563 =head2 UTCTime ($epoch)
564
565 Converts an epoch to UTC Time
566
567 Parameters:
568
569 =for html <blockquote>
570
571 =over
572
573 =item $epoch
574
575 Epoch to convert to a datetime
576
577 =back
578
579 =for html </blockquote>
580
581 Returns:
582
583 =for html <blockquote>
584
585 =over
586
587 =item $datetime
588
589 =back
590
591 =for html </blockquote>
592
593 =cut
594
595   my @localtime = localtime;
596   my ($sec, $min, $hour, $mday, $mon, $year) = gmtime (
597     DateToEpoch ($datetime) - (timegm (@localtime) - timelocal (@localtime))
598   );
599       
600   $year += 1900;
601   $mon++;
602
603   $sec  = '0' . $sec  if $sec  < 10;  
604   $min  = '0' . $min  if $min  < 10;  
605   $hour = '0' . $hour if $hour < 10;  
606   $mon  = '0' . $mon  if $mon  < 10;
607   $mday = '0' . $mday if $mday < 10;
608       
609   return "$year-$mon-${mday}T$hour:$min:${sec}Z";  
610 } # UTCTime
611
612 sub UTC2Localtime ($) {
613   my ($utcdatetime) = @_;
614   
615   # If the field does not look like a UTC time then just return it.
616   return $utcdatetime unless $utcdatetime =~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/;
617
618   $utcdatetime =~ s/T/ /;
619   $utcdatetime =~ s/Z//;
620
621   my @localtime = localtime;
622
623   return EpochToDate (
624     DateToEpoch ($utcdatetime) + (timegm (@localtime) - timelocal (@localtime))
625   );
626 } # UTC2Localtime
627
628 sub FormatDate ($) {
629   my ($date) = @_;
630
631 =pod
632
633 =head2 FormatDate ($date)
634
635 Formats date
636
637 Parameters:
638
639 =for html <blockquote>
640
641 =over
642
643 =item $date:
644
645 Date in YYYYMMDD
646
647 =back
648
649 =for html </blockquote>
650
651 Returns:
652
653 =for html <blockquote>
654
655 =over
656
657 =item Returns a date in MM/DD/YYYY format
658
659 =back
660
661 =for html </blockquote>
662
663 =cut
664
665   return substr ($date, 4, 2)
666        . "/"
667        . substr ($date, 6, 2)
668        .  "/"
669        . substr ($date, 0, 4);
670 } # FormatDate
671
672 sub FormatTime ($) {
673   my ($time) = @_;
674
675 =pod
676
677 =head2 FormatTime ($time)
678
679 Formats Time
680
681 Parameters:
682
683 =for html <blockquote>
684
685 =over
686
687 =item $time:
688
689 Time in in HH:MM format (24 hour format)
690
691 =back
692
693 =for html </blockquote>
694
695 Returns:
696
697 =for html <blockquote>
698
699 =over
700
701 =item Time in HH:MM [Am|Pm] format
702
703 =back
704
705 =for html </blockquote>
706
707 =cut
708
709   my $hours   = substr $time, 0, 2;
710   my $minutes = substr $time, 3, 2;
711   my $AmPm    = $hours > 12 ? "Pm" : "Am";
712
713   $hours = $hours - 12 if $hours > 12;
714   
715   $hours = "0$hours" if length $hours == 1;
716
717   return "$hours:$minutes $AmPm";
718 } # FormatTime
719
720 sub MDY (;$) {
721   my ($time) = @_;
722
723 =pod
724
725 =head2 MDY ($time)
726
727 Returns MM/DD/YYYY for $time
728
729 Parameters:
730
731 =for html <blockquote>
732
733 =over
734
735 =item $time:
736
737 Time in Unix time format (Default: current time)
738
739 =back
740
741 =for html </blockquote>
742
743 Returns:
744
745 =for html <blockquote>
746
747 =over
748
749 =item Date in MM/DD/YYYY
750
751 =back
752
753 =for html </blockquote>
754
755 =cut
756
757   my ($year, $mon, $mday) = ymdhms $time;
758
759   return "$mon/$mday/$year";
760 } # MDY
761
762 sub SQLDatetime2UnixDatetime ($) {
763   my ($sqldatetime) = @_;
764
765 =pod
766
767 =head2 SQLDatetime2UnixDatetime ($sqldatetime)
768
769 Converts an SQL formatted date to a Unix (localtime) formatted date)
770
771 Parameters:
772
773 =for html <blockquote>
774
775 =over
776
777 =item $sqldatetime:
778
779 Date and time stamp in SQL format
780
781 =back
782
783 =for html </blockquote>
784
785 Returns:
786
787 =for html <blockquote>
788
789 =over
790
791 =item Returns a Unix formated date and time (a la localtime)
792
793 =back
794
795 =for html </blockquote>
796
797 =cut
798
799   my %months = (
800     "01" => "Jan",
801     "02" => "Feb",
802     "03" => "Mar",
803     "04" => "Apr",
804     "05" => "May",
805     "06" => "Jun",
806     "07" => "Jul",
807     "08" => "Aug",
808     "09" => "Sep",
809     "10" => "Oct",
810     "11" => "Nov",
811     "12" => "Dec"
812   );
813
814   my $year  = substr $sqldatetime, 0, 4;
815   my $month = substr $sqldatetime, 5, 2;
816   my $day   = substr $sqldatetime, 8, 2;
817   my $time  = FormatTime (substr $sqldatetime, 11);
818
819   return $months{$month} . " $day, $year \@ $time";
820 } # SQLDatetime2UnixDatetime
821
822 sub SubtractDays ($$) {
823   my ($timestamp, $nbr_of_days) = @_;
824
825 =pod
826
827 =head2 SubtractDays ($timestamp, $nbr_of_days)
828
829 Subtracts $nbr_of_days from $timestamp
830
831 Parameters:
832
833 =for html <blockquote>
834
835 =over
836
837 =item $timestamp:
838
839 Timestamp to subtract days from
840
841 =back
842
843 =over
844
845 =item $nbr_of_days:
846
847 =back
848
849 Number of days to subtract from $timestamp
850
851 =for html </blockquote>
852
853 Returns:
854
855 =for html <blockquote>
856
857 =over
858
859 =item SQL format date $nbr_of_days ago
860
861 =back
862
863 =for html </blockquote>
864
865 =cut
866
867   my $year  = substr $timestamp, 0, 4;
868   my $month = substr $timestamp, 5, 2;
869   my $day   = substr $timestamp, 8, 2;
870
871   # Convert to Julian
872   my $days = julian $year, $month, $day;
873
874   # Subtract $nbr_of_days
875   $days -= $nbr_of_days;
876
877   # Compute $days_in_year
878   my $days_in_year;
879
880   # Adjust if crossing year boundary
881   if ($days <= 0) {
882     $year--;
883     $days_in_year = (($year % 4) == 0) ? 366 : 365;
884     $days = $days_in_year + $days;
885   } else {
886     $days_in_year = (($year % 4) == 0) ? 366 : 365;
887   } # if
888
889   # Convert back
890   $month = 0;
891
892   while ($days > 28) {
893     # If remaining days is less than the current month then last
894     last if ($days <= $months[$month]);
895
896     # Subtract off the number of days in this month
897     $days -= $months[$month++];
898   } # while
899
900   # Prefix month with 0 if necessary
901   $month++;
902   if ($month < 10) {
903     $month = "0" . $month;
904   } # if
905
906   # Prefix days with  0 if necessary
907   if ($days == 0) {
908     $days = "01";
909   } elsif ($days < 10) {
910     $days = "0" . $days;
911   } # if
912
913   return $year . "-" . $month . "-" . $days . substr $timestamp, 10;
914 } # SubtractDays
915
916 sub Today2SQLDatetime () {
917
918 =pod
919
920 =head2 Today2SQLDatetime ($datetime)
921
922 Returns today's date in an SQL format
923
924 Parameters:
925
926 =for html <blockquote>
927
928 =over
929
930 =item None
931
932 =back
933
934 =for html </blockquote>
935
936 Returns:
937
938 =for html <blockquote>
939
940 =over
941
942 =item SQL formated time stamp for today
943
944 =back
945
946 =for html </blockquote>
947
948 =cut
949
950   return UnixDatetime2SQLDatetime (scalar (localtime));
951 } # Today2SQLDatetime
952
953 sub UnixDatetime2SQLDatetime ($) {
954   my ($datetime) = @_;
955
956 =pod
957
958 =head2 UnixDatetime2SQLDatetime ($datetime)
959
960 Converts a Unix (localtime) date/time stamp to an SQL formatted
961 date/time stamp
962
963 Parameters:
964
965 =for html <blockquote>
966
967 =over
968
969 =item $datetime:
970
971 Unix formated date time stamp
972
973 =back
974
975 =for html </blockquote>
976
977 Returns:
978
979 =for html <blockquote>
980
981 =over
982
983 =item SQL formated time stamp
984
985 =back
986
987 =for html </blockquote>
988
989 =cut
990
991   my $orig_datetime = $datetime;
992   my %months = (
993     Jan => '01',
994     Feb => '02',
995     Mar => '03',
996     Apr => '04',
997     May => '05',
998     Jun => '06',
999     Jul => '07',
1000     Aug => '08',
1001     Sep => '09',
1002     Oct => '10',
1003     Nov => '11',
1004     Dec => '12',
1005   );
1006
1007   # Some mailers neglect to put the leading day of the week field in.
1008   # Check for this and compensate.
1009   my $dow = substr $datetime, 0, 3;
1010
1011   if ($dow ne 'Mon' and
1012       $dow ne 'Tue' and
1013       $dow ne 'Wed' and
1014       $dow ne 'Thu' and
1015       $dow ne 'Fri' and
1016       $dow ne 'Sat' and
1017       $dow ne 'Sun') {
1018     $datetime = 'XXX, ' . $datetime;
1019   } # if
1020
1021   # Some mailers have day before month. We need to correct this
1022   my $day = substr $datetime, 5, 2;
1023
1024   if ($day =~ /\d /) {
1025     $day      = '0' . (substr $day, 0, 1);
1026     $datetime = (substr $datetime, 0, 5) . $day . (substr $datetime, 6);
1027   } # if
1028
1029   if ($day !~ /\d\d/) {
1030     $day = substr $datetime, 8, 2;
1031   } # if
1032
1033   # Check for 1 digit date
1034   if ((substr $day, 0, 1) eq ' ') {
1035     $day      = '0' . (substr $day, 1, 1);
1036     $datetime = (substr $datetime, 0, 8) . $day . (substr $datetime, 10);
1037   } elsif ((substr $day, 1, 1) eq ' ') {
1038     $day      = '0' . (substr $day, 0, 1);
1039     $datetime = (substr $datetime, 0, 8) . $day . (substr $datetime, 9);
1040   } # if
1041
1042   my $year = substr $datetime, 20, 4;
1043
1044   if ($year !~ /\d\d\d\d/) {
1045     $year = substr $datetime, 12, 4;
1046     if ($year !~ /\d\d\d\d/) {
1047       $year = substr $datetime, 12, 2;
1048     } #if
1049   } # if
1050
1051   # Check for 2 digit year. Argh!
1052   if (length $year == 2 or (substr $year, 2, 1) eq ' ') {
1053     $year     = '20' . (substr $year, 0, 2);
1054     $datetime = (substr $datetime, 0, 12) . '20' . (substr $datetime, 12);
1055   } # if
1056
1057   my $month_name = substr $datetime, 4, 3;
1058
1059   unless ($months{$month_name}) {
1060     $month_name = substr $datetime, 8, 3;
1061   } # unless
1062   
1063   my $month = $months{$month_name};
1064   my $time  = substr $datetime, 11, 8;
1065
1066   if ($time !~ /\d\d:\d\d:\d\d/) {
1067     $time = substr $datetime, 17, 8
1068   } # if
1069
1070   unless ($year) {
1071     warning "Year undefined for $orig_datetime\nReturning today's date";
1072     return Today2SQLDatetime;
1073   } # unless
1074     
1075   unless ($month) {
1076     warning "Month undefined for $orig_datetime\nReturning today's date";
1077     return Today2SQLDatetime;
1078   } # unless
1079   
1080   unless ($day) {
1081     warning "Day undefined for $orig_datetime\nReturning today's date";
1082     return Today2SQLDatetime;
1083   } # unless
1084
1085   unless ($time) {
1086     warning "Time undefined for $orig_datetime\nReturning today's date";
1087     return Today2SQLDatetime;
1088   } # unless
1089
1090   return "$year-$month-$day $time";
1091 } # UnixDatetime2SQLDatetime
1092
1093 sub YMD (;$) {
1094   my ($time) = @_;
1095
1096 =pod
1097
1098 =head2 YMD ($time)
1099
1100 Returns the YMD in a format of YYYYMMDD
1101
1102 Parameters:
1103
1104 =for html <blockquote>
1105
1106 =over
1107
1108 =item $time:
1109
1110 Time to convert to YYYYMMDD (Default: Current time)
1111
1112 =back
1113
1114 =for html </blockquote>
1115
1116 Returns:
1117
1118 =for html <blockquote>
1119
1120 =over
1121
1122 =item Date in YYYYMMDD format
1123
1124 =back
1125
1126 =for html </blockquote>
1127
1128 =cut
1129
1130   my ($year, $mon, $mday) = ymdhms $time;
1131
1132   return "$year$mon$mday";
1133 } # YMD
1134
1135 sub YMDHM (;$) {
1136   my ($time) = @_;
1137
1138 =pod
1139
1140 =head2 YMDHM ($time)
1141
1142 Returns the YMD in a format of YYYYMMDD@HH:MM
1143
1144 Parameters:
1145
1146 =for html <blockquote>
1147
1148 =over
1149
1150 =item $time:
1151
1152 Time to convert to YYYYMMDD@HH:MM (Default: Current time)
1153
1154 =back
1155
1156 =for html </blockquote>
1157
1158 Returns:
1159
1160 =for html <blockquote>
1161
1162 =over
1163
1164 =item Date in YYYYMMDD@HH:MM format
1165
1166 =back
1167
1168 =for html </blockquote>
1169
1170 =cut
1171
1172   my ($year, $mon, $mday, $hour, $min) = ymdhms $time;
1173
1174   return "$year$mon$mday\@$hour:$min";
1175 } # YMDHM
1176
1177 sub YMDHMS (;$) {
1178   my ($time) = @_;
1179
1180 =pod
1181
1182 =head2 YMDHMS ($time)
1183
1184 Returns the YMD in a format of YYYYMMDD@HH:MM:SS
1185
1186 Parameters:
1187
1188 =for html <blockquote>
1189
1190 =over
1191
1192 =item $time:
1193
1194 Time to convert to YYYYMMDD@HH:MM:SS (Default: Current time)
1195
1196 =back
1197
1198 =for html </blockquote>
1199
1200 Returns:
1201
1202 =for html <blockquote>
1203
1204 =over
1205
1206 =item Date in YYYYMMDD@HH:MM:SS format
1207
1208 =back
1209
1210 =for html </blockquote>
1211
1212 =cut
1213
1214   my ($year, $mon, $mday, $hour, $min, $sec) = ymdhms $time;
1215
1216   return "$year$mon$mday\@$hour:$min:$sec";
1217 } # YMDHMS
1218
1219 sub timestamp (;$) {
1220   my ($time) = @_;
1221
1222 =pod
1223
1224 =head2 timestamp ($time)
1225
1226 Returns the YMD in a format of YYYYMMDD_HHMM
1227
1228 Parameters:
1229
1230 =for html <blockquote>
1231
1232 =over
1233
1234 =item $time:
1235
1236 Time to convert to YYYYMMDD_HHMMSS (Default: Current time)
1237
1238 =for html </blockquote>
1239
1240 Returns:
1241
1242 =for html <blockquote>
1243
1244 =over
1245
1246 =item Date in YYYYMMDD_HHMMSS format
1247
1248 =back
1249
1250 =for html </blockquote>
1251
1252 =cut
1253
1254   my ($year, $mon, $mday, $hour, $min, $sec) = ymdhms $time;
1255
1256   return "$year$mon${mday}_$hour$min$sec";
1257 } # timestamp
1258
1259 1;
1260
1261 =head2 DEPENDENCIES
1262
1263 =head3 Perl Modules
1264
1265 =for html <p><a href="/php/scm_man.php?file=lib/Display.pm">Display</a></p>
1266
1267 =head1 INCOMPATABILITIES
1268
1269 None yet...
1270
1271 =head1 BUGS AND LIMITATIONS
1272
1273 There are no known bugs in this module.
1274
1275 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
1276
1277 =head1 LICENSE AND COPYRIGHT
1278
1279 This Perl Module is freely available; you can redistribute it and/or
1280 modify it under the terms of the GNU General Public License as
1281 published by the Free Software Foundation; either version 2 of the
1282 License, or (at your option) any later version.
1283
1284 This Perl Module is distributed in the hope that it will be useful,
1285 but WITHOUT ANY WARRANTY; without even the implied warranty of
1286 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1287 General Public License (L<http://www.gnu.org/copyleft/gpl.html>) for more
1288 details.
1289
1290 You should have received a copy of the GNU General Public License
1291 along with this Perl Module; if not, write to the Free Software Foundation,
1292 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1293 reserved.
1294
1295 =cut