1package Math::Calc::Units::Convert::Time; 2use base 'Math::Calc::Units::Convert::Metric'; 3use strict; 4use vars qw(%units %pref %ranges %total_unit_map); 5 6%units = ( minute => [ 60, 'sec' ], 7 hour => [ 60, 'minute' ], 8 day => [ 24, 'hour' ], 9 week => [ 7, 'day' ], 10 year => [ 365, 'day' ], # Inexact unit... ugh... 11); 12 13%pref = ( default => 1, 14 hour => 0.8, 15 day => 0.8, 16 week => 0.4, 17 minute => 0.9, 18 year => 0.9, 19); 20 21%ranges = ( default => [ 1, 300 ], 22 millisec => [ 1, 999 ], 23 sec => [ 1, 200 ], 24 minute => [ 2, 100 ], 25 hour => [ 1, 80 ], 26 day => [ 1, 500 ], 27 week => [ 1, 4 ], 28 year => [ 1, undef ], 29); 30 31sub major_pref { 32 return 2; 33} 34 35sub major_variants { 36 my ($self) = @_; 37 return grep { ($_ ne 'default') && ($_ ne 'week') } keys %ranges; 38} 39 40# Return a list of the variants of the canonical unit of time: 'sec' 41sub variants { 42 my ($self, $base) = @_; 43 return 'sec', (keys %units), map { "${_}sec" } $self->get_prefixes({ small => 1 }); 44} 45 46sub unit_map { 47 my ($self) = @_; 48 if (keys %total_unit_map == 0) { 49 %total_unit_map = (%{$self->SUPER::unit_map()}, %units); 50 } 51 return \%total_unit_map; 52} 53 54sub canonical_unit { return 'sec'; } 55 56sub abbreviated_canonical_unit { return 's'; } 57 58# demetric : string => [ mult, base ] 59# 60# Must override here to avoid megahours or milliweeks 61# 62sub demetric { 63 my ($self, $string) = @_; 64 if (my $prefix = $self->get_prefix($string)) { 65 my $tail = substr($string, length($prefix)); 66 if ($tail =~ /^sec(ond)?s?$/) { 67 return ($self->get_metric($prefix), "sec"); 68 } 69 return; # Should this fail, or assume it's a non-metric unit? 70 } else { 71 return (1, $string); 72 } 73} 74 75# simple_convert : unitName x unitName -> multiplier 76# 77# Does not allow msec (only millisec or ms) 78# 79sub simple_convert { 80 my ($self, $from, $to) = @_; 81 82 # sec, secs, second, seconds 83 $from = "sec" if $from =~ /^sec(ond)?s?$/i; 84 $from = "minute" if $from =~ /^min(ute)?s?$/i; 85 86 if (my $easy = $self->SUPER::simple_convert($from, $to)) { 87 return $easy; 88 } 89 90 # ms == millisec 91 if ($from =~ /^(.)s$/) { 92 my ($expansion) = $self->expand($1); 93 return $self->simple_convert($expansion . "sec", $to); 94 } 95 96 return; # Failed 97} 98 99############################################################################## 100 101sub preference { 102 my ($self, $v) = @_; 103 my ($val, $unit) = @$v; 104 my $base = lc(($self->demetric($unit))[1]); 105 my $pref = $pref{$base} || $pref{default}; 106 return $pref * $self->prefix_pref(substr($unit, 0, -length($base))); 107} 108 109sub get_ranges { 110 return \%ranges; 111} 112 113sub get_prefs { 114 return \%pref; 115} 116 117my @BREAKDOWN = qw(year week day hour minute sec ms us ns ps); 118sub render { 119 my ($self, $val, $name, $power, $options) = @_; 120 my $full_name = $name; 121 if ($options->{abbreviate}) { 122 if ($name =~ /(\w+)sec/) { 123 my $prefix = $1; 124 my $mabbrev = $self->metric_abbreviation($prefix); 125 $name = $mabbrev . "s" unless $mabbrev eq $prefix; 126 } 127 } 128 my $basic = $self->SUPER::render($val, $name, $power, $options); 129 return $basic if $power != 1; 130 131 $val *= $self->simple_convert($full_name, 'sec'); 132 my @spread = $self->spread($val, 'sec', $name, \@BREAKDOWN); 133 my $spread = join(" ", map { "$_->[0] $_->[1]" } @spread); 134 135 return "($basic = $spread)" if @spread > 1; 136 return $basic; 137} 138 1391; 140