File Coverage

lib/Sisimai/DateTime.pm
Criterion Covered Total %
statement 140 146 95.8
branch 78 90 86.6
condition 50 74 67.5
subroutine 14 14 100.0
pod 3 7 42.8
total 285 331 86.1


line stmt bran cond sub pod time code
1             package Sisimai::DateTime;
2 77     77   59665 use feature ':5.10';
  77         154  
  77         5073  
3 77     77   387 use strict;
  77         129  
  77         1670  
4 77     77   382 use warnings;
  77         158  
  77         2008  
5 77     77   852 use Time::Piece;
  77         10725  
  77         420  
6              
7             sub BASE_D() { 86400 } # 1 day = 86400 sec
8             sub BASE_Y() { 365.2425 } # 1 year = 365.2425 days
9             sub BASE_L() { 29.53059 } # 1 lunar month = 29.53059 days
10             sub CONST_P() { 4 * atan2(1,1) } # PI, 3.1415926535
11             sub CONST_E() { exp(1) } # e, Napier's constant
12             sub TZ_OFFSET() { 54000 } # Max time zone offset, 54000 seconds
13              
14 77         6869 use constant MonthName => {
15             'full' => [qw|January February March April May June July August September October November December|],
16             'abbr' => [qw|Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec|],
17 77     77   17586 };
  77         130  
18 77         11277 use constant DayOfWeek => {
19             'full' => [qw|Sunday Monday Tuesday Wednesday Thursday Friday Saturday|],
20             'abbr' => [qw|Sun Mon Tue Wed Thu Fri Sat|],
21 77     77   481 };
  77         153  
