-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathGoatLib.pm
149 lines (114 loc) · 3.82 KB
/
GoatLib.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package GoatLib;
use strict;
use Pod::Usage;
use Carp;
use Encode;
use Exporter;
use Time::ParseDate; # This is in Debian's libtime-modules-perl package
use Date::Parse;
use Date::Language;
use DateTime; # package libdatetime-perl
use DateTime::TimeZone;
use DateTime::Locale; # package libdatetime-locale-perl
use GoatConfig;
our @ISA=qw(Exporter);
our @EXPORT=qw( stone_to_level level_to_stones parse_datestr
utc2str my_time
);
=head2 fixup_frenchisms
Fix up accents in dates, so sloppy French writers can get
away with 'fev' instead of 'fév' etc.
Also change '18h32' to '18:32'
Also change "01/02/2016" to "02/01/2016", which may be
wrong, but there is no better way. DateTime parses US way,
the French do dd/mm/yyyy.
=cut
sub fixup_frenchisms {
my %fixups = (
'fevrier' => 'février',
'aout' => 'août',
'decembre' => 'décembre',
);
my ($date) = @_;
foreach my $k (keys %fixups) {
my $v = $fixups{$k};
$date =~ s/$k/$v/; # Substitute whole words
$k = substr $k, 0, 3;
$v = substr $v, 0, 3;
$date =~ s/$k/$v/; # Substitute 3-letter abbreviations
}
$date =~ s/(\d\d)h(\d\d)/$1:$2/;
# Swap month and day if date in xx/yy/zz format
my ($d, $m, $rest) = split /\//, $date;
$date = "$m/$d/$rest" if (defined $m and defined $rest);
return $date;
}
# get date as string, and return a date
#
sub parse_datestr {
my ($date_str) = @_;
$date_str = Encode::decode("utf8", $date_str);
my $lang = Date::Language->new('French');
my $date;
eval { # str2time and others die on illegal dates: we really don't want that.
$date_str = fixup_frenchisms $date_str;
# Caveat: str2time takes a timezone in the silly 3-letter format
# ('GMT', 'EST', 'CET', ...); if it doesn't understand the timezone, it
# silently reverts to the system timezone, so the code becomes
# non-deterministic across systems.
#
# Hence: we convert text with str2time forced to UTC (so we're
# independant from system settings), then compute the offset with
# DateTime::TimeZone, which works with standard timezone names.
$date = $lang->str2time($date_str, 'UTC');
my $tz = DateTime::TimeZone->new( name => $TIMEZONE );
my $dt = DateTime->from_epoch(epoch => $date);
my $offset = $tz->offset_for_datetime($dt);
#print "datestr: str2time($date_str) => $date ; $TIMEZONE: $offset, ";
$date -= $offset;
#print "final: $date\n";
};
return $date;
}
# Renvoie une valeur numérique correspondant à un niveau en
# pierres
sub stone_to_level {
my ($niv) = @_;
return undef if not defined $niv;
$niv =~ /(\d+)([kd])/i or warn "Illegal level $niv\n" and return undef;
if (lc $2 eq 'd') {
return $1 * 100 - 50;
} else {
return - ($1 * 100 - 50);
}
}
# Converts a numeric level to a stone level (1632 => '17K')
sub level_to_stones {
my ($l) = @_;
$l = -2950 if $l < -2950; # Cap for undefined level of -9999
my $level = $l / 100;
if ($level < 0) {
$level = - POSIX::floor($level);
$level .= 'k';
} else {
$level = POSIX::ceil($level);
$level .= 'd';
}
return $level;
}
# Turn a UTC time_t into a localised timezoned string
# ts: time_t
# format: strftime format, with sane default if undef
sub utc2str {
my ($ts, $format) = @_;
croak("***utc2str called with empty time stamp") unless $ts;
my $loc = DateTime::Locale->load($LOCALE);
my $o = DateTime->from_epoch(epoch => $_[0], locale => $LOCALE, time_zone=>$TIMEZONE);
my $date = $o->strftime($format // "%A %d %B %Y %R");
}
# Equivalent to time(), except if $ENV{TEST_TIME} is defined it returns that instead.
# This allows regression testing.
sub my_time {
return $ENV{TEST_TIME} // time;
}
1;