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