22 77         160821 use constant TimeZones => {
23             # http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
24             #'ACDT' => '+1030', # Australian Central Daylight Time UTC+10:30
25             #'ACST' => '+0930', # Australian Central Standard Time UTC+09:30
26             #'ACT' => '+0800', # ASEAN Common Time UTC+08:00
27             'ADT' => '-0300', # Atlantic Daylight Time UTC-03:00
28             #'AEDT' => '+1100', # Australian Eastern Daylight Time UTC+11:00
29             #'AEST' => '+1000', # Australian Eastern Standard Time UTC+10:00
30             #'AFT' => '+0430', # Afghanistan Time UTC+04:30
31             'AKDT' => '-0800', # Alaska Daylight Time UTC-08:00
32             'AKST' => '-0900', # Alaska Standard Time UTC-09:00
33             #'AMST' => '+0500', # Armenia Summer Time UTC+05:00
34             #'AMT' => '+0400', # Armenia Time UTC+04:00
35             #'ART' => '-0300', # Argentina Time UTC+03:00
36             #'AST' => '+0300', # Arab Standard Time (Kuwait, Riyadh) UTC+03:00
37             #'AST' => '+0400', # Arabian Standard Time (Abu Dhabi, Muscat) UTC+04:00
38             #'AST' => '+0300', # Arabic Standard Time (Baghdad) UTC+03:00
39             'AST' => '-0400', # Atlantic Standard Time UTC-04:00
40             #'AWDT' => '+0900', # Australian Western Daylight Time UTC+09:00
41             #'AWST' => '+0800', # Australian Western Standard Time UTC+08:00
42             #'AZOST'=> '-0100', # Azores Standard Time UTC-01:00
43             #'AZT' => '+0400', # Azerbaijan Time UTC+04:00
44             #'BDT' => '+0800', # Brunei Time UTC+08:00
45             #'BIOT' => '+0600', # British Indian Ocean Time UTC+06:00
46             #'BIT' => '-1200', # Baker Island Time UTC-12:00
47             #'BOT' => '-0400', # Bolivia Time UTC-04:00
48             #'BRT' => '-0300', # Brasilia Time UTC-03:00
49             #'BST' => '+0600', # Bangladesh Standard Time UTC+06:00
50             #'BST' => '+0100', # British Summer Time (British Standard Time from Feb 1968 to Oct 1971) UTC+01:00
51             #'BTT' => '+0600', # Bhutan Time UTC+06:00
52             #'CAT' => '+0200', # Central Africa Time UTC+02:00
53             #'CCT' => '+0630', # Cocos Islands Time UTC+06:30
54             'CDT' => '-0500', # Central Daylight Time (North America) UTC-05:00
55             #'CEDT' => '+0200', # Central European Daylight Time UTC+02:00
56             #'CEST' => '+0200', # Central European Summer Time UTC+02:00
57             #'CET' => '+0100', # Central European Time UTC+01:00
58             #'CHAST'=> '+1245', # Chatham Standard Time UTC+12:45
59             #'CIST' => '-0800', # Clipperton Island Standard Time UTC-08:00
60             #'CKT' => '-1000', # Cook Island Time UTC-10:00
61             #'CLST' => '-0300', # Chile Summer Time UTC-03:00
62             #'CLT' => '-0400', # Chile Standard Time UTC-04:00
63             #'COST' => '-0400', # Colombia Summer Time UTC-04:00
64             #'COT' => '-0500', # Colombia Time UTC-05:00
65             'CST' => '-0600', # Central Standard Time (North America) UTC-06:00
66             #'CST' => '+0800', # China Standard Time UTC+08:00
67             #'CVT' => '-0100', # Cape Verde Time UTC-01:00
68             #'CXT' => '+0700', # Christmas Island Time UTC+07:00
69             #'ChST' => '+1000', # Chamorro Standard Time UTC+10:00
70             # 'DST' => '' # Daylight saving time Depending
71             #'DFT' => '+0100', # AIX specific equivalent of Central European Time UTC+01:00
72             #'EAST' => '-0600', # Easter Island Standard Time UTC-06:00
73             #'EAT' => '+0300', # East Africa Time UTC+03:00
74             #'ECT' => '-0400', # Eastern Caribbean Time (does not recognise DST) UTC-04:00
75             #'ECT' => '-0500', # Ecuador Time UTC-05:00
76             'EDT' => '-0400', # Eastern Daylight Time (North America) UTC-04:00
77             #'EEDT' => '+0300', # Eastern European Daylight Time UTC+03:00
78             #'EEST' => '+0300', # Eastern European Summer Time UTC+03:00
79             #'EET' => '+0200', # Eastern European Time UTC+02:00
80             'EST' => '+0500', # Eastern Standard Time (North America) UTC-05:00
81             #'FJT' => '+1200', # Fiji Time UTC+12:00
82             #'FKST' => '-0400', # Falkland Islands Standard Time UTC-04:00
83             #'GALT' => '-0600', # Galapagos Time UTC-06:00
84             #'GET' => '+0400', # Georgia Standard Time UTC+04:00
85             #'GFT' => '-0300', # French Guiana Time UTC-03:00
86             #'GILT' => '+1200', # Gilbert Island Time UTC+12:00
87             #'GIT' => '-0900', # Gambier Island Time UTC-09:00
88             'GMT' => '+0000', # Greenwich Mean Time UTC
89             #'GST' => '-0200', # South Georgia and the South Sandwich Islands UTC-02:00
90             #'GYT' => '-0400', # Guyana Time UTC-04:00
91             'HADT' => '-0900', # Hawaii-Aleutian Daylight Time UTC-09:00
92             'HAST' => '-1000', # Hawaii-Aleutian Standard Time UTC-10:00
93             #'HKT' => '+0800', # Hong Kong Time UTC+08:00
94             #'HMT' => '+0500', # Heard and McDonald Islands Time UTC+05:00
95             'HST' => '-1000', # Hawaii Standard Time UTC-10:00
96             #'IRKT' => '+0800', # Irkutsk Time UTC+08:00
97             #'IRST' => '+0330', # Iran Standard Time UTC+03:30
98             #'IST' => '+0530', # Indian Standard Time UTC+05:30
99             #'IST' => '+0100', # Irish Summer Time UTC+01:00
100             #'IST' => '+0200', # Israel Standard Time UTC+02:00
101             'JST' => '+0900', # Japan Standard Time UTC+09:00
102             #'KRAT' => '+0700', # Krasnoyarsk Time UTC+07:00
103             #'KST' => '+0900', # Korea Standard Time UTC+09:00
104             #'LHST' => '+1030', # Lord Howe Standard Time UTC+10:30
105             #'LINT' => '+1400', # Line Islands Time UTC+14:00
106             #'MAGT' => '+1100', # Magadan Time UTC+11:00
107             'MDT' => '-0600', # Mountain Daylight Time(North America) UTC-06:00
108             #'MIT' => '-0930', # Marquesas Islands Time UTC-09:30
109             #'MSD' => '+0400', # Moscow Summer Time UTC+04:00
110             #'MSK' => '+0300', # Moscow Standard Time UTC+03:00
111             #'MST' => '+0800', # Malaysian Standard Time UTC+08:00
112             'MST' => '-0700', # Mountain Standard Time(North America) UTC-07:00
113             #'MST' => '+0630', # Myanmar Standard Time UTC+06:30
114             #'MUT' => '+0400', # Mauritius Time UTC+04:00
115             #'NDT' => '-0230', # Newfoundland Daylight Time UTC-02:30
116             #'NFT' => '+1130', # Norfolk Time[1] UTC+11:30
117             #'NPT' => '+0545', # Nepal Time UTC+05:45
118             #'NST' => '-0330', # Newfoundland Standard Time UTC-03:30
119             #'NT' => '-0330', # Newfoundland Time UTC-03:30
120             #'OMST' => '+0600', # Omsk Time UTC+06:00
121             'PDT' => '-0700', # Pacific Daylight Time(North America) UTC-07:00
122             #'PETT' => '+1200', # Kamchatka Time UTC+12:00
123             #'PHOT' => '+1300', # Phoenix Island Time UTC+13:00
124             #'PKT' => '+0500', # Pakistan Standard Time UTC+05:00
125             'PST' => '-0800', # Pacific Standard Time (North America) UTC-08:00
126             #'PST' => '+0800', # Philippine Standard Time UTC+08:00
127             #'RET' => '+0400', # Reunion Time UTC+04:00
128             #'SAMT' => '+0400', # Samara Time UTC+04:00
129             #'SAST' => '+0200', # South African Standard Time UTC+02:00
130             #'SBT' => '+1100', # Solomon Islands Time UTC+11:00
131             #'SCT' => '+0400', # Seychelles Time UTC+04:00
132             #'SLT' => '+0530', # Sri Lanka Time UTC+05:30
133             #'SST' => '-1100', # Samoa Standard Time UTC-11:00
134             #'SST' => '+0800', # Singapore Standard Time UTC+08:00
135             #'TAHT' => '-1000', # Tahiti Time UTC-10:00
136             #'THA' => '+0700', # Thailand Standard Time UTC+07:00
137             'UT' => '-0000', # Coordinated Universal Time UTC
138             'UTC' => '-0000', # Coordinated Universal Time UTC
139             #'UYST' => '-0200', # Uruguay Summer Time UTC-02:00
140             #'UYT' => '-0300', # Uruguay Standard Time UTC-03:00
141             #'VET' => '-0430', # Venezuelan Standard Time UTC-04:30
142             #'VLAT' => '+1000', # Vladivostok Time UTC+10:00
143             #'WAT' => '+0100', # West Africa Time UTC+01:00
144             #'WEDT' => '+0100', # Western European Daylight Time UTC+01:00
145             #'WEST' => '+0100', # Western European Summer Time UTC+01:00
146             #'WET' => '-0000', # Western European Time UTC
147             #'YAKT' => '+0900', # Yakutsk Time UTC+09:00
148             #'YEKT' => '+0500', # Yekaterinburg Time UTC+05:00
149 77     77   450 };
  77         131  
