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