150              
151             sub to_second {
152             # Convert to second
153             # @param [String] value Digit and a unit of time
154             # @return [Integer] n: seconds
155             # 0: 0 or invalid unit of time
156             # @example Get the value of seconds
157             # to_second('1d') #=> 86400
158             # to_second('2h') #=> 7200
159 83     83 1 31628 my $class = shift;
160 83   100     185 my $value = shift || return 0;
161              
162 59         67 state $timeunit = {
163             'o' => ( BASE_D * BASE_Y * 4 ), # Olympiad, 4 years
164             'y' => ( BASE_D * BASE_Y ), # Year, Gregorian Calendar
165             'q' => ( BASE_D * BASE_Y / 4 ), # Quarter, year/4
166             'l' => ( BASE_D * BASE_L ), # Lunar month
167             'f' => ( BASE_D * 14 ), # Fortnight, 2 weeks
168             'w' => ( BASE_D * 7 ), # Week, 604800 seconds
169             'd' => BASE_D, # Day
170             'h' => 3600, # Hour
171             'b' => 86.4, # Beat, Swatch internet time: 1000b = 1d
172             'm' => 60, # Minute,
173             's' => 1, # Second
174             };
175 59         56 state $mathematicalconstant = {
176             'e' => CONST_E,
177             'p' => CONST_P,
178             'g' => CONST_E ** CONST_P,
179             };
180              
181 59         57 my $getseconds = 0;
182 59         189 my $unitoftime = [keys %$timeunit];
183 59         120 my $mathconsts = [keys %$mathematicalconstant];
184              
185 59 100       416 if( $value =~ /\A(\d+|\d+[.]\d+)([@$unitoftime])?\z/o ) {
    100          
186             # 1d, 1.5w
187 7         14 my $n = $1;
188 7   50     17 my $u = $2 // 'd';
189 7         16 $getseconds = $n * $timeunit->{ $u };
190              
191             } elsif( $value =~ /\A(\d+|\d+[.]\d+)?([@$mathconsts])([@$unitoftime])?\z/o ) {
192             # 1pd, 1.5pw
193 5   50     22 my $n = $1 // 1;
194 5   100     17 my $m = $mathematicalconstant->{ $2 } // 0;
195 5   100     12 my $u = $3 // 'd';
196 5         12 $getseconds = $n * $m * $timeunit->{ $u };
197              
198             } else {
199 47         58 $getseconds = 0;
200             }
201 59         242 return $getseconds;
202             }
203              
204             sub monthname {
205             # Month name list
206             # @param [Integer] argv1 Require full name or not
207             # @return [Array, String] Month name list or month name
208             # @example Get the names of each month
209             # monthname() #=> ['Jan', 'Feb', ...]
210             # monthname(1) #=> ['January', 'February', 'March', ...]
211 18     18 0 1716 my $class = shift;
212 18   50     47 my $argv1 = shift // 0;
213 18 100       49 my $value = $argv1 ? 'full' : 'abbr';
214              
215 18 50       43 return @{ MonthName->{ $value } } if wantarray;
  0         0  
216 18         93 return MonthName->{ $value };
217             }
218              
219             sub dayofweek {
220             # List of day of week
221             # @param [Integer] argv1 Require full name
222             # @return [Array, String] List of day of week or day of week
223             # @example Get the names of each day of week
224             # dayofweek() #=> ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
225             # dayofweek(1) #=> ['Sunday', 'Monday', 'Tuesday', ...]
226 2     2 0 2306 my $class = shift;
227 2   50     6 my $argv1 = shift // 0;
228 2 100       6 my $value = $argv1 ? 'full' : 'abbr';
229              
230 2 50       5 return @{ DayOfWeek->{ $value } } if wantarray;
  0         0  
231 2         6 return DayOfWeek->{ $value };
232             }
233              
234             sub parse {
235             # Parse date string; strptime() wrapper
236             # @param [String] argv1 Date string
237             # @return [String] Converted date string
238             # @see http://en.wikipedia.org/wiki/ISO_8601
239             # @see http://www.ietf.org/rfc/rfc3339.txt
240             # @example Parse date string and convert to generic format string
241             # parse("2015-11-03T23:34:45 Tue") #=> Tue, 3 Nov 2015 23:34:45 +0900
242             # parse("Tue, Nov 3 2015 2:2:2") #=> Tue, 3 Nov 2015 02:02:02 +0900
243 2969     2969 1 32188 my $class = shift;
244 2969   50     5356 my $argv1 = shift || return undef;
245              
246 2969         4164 my $datestring = $argv1;
247 2969         8271 $datestring =~ s{[,](\d+)}{, $1}; # Thu,13 -> Thu, 13
248 2969         6115 $datestring =~ s{(\d{1,2}),}{$1}; # Apr 29, -> Apr 29
249 2969         8804 my @timetokens = split(' ', $datestring);
250 2969         4881 my $parseddate = ''; # [String] Canonified Date/Time string
251 2969         3265 my $afternoon1 = 0; # [Integer] After noon flag
252 2969         3697 my $altervalue = {}; # [Hash] To store alternative values
253 2969         11483 my $v = {
254             'Y' => undef, # [Integer] Year
255             'M' => undef, # [String] Month Abbr.
256             'd' => undef, # [Integer] Day
257             'a' => undef, # [String] Day of week, Abbr.
258             'T' => undef, # [String] Time
259             'z' => undef, # [Integer] Timezone offset
260             };
261              
262 2969         6189 for my $p ( @timetokens ) {
263             # Parse each piece of time
264 18633 100 100     70100 if( $p =~ /\A[A-Z][a-z]{2,}[,]?\z/ ) {
    100          
    100          
    100          
    100          
    100          
265             # Day of week or Day of week; Thu, Apr, ...
266 5840 100       16266 $p =~ s/,\z//g if substr($p, -1, 1) eq ','; # "Thu," => "Thu"
267 5840 100       9822 $p = substr($p, 0, 3) if length $p > 3;
268              
269 5840 100       5554 if( grep { $p eq $_ } @{ DayOfWeek->{'abbr'} } ) {
  40880 100       51058  
  5840         10538  
270             # Day of week; Mon, Thu, Sun,...
271 2895         5399 $v->{'a'} = $p;
272              
273 35340         40097 } elsif( grep { $p eq $_ } @{ MonthName->{'abbr'} } ) {
  2945         5182  
274             # Month name abbr.; Apr, May, ...
275 2942         4664 $v->{'M'} = $p;
276             }
277             } elsif( $p =~ /\A\d{1,4}\z/ ) {
278             # Year or Day; 2005, 31, 04, 1, ...
279 5886         11405 $p = int $p;
280 5886 100       8078 if( $p > 31 ) {
281             # The piece is the value of an year
282 2908         5224 $v->{'Y'} = $p;
283              
284             } else {
285             # The piece is the value of a day
286 2978 100       5502 if( $v->{'d'} ) {
287             # 2-digit year?
288 36 50       167 $altervalue->{'Y'} = $p unless $v->{'Y'};
289              
290             } else {
291             # The value is "day"
292 2942         4614 $v->{'d'} = $p;
293             }
294             }
295             } elsif( $p =~ /\A([0-2]\d):([0-5]\d):([0-5]\d)\z/ ||
296             $p =~ /\A(\d{1,2})[-:](\d{1,2})[-:](\d{1,2})\z/ ) {
297             # Time; 12:34:56, 03:14:15, ...
298             # Arrival-Date: 2014-03-26 00-01-19
299 2949 100 100     19610 if( $1 < 24 && $2 < 60 && $3 < 60 ) {
      100        
300             # Valid time format, maybe...
301 2946         17460 $v->{'T'} = sprintf("%02d:%02d:%02d", $1, $2, $3);
302             }
303             } elsif( $p =~ /\A([0-2]\d):([0-5]\d)\z/ ) {
304             # Time; 12:34 => 12:34:00
305 1 50 33     8 if( $1 < 24 && $2 < 60 ) {
306 1         6 $v->{'T'} = sprintf("%02d:%02d:00", $1, $2);
307             }
308             } elsif( $p =~ /\A(\d\d?):(\d\d?)\z/ ) {
309             # Time: 1:4 => 01:04:00
310 1         8 $v->{'T'} = sprintf("%02d:%02d:00", $1, $2);
311              
312             } elsif( $p =~ /\A[APap][Mm]\z/ ) {
313             # AM or PM
314 11         35 $afternoon1 = 1;
315              
316             } else {
317             # Timezone offset and others
318 3945 100       11400 if( $p =~ /\A[-+][01]\d{3}\z/ ) {
    100          
319             # Timezone offset; +0000, +0900, -1000, ...
320 2870   33     9333 $v->{'z'} ||= $p;
321              
322             } elsif( $p =~ /\A[(]?[A-Z]{2,5}[)]?\z/ ) {
323             # Timezone abbreviation; JST, GMT, UTC, ...
324 1041   50     3089 $v->{'z'} ||= __PACKAGE__->abbr2tz($p) || '+0000';
      66        
325              
326             } else {
327             # Other date format
328 34 100       334 if( $p =~ m|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})\z| ) {
    100          
    100          
    50          
329             # Mail.app(MacOS X)'s faked Bounce, Arrival-Date: 2010-06-18 17:17:52 +0900
330 2         7 $v->{'Y'} = int $1;
331 2         6 $v->{'M'} = MonthName->{'abbr'}->[int($2) - 1];
332 2         5 $v->{'d'} = int $3;
333              
334             } elsif( $p =~ m|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})T([0-2]\d):([0-5]\d):([0-5]\d)\z| ) {
335             # ISO 8601; 2000-04-29T01:23:45
336 18         81 $v->{'Y'} = int $1;
337 18         76 $v->{'M'} = MonthName->{'abbr'}->[int($2) - 1];
338 18 50       71 $v->{'d'} = int $3 if $3 < 32;
339              
340 18 50 33     184 if( $4 < 24 && $5 < 60 && $6 < 60 ) {
      33        
341 18         110 $v->{'T'} = sprintf("%02d:%02d:%02d", $4, $5, $6);
342             }
343             } elsif( $p =~ m|\A(\d{1,2})/(\d{1,2})/(\d{1,2})\z| ) {
344             # 4/29/01 11:34:45 PM
345 6         35 $v->{'M'} = MonthName->{'abbr'}->[int($1) - 1];
346 6         18 $v->{'d'} = int $2;
347 6         15 $v->{'Y'} = int($3) + 2000;
348 6 50       35 $v->{'Y'} -= 100 if $v->{'Y'} > Time::Piece->new->year() + 1;
349              
350             } elsif( $p =~ m|\A(\d{1,2})[-/](\d{1,2})[-/](\d{4})| ) {
351             # 29-04-2017 22:22
352 0 0       0 $v->{'d'} = int $1 if $1 < 32;
353 0         0 $v->{'M'} = MonthName->{'abbr'}->[int($2) - 1];
354 0         0 $v->{'Y'} = int($3);
355             }
356             }
357             }
358             } # End of while()
359              
360 2969 100 100     9360 if( $v->{'T'} && $afternoon1 ) {
361             # +12
362 11         25 my $t0 = $v->{'T'};
363 11         38 my @t1 = split(':', $v->{'T'});
364 11         62 $v->{'T'} = sprintf("%02d:%02d:%02d", $t1[0] + 12, $t1[1], $t1[2]);
365 11 100       37 $v->{'T'} = $t0 if $t1[0] > 12;
366             }
367              
368 2969   100     5849 $v->{'a'} ||= 'Thu'; # There is no day of week
369 2969 100 100     10565 if( defined $v->{'Y'} && $v->{'Y'} < 200 ) {
370             # 99 -> 1999, 102 -> 2002
371 2         3 $v->{'Y'} += 1900;
372             }
373 2969   66     5815 $v->{'z'} ||= __PACKAGE__->second2tz(Time::Piece->new->tzoffset);
374              
375             # Adjust 2-digit Year
376 2969 100 66     6435 if( exists $altervalue->{'Y'} && ! $v->{'Y'} ) {
377             # Check alternative value(Year)
378 36 50       73 if( $altervalue->{'Y'} >= 82 ) {
379             # SMTP was born in 1982
380 0   0     0 $v->{'Y'} ||= 1900 + $altervalue->{'Y'};
381              
382             } else {
383             # 20XX
384 36   33     113 $v->{'Y'} ||= 2000 + $altervalue->{'Y'};
385             }
386             }
387              
388             # Check each piece
389 2969 100       7363 if( grep { ! defined $_ } values %$v ) {
  17814         24194  
390             # Strange date format
391 5         173 printf(STDERR " ***warning: Strange date format [%s]\n", $datestring);
392 5         37 return undef;
393             }
394              
395 2964 100 100     11083 if( $v->{'Y'} < 1902 || $v->{'Y'} > 2037 ) {
396             # -(2^31) ~ (2^31)
397 2         9 return undef;
398             }
399              
400             # Build date string
401             # Thu, 29 Apr 2004 10:01:11 +0900
402             return sprintf("%s, %s %s %s %s %s",
403 2962         21321 $v->{'a'}, $v->{'d'}, $v->{'M'}, $v->{'Y'}, $v->{'T'}, $v->{'z'});
404             }
405              
406             sub abbr2tz {
407             # Abbreviation -> Tiemzone
408             # @param [String] argv1 Abbr. e.g.) JST, GMT, PDT
409             # @return [String, Undef] +0900, +0000, -0600 or Undef if the argument is
410             # invalid format or not supported abbreviation
411             # @example Get the timezone string of "JST"
412             # abbr2tz('JST') #=> '+0900'
413 87     87 1 398 my $class = shift;
414 87   50     195 my $argv1 = shift || return undef;
415 87         453 return TimeZones->{ $argv1 };
416             }
417              
418             sub tz2second {
419             # Convert to second
420             # @param [String] argv1 Timezone string e.g) +0900
421             # @return [Integer,Undef] n: seconds or Undef it the argument is invalid
422             # format string
423             # @see second2tz
424             # @example Convert '+0900' to seconds
425             # tz2second('+0900') #=> 32400
426 3014     3014 0 26023 my $class = shift;
427 3014   100     9444 my $argv1 = shift || return undef;
428              
429 2988 100       9588 if( $argv1 =~ /\A([-+])(\d)(\d)(\d{2})\z/ ) {
    100          
430 2940         3598 my $ztime = 0;
431 2940         14900 my $digit = {
432             'operator' => $1,
433             'hour-10' => $2,
434             'hour-01' => $3,
435             'minutes' => $4
436             };
437 2940         7703 $ztime += ( $digit->{'hour-10'} * 10 + $digit->{'hour-01'} ) * 3600;
438 2940         5098 $ztime += ( $digit->{'minutes'} * 60 );
439 2940 100       6542 $ztime *= -1 if $digit->{'operator'} eq '-';
440              
441 2940 100       5654 return undef if abs($ztime) > TZ_OFFSET;
442 2938         8122 return $ztime;
443              
444             } elsif( $argv1 =~ /\A[A-Za-z]+\z/ ) {
445 1         19 return __PACKAGE__->tz2second(TimeZones->{ $argv1 });
446              
447             } else {
448 47         130 return undef;
449             }
450             }
451              
452             sub second2tz {
453             # Convert to Timezone string
454             # @param [Integer] argv1 Second to be converted
455             # @return [String] Timezone offset string
456             # @see tz2second
457             # @example Get timezone offset string of specified seconds
458             # second2tz(12345) #=> '+0325'
459 46     46 0 6679 my $class = shift;
460 46   100     371 my $argv1 = shift // return '+0000';
461 45         297 my $digit = { 'operator' => '+' };
462              
463 45 50 66     220 return '' if( ref($argv1) && ref($argv1) ne 'Time::Seconds' );
464 45 100       146 return '' if( abs($argv1) > TZ_OFFSET ); # UTC+14 + 1(DST?)
465 43 100       1107 $digit->{'operator'} = '-' if $argv1 < 0;
466              
467 43         527 $digit->{'hours'} = int(abs($argv1) / 3600);
468 43         579 $digit->{'minutes'} = int((abs($argv1) % 3600) / 60);
469 43         820 return sprintf("%s%02d%02d", $digit->{'operator'}, $digit->{'hours'}, $digit->{'minutes'});
470             }
471              
472             1;
473             __END__