diff options
| author | fukachan <fukachan> | 2001-05-01 03:13:32 +0000 |
|---|---|---|
| committer | fukachan <fukachan> | 2001-05-01 03:13:32 +0000 |
| commit | fe9a0dcf78d2eb27e33b02b113bae28e05be4623 (patch) | |
| tree | 615a4056c8f0d95babd7bfaae64ed6d8ce7be3f1 /cpan | |
| parent | 12059d51b53964f2397d7b9e72a32f2becc85603 (diff) | |
| download | fml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.tar.gz fml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.tar.bz2 fml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.zip | |
Initial revision
Diffstat (limited to 'cpan')
| -rw-r--r-- | cpan/dist/Time-modules/CHANGELOG | 74 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/MANIFEST | 10 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/Makefile.PL | 11 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/README | 25 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/lib/Time/CTime.pm | 197 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/lib/Time/DaysInMonth.pm | 75 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/lib/Time/JulianDay.pm | 200 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/lib/Time/ParseDate.pm | 1194 | ||||
| -rw-r--r-- | cpan/dist/Time-modules/lib/Time/Timezone.pm | 272 | ||||
| -rwxr-xr-x | cpan/dist/Time-modules/t/datetime.t | 416 | ||||
| -rw-r--r-- | cpan/lib/Time/CTime.pm | 197 | ||||
| -rw-r--r-- | cpan/lib/Time/DaysInMonth.pm | 75 | ||||
| -rw-r--r-- | cpan/lib/Time/JulianDay.pm | 200 | ||||
| -rw-r--r-- | cpan/lib/Time/ParseDate.pm | 1194 | ||||
| -rw-r--r-- | cpan/lib/Time/Timezone.pm | 272 |
15 files changed, 4412 insertions, 0 deletions
diff --git a/cpan/dist/Time-modules/CHANGELOG b/cpan/dist/Time-modules/CHANGELOG new file mode 100644 index 00000000..3a2a69d9 --- /dev/null +++ b/cpan/dist/Time-modules/CHANGELOG @@ -0,0 +1,74 @@ +100.010301: + W. Phillip Moore <Phil.Moore@msdw.com> reproted a Y2K bug: it could + not parse 'Jan 1 2000 10:30:30AM'. Fixed. This is embarassing + beasue <alangmead@boston.com> sent in a patch Dec 9th! +99.111701: + Allen Smith <easmith@beatrice.rutgers.edu> noticed that VALIDATE + wouldn't accept hours > 0... + Hugh C Kennedy <kennedyh@engin.umich.edu> contributed patches to + parse months like `Jan.' in additon to `Jan' and `January'. +99.062401: + Randy Kobes <randy@theoryx5.uwinnipeg.ca> sent in a fix for + Timezone.pm that allows all tests to pass on MSWin32. +99.062301: + Eric Prestemon <ecp@adknowledge.com> noticed that %y would print + "0" in 2000, not "00" as it should. Fixed. + + Parsedate() now checks for wantarray() and returns what remains of + its input when called from array context. + + Added another parsing option: VALIDATE. When VALIDATE is set, + silly values for hours, months, etc will be rejected. Like the + 32nd of December will no longer be new years day. +99.062201: + Optionally return fractional seconds from ParseDate (including + the SYBASE formatted ones). Provide for printing fractional + seconds in CTime. Changes from Douglas Wegscheid + <wegscd@whirlpool.com> +99.061601: + More attempts to support systems whose time function isn't + centered around Jan 1, 1970 midnight GMT. +99.061501: + Fixed a couple of compiler warnings. Did extensive testing with + dates beyond 2000 and before 1970. Quite a few code changes. + Old code would have worked in many > 2000 cases but broke + terribly for dates < 1970. + New code should work until year 10k. Well, there's one problem: + unix time format is only 32 bits, signed. +98.112901: + Slight change to avoid an "Use of uninitialized value" error + pointed out by Mike Coffin <mhc@Eng.Sun.COM>. +98.112801: + It turns out that Sybase emits time values like "3:09:59:000PM". The + last three digits are milliseconds. Such times are now recognized + but the milliseconds are ignored. Change requested by + ryanmcleish <ryan@email.labmed.umn.edu>. + + Corrected the documenation on strftime conversions for %d and %e. The + code and the documentation now agree: + %d - 01 to 31 + %e - 1 to 31 +98.052201: + Minor bugfix: didn't recognize 5/18/1998. +97.092101: + Fix a 5.004 warning. + Add support for "June 4, 1997" +96.110801: + Added support for negative relative times. + Bugfix: "now" by itself was ignored. + Bugfix: could not combine "now" with time offsets (only date offsets) +96.032801: + Added support for "TZN +96.032702: + This is the first release to use the Makefile.PL + packaging. There are many changes with repect to + previous releases. All modules now running with -w and + use strict. + + CTime.pm: should be faster + ParseDate.pm: core logic changed, more dates parsed. + datetime.t: more tests + Timezone.pm: ripped out tzset code and replaced it + with code from Graham Barr <bodg@tiuk.ti.com> +??.?????: + Patch for UK-style dates: Sam Yates <syates@maths.adelaide.edu.au> diff --git a/cpan/dist/Time-modules/MANIFEST b/cpan/dist/Time-modules/MANIFEST new file mode 100644 index 00000000..75076d8a --- /dev/null +++ b/cpan/dist/Time-modules/MANIFEST @@ -0,0 +1,10 @@ +MANIFEST +CHANGELOG +Makefile.PL +README +lib/Time/CTime.pm +lib/Time/DaysInMonth.pm +lib/Time/JulianDay.pm +lib/Time/ParseDate.pm +lib/Time/Timezone.pm +t/datetime.t diff --git a/cpan/dist/Time-modules/Makefile.PL b/cpan/dist/Time-modules/Makefile.PL new file mode 100644 index 00000000..0340d250 --- /dev/null +++ b/cpan/dist/Time-modules/Makefile.PL @@ -0,0 +1,11 @@ + +use ExtUtils::MakeMaker; + +WriteMakefile( + 'VERSION' => 100.010301, + 'NAME' => 'Time-modules', + ($] >= 5.005 ? + ('ABSTRACT' => 'Parse and format time values', + 'AUTHOR' => 'David Muir Sharnoff <muir@idiom.com>') : ()), + 'dist' => { COMPRESS=>"gzip", SUFFIX=>"gz" }, + ); diff --git a/cpan/dist/Time-modules/README b/cpan/dist/Time-modules/README new file mode 100644 index 00000000..5f743bd5 --- /dev/null +++ b/cpan/dist/Time-modules/README @@ -0,0 +1,25 @@ + +This package contains the following perl5 modules: + + Time::CTime.pm + ctime, strftime, and asctime + Time::JulianDay.pm + Julian Day conversions + Time::ParseDate.pm + Reverses strftime and also understands relative times + Time::Timezone.pm + Time::DaysInMonth.pm + + +To install these modules, cd to the directory that this README file is +in and type the following: + + perl Makefile.PL + make + make test + make install + +Documentation for these modules is part of the files themselves using +the pod (Plain Old Documentation) format. This documentation will be +installed as part of the installation process. + diff --git a/cpan/dist/Time-modules/lib/Time/CTime.pm b/cpan/dist/Time-modules/lib/Time/CTime.pm new file mode 100644 index 00000000..530a6094 --- /dev/null +++ b/cpan/dist/Time-modules/lib/Time/CTime.pm @@ -0,0 +1,197 @@ +package Time::CTime; + + +require 5.000; + +use Time::Timezone; +use Time::CTime; +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(ctime asctime strftime); +@EXPORT_OK = qw(asctime_n ctime_n @DoW @MoY @DayOfWeek @MonthOfYear); + +use strict; + +# constants +use vars qw(@DoW @DayOfWeek @MoY @MonthOfYear %strftime_conversion $VERSION); +use vars qw($template $sec $min $hour $mday $mon $year $wday $yday $isdst); + +$VERSION = 99.06_22_01; + +CONFIG: { + @DoW = qw(Sun Mon Tue Wed Thu Fri Sat); + @DayOfWeek = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday); + @MoY = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + @MonthOfYear = qw(January February March April May June + July August September October November December); + + %strftime_conversion = ( + '%', sub { '%' }, + 'a', sub { $DoW[$wday] }, + 'A', sub { $DayOfWeek[$wday] }, + 'b', sub { $MoY[$mon] }, + 'B', sub { $MonthOfYear[$mon] }, + 'c', sub { asctime_n($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, "") }, + 'd', sub { sprintf("%02d", $mday); }, + 'D', sub { sprintf("%02d/%02d/%02d", $mon+1, $mday, $year%100) }, + 'e', sub { sprintf("%2d", $mday); }, + 'f', sub { fracprintf ("%3.3f", $sec); }, + 'F', sub { fracprintf ("%6.6f", $sec); }, + 'h', sub { $MoY[$mon] }, + 'H', sub { sprintf("%02d", $hour) }, + 'I', sub { sprintf("%02d", $hour % 12 || 12) }, + 'j', sub { sprintf("%03d", $yday + 1) }, + 'k', sub { sprintf("%2d", $hour); }, + 'l', sub { sprintf("%2d", $hour % 12 || 12) }, + 'm', sub { sprintf("%02d", $mon+1); }, + 'M', sub { sprintf("%02d", $min) }, + 'n', sub { "\n" }, + 'o', sub { sprintf("%d%s", $mday, (($mday < 20 && $mday > 3) ? 'th' : ($mday%10 == 1 ? "st" : ($mday%10 == 2 ? "nd" : ($mday%10 == 3 ? "rd" : "th"))))) }, + 'p', sub { $hour > 11 ? "PM" : "AM" }, + 'r', sub { sprintf("%02d:%02d:%02d %s", $hour % 12 || 12, $min, $sec, $hour > 11 ? 'PM' : 'AM') }, + 'R', sub { sprintf("%02d:%02d", $hour, $min) }, + 'S', sub { sprintf("%02d", $sec) }, + 't', sub { "\t" }, + 'T', sub { sprintf("%02d:%02d:%02d", $hour, $min, $sec) }, + 'U', sub { wkyr(0, $wday, $yday) }, + 'w', sub { $wday }, + 'W', sub { wkyr(1, $wday, $yday) }, + 'y', sub { sprintf("%02d",$year%100) }, + 'Y', sub { $year + 1900 }, + 'x', sub { sprintf("%02d/%02d/%02d", $mon + 1, $mday, $year%100) }, + 'X', sub { sprintf("%02d:%02d:%02d", $hour, $min, $sec) }, + 'Z', sub { &tz2zone(undef,undef,$isdst) } + ); + + +} + +sub fracprintf { + my($t,$s) = @_; + my($p) = sprintf($t, $s-int($s)); + $p=~s/^0+//; + $p; +} + +sub asctime_n { + my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $TZname) = @_; + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $TZname) = localtime($sec) unless defined $min; + $year += 1900; + $TZname .= ' ' + if $TZname; + sprintf("%s %s %2d %2d:%02d:%02d %s%4d", + $DoW[$wday], $MoY[$mon], $mday, $hour, $min, $sec, $TZname, $year); +} + +sub asctime +{ + return asctime_n(@_)."\n"; +} + +# is this formula right? +sub wkyr { + my($wstart, $wday, $yday) = @_; + $wday = ($wday + 7 - $wstart) % 7; + return int(($yday - $wday + 13) / 7 - 1); +} + +# ctime($time) + +sub ctime { + my($time) = @_; + asctime(localtime($time), &tz2zone(undef,$time)); +} + +sub ctime_n { + my($time) = @_; + asctime_n(localtime($time), &tz2zone(undef,$time)); +} + +# strftime($template, @time_struct) +# +# Does not support locales + +sub strftime { + local ($template, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = @_; + + undef $@; + $template =~ s/%([%aAbBcdDefFhHIjklmMnopQrRStTUwWxXyYZ])/&{$Time::CTime::strftime_conversion{$1}}()/egs; + die $@ if $@; + return $template; +} + +1; + +__DATA__ + +=head1 NAME + +Time::CTime -- format times ala POSIX asctime + +=head1 SYNOPSIS + + use Time::CTime + print ctime(time); + print asctime(localtime(time)); + print strftime(template, localtime(time)); + +=head2 strftime conversions + + %% PERCENT + %a day of the week abbr + %A day of the week + %b month abbr + %B month + %c ctime format: Sat Nov 19 21:05:57 1994 + %d DD + %D MM/DD/YY + %e numeric day of the month + %f floating point seconds (milliseconds): .314 + %F floating point seconds (microseconds): .314159 + %h month abbr + %H hour, 24 hour clock, leading 0's) + %I hour, 12 hour clock, leading 0's) + %j day of the year + %k hour + %l hour, 12 hour clock + %m month number, starting with 1 + %M minute, leading 0's + %n NEWLINE + %o ornate day of month -- "1st", "2nd", "25th", etc. + %p AM or PM + %r time format: 09:05:57 PM + %R time format: 21:05 + %S seconds, leading 0's + %t TAB + %T time format: 21:05:57 + %U week number, Sunday as first day of week + %w day of the week, numerically, Sunday == 0 + %W week number, Monday as first day of week + %x date format: 11/19/94 + %X time format: 21:05:57 + %y year (2 digits) + %Y year (4 digits) + %Z timezone in ascii. eg: PST + +=head1 DESCRIPTION + +This module provides routines to format dates. They correspond +to the libc routines. &strftime() supports a pretty good set of +coversions -- more than most C libraries. + +strftime supports a pretty good set of conversions. + +The POSIX module has very similar functionality. You should consider +using it instead if you do not have allergic reactions to system +libraries. + +=head1 GENESIS + +Written by David Muir Sharnoff <muir@idiom.com>. + +The starting point for this package was a posting by +Paul Foley <paul@ascent.com> + +Copyright (C) 1996-1999 David Muir Sharnoff. All Rights Reserved. +Use and redistribution allowed at user's own risk. + diff --git a/cpan/dist/Time-modules/lib/Time/DaysInMonth.pm b/cpan/dist/Time-modules/lib/Time/DaysInMonth.pm new file mode 100644 index 00000000..2139f2f8 --- /dev/null +++ b/cpan/dist/Time-modules/lib/Time/DaysInMonth.pm @@ -0,0 +1,75 @@ +package Time::DaysInMonth; + +use Carp; + +require 5.000; + +@ISA = qw(Exporter); +@EXPORT = qw(days_in is_leap); +@EXPORT_OK = qw(%mltable); + +use strict; + +use vars qw($VERSION %mltable); + +$VERSION = 96.032702; + +CONFIG: { + %mltable = qw( + 1 31 + 3 31 + 4 30 + 5 31 + 6 30 + 7 31 + 8 31 + 9 30 + 10 31 + 11 30 + 12 31); +} + +sub days_in +{ + # Month is 1..12 + my ($year, $month) = @_; + return $mltable{$month+0} unless $month == 2; + return 28 unless &is_leap($year); + return 29; +} + +sub is_leap +{ + my ($year) = @_; + return 0 unless $year % 4 == 0; + return 1 unless $year % 100 == 0; + return 0 unless $year % 400 == 0; + return 1; +} + +1; + +__DATA__ + +=head1 NAME + +Time::DaysInMonth -- simply report the number of days in a month + +=head1 SYNOPSIS + + use Time::DaysInMonth; + $days = days_in($year, $month_1_to_12); + $leapyear = is_leap($year); + +=head1 DESCRIPTION + +DaysInMonth is simply a package to report the number of days in +a month. That's all it does. Really! + +=head1 AUTHOR + +David Muir Sharnoff <muir@idiom.com> + +Copyright (C) 1996-1999 David Muir Sharnoff. All Rights Reserved. +Use and redistribution allowed at user's own risk. + diff --git a/cpan/dist/Time-modules/lib/Time/JulianDay.pm b/cpan/dist/Time-modules/lib/Time/JulianDay.pm new file mode 100644 index 00000000..1b011da1 --- /dev/null +++ b/cpan/dist/Time-modules/lib/Time/JulianDay.pm @@ -0,0 +1,200 @@ +package Time::JulianDay; + +require 5.000; + +use Carp; +use Time::Timezone; + +@ISA = qw(Exporter); +@EXPORT = qw(julian_day inverse_julian_day day_of_week + jd_secondsgm jd_secondslocal + jd_timegm jd_timelocal + gm_julian_day local_julian_day + ); +@EXPORT_OK = qw($brit_jd); + +use strict; +use integer; + +# constants +use vars qw($brit_jd $jd_epoch $jd_epoch_remainder $VERSION); + +$VERSION = 99.061501; + +# calculate the julian day, given $year, $month and $day +sub julian_day +{ + my($year, $month, $day) = @_; + my($tmp); + my($secs); + + use Carp; +# confess() unless defined $day; + + $tmp = $day - 32075 + + 1461 * ( $year + 4800 - ( 14 - $month ) / 12 )/4 + + 367 * ( $month - 2 + ( ( 14 - $month ) / 12 ) * 12 ) / 12 + - 3 * ( ( $year + 4900 - ( 14 - $month ) / 12 ) / 100 ) / 4 + ; + + return($tmp); + +} + +sub gm_julian_day +{ + my($secs) = @_; + my($sec, $min, $hour, $mon, $year, $day, $month); + ($sec, $min, $hour, $day, $mon, $year) = gmtime($secs); + $month = $mon + 1; + $year += 1900; + return julian_day($year, $month, $day) +} + +sub local_julian_day +{ + my($secs) = @_; + my($sec, $min, $hour, $mon, $year, $day, $month); + ($sec, $min, $hour, $day, $mon, $year) = localtime($secs); + $month = $mon + 1; + $year += 1900; + return julian_day($year, $month, $day) +} + +sub day_of_week +{ + my ($jd) = @_; + return (($jd + 1) % 7); # calculate weekday (0=Sun,6=Sat) +} + + +# The following defines the first day that the Gregorian calendar was used +# in the British Empire (Sep 14, 1752). The previous day was Sep 2, 1752 +# by the Julian Calendar. The year began at March 25th before this date. + +$brit_jd = 2361222; + +# Usage: ($year,$month,$day) = &inverse_julian_day($julian_day) +sub inverse_julian_day +{ + my($jd) = @_; + my($jdate_tmp); + my($m,$d,$y); + + carp("warning: julian date $jd pre-dates British use of Gregorian calendar\n") + if ($jd < $brit_jd); + + $jdate_tmp = $jd - 1721119; + $y = (4 * $jdate_tmp - 1)/146097; + $jdate_tmp = 4 * $jdate_tmp - 1 - 146097 * $y; + $d = $jdate_tmp/4; + $jdate_tmp = (4 * $d + 3)/1461; + $d = 4 * $d + 3 - 1461 * $jdate_tmp; + $d = ($d + 4)/4; + $m = (5 * $d - 3)/153; + $d = 5 * $d - 3 - 153 * $m; + $d = ($d + 5) / 5; + $y = 100 * $y + $jdate_tmp; + if($m < 10) { + $m += 3; + } else { + $m -= 9; + ++$y; + } + return ($y, $m, $d); +} + +{ + my($sec, $min, $hour, $day, $mon, $year) = gmtime(0); + $year += 1900; + if ($year == 1970 && $mon == 0 && $day == 1) { + # standard unix time format + $jd_epoch = 2440588; + } else { + $jd_epoch = julian_day($year, $mon+1, $day); + } + $jd_epoch_remainder = $hour*3600 + $min*60 + $sec; +} + +sub jd_secondsgm +{ + my($jd, $hr, $min, $sec) = @_; + + my($r) = (($jd - $jd_epoch) * 86400 + + $hr * 3600 + $min * 60 + - $jd_epoch_remainder); + + no integer; + return ($r + $sec); + use integer; +} + +sub jd_secondslocal +{ + my($jd, $hr, $min, $sec) = @_; + my $jds = jd_secondsgm($jd, $hr, $min, $sec); + return $jds - tz_local_offset($jds); +} + +# this uses a 0-11 month to correctly reverse localtime() +sub jd_timelocal +{ + my ($sec,$min,$hours,$mday,$mon,$year) = @_; + $year += 1900 unless $year > 1000; + my $jd = julian_day($year, $mon+1, $mday); + my $jds = jd_secondsgm($jd, $hours, $min, $sec); + return $jds - tz_local_offset($jds); +} + +# this uses a 0-11 month to correctly reverse gmtime() +sub jd_timegm +{ + my ($sec,$min,$hours,$mday,$mon,$year) = @_; + $year += 1900 unless $year > 1000; + my $jd = julian_day($year, $mon+1, $mday); + return jd_secondsgm($jd, $hours, $min, $sec); +} + +1; + +__DATA__ + +=head1 NAME + +Time::JulianDay -- Julian calendar manipulations + +=head1 SYNOPSIS + + use Time::JulianDay + + $jd = julian_day($year, $month_1_to_12, $day) + $jd = local_julian_day($seconds_since_1970); + $jd = gm_julian_day($seconds_since_1970); + ($year, $month_1_to_12, $day) = inverse_julian_day($jd) + $dow = day_of_week($jd) + + print (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$dow]; + + $seconds_since_jan_1_1970 = jd_secondslocal($jd, $hour, $min, $sec) + $seconds_since_jan_1_1970 = jd_secondsgm($jd, $hour, $min, $sec) + $seconds_since_jan_1_1970 = jd_timelocal($sec,$min,$hours,$mday,$month_0_to_11,$year) + $seconds_since_jan_1_1970 = jd_timegm($sec,$min,$hours,$mday,$month_0_to_11,$year) + +=head1 DESCRIPTION + +JulianDay is a package that manipulates dates as number of days since +some time a long time ago. It's easy to add and subtract time +using julian days... + +The day_of_week returned by day_of_week() is 0 for Sunday, and 6 for +Saturday and everything else is in between. + +=head1 GENESIS + +Written by David Muir Sharnoff <muir@idiom.com> with help from +previous work by +Kurt Jaeger aka PI <zrzr0111@helpdesk.rus.uni-stuttgart.de> + based on postings from: Ian Miller <ian_m@cix.compulink.co.uk>; +Gary Puckering <garyp%cognos.uucp@uunet.uu.net> + based on Collected Algorithms of the ACM ?; +and the unknown-to-me author of Time::Local. diff --git a/cpan/dist/Time-modules/lib/Time/ParseDate.pm b/cpan/dist/Time-modules/lib/Time/ParseDate.pm new file mode 100644 index 00000000..8f1c18fb --- /dev/null +++ b/cpan/dist/Time-modules/lib/Time/ParseDate.pm @@ -0,0 +1,1194 @@ + +package Time::ParseDate; + +require 5.000; + +use Carp; +use Time::Timezone; +use Time::JulianDay; +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(parsedate); +@EXPORT_OK = qw(pd_raw %mtable %umult %wdays); + +use strict; + +# constants +use vars qw(%mtable %umult %wdays $VERSION); + +$VERSION = 100.01_03_01; + +# globals +use vars qw($debug); + +# dynamically-scoped +use vars qw($parse); + +my %mtable; +my %umult; +my %wdays; +my $y2k; + +CONFIG: { + + %mtable = qw( + Jan 1 Jan. 1 January 1 + Feb 2 Feb. 2 February 2 + Mar 3 Mar. 3 March 3 + Apr 4 Apr. 4 April 4 + May 5 + Jun 6 Jun. 6 June 6 + Jul 7 Jul. 7 July 7 + Aug 8 Aug. 8 August 8 + Sep 9 Sep. 9 September 9 + Oct 10 Oct. 10 October 10 + Nov 11 Nov. 11 November 11 + Dec 12 Dec. 12 December 12 ); + %umult = qw( + sec 1 second 1 + min 60 minute 60 + hour 3600 + day 86400 + week 604800 ); + %wdays = qw( + sun 0 sunday 0 + mon 1 monday 1 + tue 2 tuesday 2 + wed 3 wednesday 3 + thu 4 thursday 4 + fri 5 friday 5 + sat 6 saturday 6 + ); + + $y2k = 946684800; # turn of the century +} + +sub parsedate +{ + my ($t, %options) = @_; + + my ($y, $m, $d); # year, month - 1..12, day + my ($H, $M, $S); # hour, minute, second + my $tz; # timezone + my $tzo; # timezone offset + my ($rd, $rs); # relative days, relative seconds + + my $rel; # time&|date is relative + + my $isspec; + my $now = $options{NOW} || time; + my $passes = 0; + my $uk = defined($options{UK}) ? $options{UK} : 0; + + local $parse = ''; # will be dynamically scoped. + + if ($t =~ s#^ ([ \d]\d) + / (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) + / (\d\d\d\d) + : (\d\d) + : (\d\d) + : (\d\d) + (?: + [ ] + ([-+] \d\d\d\d) + (?: \("?(?:(?:[A-Z]{1,4}[TCW56])|IDLE)\))? + )? + ##xi) { #"emacs + # [ \d]/Mon/yyyy:hh:mm:ss [-+]\d\d\d\d + # This is the format for www server logging. + + ($d, $m, $y, $H, $M, $S, $tzo) = ($1, $mtable{"\u\L$2"}, $3, $4, $5, $6, $7 ? &mkoff($7) : ($tzo || undef)); + $parse .= " ".__LINE__ if $debug; + } elsif ($t =~ s#^(\d\d)/(\d\d)/(\d\d)\.(\d\d)\:(\d\d)(\s+|$)##) { + # yy/mm/dd.hh:mm + # I support this format because it's used by wbak/rbak + # on Apollo Domain OS. Silly, but historical. + + ($y, $m, $d, $H, $M, $S) = ($1, $2, $3, $4, $5, 0); + $parse .= " ".__LINE__ if $debug; + } else { + while(1) { + if (! defined $m and ! defined $rd and ! defined $y + and ! ($passes == 0 and $options{'TIMEFIRST'})) + { + # no month defined. + if (&parse_date_only(\$t, \$y, \$m, \$d, $uk)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $H and ! defined $rs) { + if (&parse_time_only(\$t, \$H, \$M, \$S, + \$tz, %options)) + { + $parse .= " ".__LINE__ if $debug; + next; + } + } + next if $passes == 0 and $options{'TIMEFIRST'}; + if (! defined $y) { + if (&parse_year_only(\$t, \$y, $now, %options)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $tz and ! defined $tzo and ! defined $rs + and (defined $m or defined $H)) + { + if (&parse_tz_only(\$t, \$tz, \$tzo)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $H and ! defined $rs) { + if (&parse_time_offset(\$t, \$rs, %options)) { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $m and ! defined $rd and ! defined $y) { + if (&parse_date_offset(\$t, $now, \$y, + \$m, \$d, \$rd, \$rs, %options)) + { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (defined $M or defined $rd) { + if ($t =~ s/^\s*(?:at|\+)\s*(\s+|$)//x) { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + last; + } continue { + print "context$parse remaider = $t.\n" if $debug; + $passes++; + } + + if ($passes == 0) { + print "nothing matched\n" if $debug; + return (undef, "no match on time/date") + if wantarray(); + return undef; + } + } + + if ($debug) { + print "t: $t.\n"; + print defined($tz) ? "tz: $tz.\n" : "no tz\n"; + print defined($tzo) ? "tzo: $tzo.\n" : "no tzo\n"; + print "HMS: "; + print defined($H) ? "$H, " : "no H, "; + print defined($M) ? "$M, " : "no M, "; + print defined($S) ? "$S\n" : "no S.\n"; + print "mdy: "; + print defined($m) ? "$m, " : "no m, "; + print defined($d) ? "$d, " : "no d, "; + print defined($y) ? "$y\n" : "no y.\n"; + print defined($rs) ? "rs: $rs.\n" : "no rs\n"; + print defined($rd) ? "rd: $rd.\n" : "no rd\n"; + print "parse:$parse\n"; + print "passes: $passes\n"; + } + + $t =~ s/^\s+//; + + if ($t ne '') { + # we didn't manage to eat the string + print "NOT WHOLE\n" if $debug; + return undef if $options{WHOLE}; + return (undef, "characters left over after parse") + if wantarray(); + } + + # define a date if there isn't one already + + if (! defined $y and ! defined $m and ! defined $rd) { + print "no date defined, trying to find one." if $debug; + if (defined $rs or defined $H) { + # we do have a time. + return (undef, "no date specified") + if wantarray(); + return undef if $options{DATE_REQUIRED}; + if (defined $rs) { + print "simple offset: $rs\n" if $debug; + my $rv = $now + $rs; + return ($rv, $t) if wantarray(); + return $rv; + } + $rd = 0; + } else { + print "no time either!\n" if $debug; + return (undef, "no time specified") + if wantarray(); + return undef; + } + } + + if ($options{TIME_REQUIRED} && ! defined($rs) + && ! defined($H) && ! defined($rd)) + { + return (undef, "no time found") + if wantarray(); + return undef; + } + + my $secs; + my $jd; + + if (defined $rd) { + if (defined $rs || ! (defined($H) || defined($M) || defined($S))) { + print "fully relative\n" if $debug; + my ($j, $in, $it); + my $definedrs = defined($rs) ? $rs : 0; + my ($isdst_now, $isdst_then); + my $r = $now + $rd * 86400 + $definedrs; + # + # It's possible that there was a timezone shift + # during the time specified. If so, keep the + # hours the "same". + # + $isdst_now = (localtime($r))[8]; + $isdst_then = (localtime($now))[8]; + if (($isdst_now == $isdst_then) || $options{GMT}) + { + return ($r, $t) if wantarray(); + return $r + } + + print "localtime changed DST during time period!\n" if $debug; + } + + print "relative date\n" if $debug; + $jd = local_julian_day($now); + print "jd($now) = $jd\n" if $debug; + $jd += $rd; + } else { + unless (defined $y) { + if ($options{PREFER_PAST}) { + my ($day, $mon011); + ($day, $mon011, $y) = (&righttime($now))[3,4,5]; + + print "calc year -past $day-$d $mon011-$m $y\n" if $debug; + $y -= 1 if ($mon011+1 < $m) || + (($mon011+1 == $m) && ($day < $d)); + } elsif ($options{PREFER_FUTURE}) { + print "calc year -future\n" if $debug; + my ($day, $mon011); + ($day, $mon011, $y) = (&righttime($now))[3,4,5]; + $y += 1 if ($mon011 >= $m) || + (($mon011+1 == $m) && ($day > $d)); + } else { + print "calc year -this\n" if $debug; + $y = (localtime($now))[5]; + } + $y += 1900; + } + + $y = expand_two_digit_year($y, $now, %options) + if $y < 100; + + if ($options{VALIDATE}) { + require Time::DaysInMonth; + my $dim = Time::DaysInMonth::days_in($y, $m); + if ($y < 1000 or $m < 1 or $d < 1 + or $y > 9999 or $m > 12 or $d > $dim) + { + return (undef, "illegal YMD: $y, $m, $d") + if wantarray(); + return undef; + } + } + $jd = julian_day($y, $m, $d); + print "jd($y, $m, $d) = $jd\n" if $debug; + } + + # put time into HMS + + if (! defined($H)) { + if (defined($rd) || defined($rs)) { + ($S, $M, $H) = &righttime($now, %options); + print "HMS set to $H $M $S\n" if $debug; + } + } + + my $carry; + + print "before ", (defined($rs) ? "$rs" : ""), + " $jd $H $M $S\n" + if $debug; + # + # add in relative seconds. Do it this way because we want to + # preserve the localtime across DST changes. + # + + $S = 0 unless $S; # -w + $M = 0 unless $M; # -w + $H = 0 unless $H; # -w + + if ($options{VALIDATE} and + ($S < 0 or $M < 0 or $H < 0 or $S > 59 or $M > 59 or $H > 23)) + { + return (undef, "illegal HMS: $H, $M, $S") if wantarray(); + return undef; + } + + $S += $rs if defined $rs; + $carry = int($S / 60); + my($frac) = $S - int($S); + $S = int($S); + $S %= 60; + $S += $frac; + $M += $carry; + $carry = int($M / 60); + $M %= 60; + $H += $carry; + $carry = int($H / 24); + $H %= 24; + $jd += $carry; + + print "after rs $jd $H $M $S\n" if $debug; + + $secs = jd_secondsgm($jd, $H, $M, $S); + print "jd_secondsgm($jd, $H, $M, $S) = $secs\n" if $debug; + + # + # If we see something link 3pm CST then and we want to end + # up with a GMT seconds, then we convert the 3pm to GMT and + # subtract in the offset for CST. We subtract because we + # are converting from CST to GMT. + # + my $tzadj; + if ($tz) { + $tzadj = tz_offset($tz, $secs); + print "adjusting secs for $tz: $tzadj\n" if $debug; + $tzadj = tz_offset($tz, $secs-$tzadj); + $secs -= $tzadj; + } elsif (defined $tzo) { + print "adjusting time for offset: $tzo\n" if $debug; + $secs -= $tzo; + } else { + unless ($options{GMT}) { + if ($options{ZONE}) { + $tzadj = tz_offset($options{ZONE}, $secs); + $tzadj = tz_offset($options{ZONE}, $secs-$tzadj); + print "adjusting secs for $options{ZONE}: $tzadj\n" if $debug; + $secs -= $tzadj; + } else { + $tzadj = tz_local_offset($secs); + print "adjusting secs for local offset: $tzadj\n" if $debug; + # + # Just in case we are very close to a time + # change... + # + $tzadj = tz_local_offset($secs-$tzadj); + $secs -= $tzadj; + } + } + } + + print "returning $secs.\n" if $debug; + + return ($secs, $t) if wantarray(); + return $secs; +} + + +sub mkoff +{ + my($offset) = @_; + + if (defined $offset and $offset =~ s#^([-+])(\d\d)(\d\d)$##) { + return ($1 eq '+' ? + 3600 * $2 + 60 * $3 + : -3600 * $2 + -60 * $3 ); + } + return undef; +} + +sub parse_tz_only +{ + my($tr, $tz, $tzo) = @_; + + $$tr =~ s#^\s+##; + my $o; + + if ($$tr =~ s#^ + ([-+]\d\d\d\d) + \s+ + \( + "? + (?: + (?: + [A-Z]{1,4}[TCW56] + ) + | + IDLE + ) + \) + (?: + \s+ + | + $ + ) + ##x) { #"emacs + $$tzo = &mkoff($1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^GMT\s*([-+]\d{1,2})(\s+|$)##x) { + $o = $1; + if ($o <= 24 and $o !~ /^0/) { + # probably hours. + printf "adjusted at %d. ($o 00)\n", __LINE__ if $debug; + $o = "${o}00"; + } + $o =~ s/\b(\d\d\d)/0$1/; + $$tzo = &mkoff($o); + printf "matched at %d. ($$tzo, $o)\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?:GMT\s*)?([-+]\d\d\d\d)(\s+|$)##x) { + $o = $1; + $$tzo = &mkoff($o); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^"?((?:[A-Z]{1,4}[TCW56])|IDLE)(?:\s+|$ )##x) { #" + $$tz = $1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_date_only +{ + my ($tr, $yr, $mr, $dr, $uk) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(\d\d\d\d)([-./])(\d\d?)\2(\d\d?)(\s+|$)##) { + # yyyy/mm/dd + + ($$yr, $$mr, $$dr) = ($1, $3, $4); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)([-./])(\d\d?)\2(\d\d\d\d?)(\s+|$)##) { + # mm/dd/yyyy - is this safe? No. + # -- or dd/mm/yyyy! If $1>12, then it's umabiguous. + # Otherwise check option UK for UK style date. + if ($uk || $1>12) { + ($$yr, $$mr, $$dr) = ($4, $3, $1); + } else { + ($$yr, $$mr, $$dr) = ($4, $1, $3); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d\d\d)/(\d\d?)(?:\s|$ )##x) { + # yyyy/mm + + ($$yr, $$mr, $$dr) = ($1, $2, 1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (?: + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),? + \s+ + )? + (\d\d?) + (\s+ | - | \. | /) + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\.? + (?: + \2 + (\d\d (?:\d\d)? ) + )? + (?: + \s+ + | + $ + ) + ##) { + # [Dow,] dd Mon [yy[yy]] + ($$yr, $$mr, $$dr) = ($4, $mtable{"\u\L$3"}, $1); + + printf "%d: %s - %s - %s\n", __LINE__, $1, $2, $3 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + return 1; + } elsif ($$tr =~ s#^(?xi) + (?: + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),? + \s+ + )? + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\.? + ((\s)+ | - | \. | /) + + (\d\d?) + (?: + (?: \2|\3+) + (\d\d (?: \d\d)?) + )? + (?: + \s+ + | + $ + ) + ##) { + # [Dow,] Mon dd [yyyy] + ($$yr, $$mr, $$dr) = ($5, $mtable{"\u\L$1"}, $4); + printf "%d: %s - %s - %s\n", __LINE__, $1, $2, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + return 1; + } elsif ($$tr =~ s#^(?xi) + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + \s+ + (\d+) + (?:st|nd|rd|th)? + \,? + (?: + \s+ + (?: + (\d\d\d\d) + |(?:\' (\d\d)) + ) + )? + (?: + \s+ + | + $ + ) + ##) { + # Month day{st,nd,rd,th}, 'yy + # Month day{st,nd,rd,th}, year + ($$yr, $$mr, $$dr) = ($3 || $4, $mtable{"\u\L$1"}, $2); + printf "%d: %s - %s - %s - %s\n", __LINE__, $1, $2, $3, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)([-/.])(\d\d?)\2(\d\d?)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12 && $4 < 32)) { + # yy/mm/dd + ($$yr, $$mr, $$dr) = ($1, $3, $4); + } elsif ($1 > 12 || $uk) { + # dd/mm/yy + ($$yr, $$mr, $$dr) = ($4, $3, $1); + } else { + # mm/dd/yy + ($$yr, $$mr, $$dr) = ($4, $1, $3); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)/(\d\d?)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12)) { + # yy/mm + ($$yr, $$mr, $$dr) = ($1, $2, 1); + } elsif ($2 > 31 || ($uk && $2 > 12)) { + # mm/yy + ($$yr, $$mr, $$dr) = ($2, $1, 1); + } elsif ($1 > 12 || $uk) { + # dd/mm + ($$mr, $$dr) = ($2, $1); + } else { + # mm/dd + ($$mr, $$dr) = ($1, $2); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d)(\d\d)(\d\d)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12)) { + # YYMMDD + ($$yr, $$mr, $$dr) = ($1, $2, $3); + } elsif ($1 > 12 || $uk) { + # DDMMYY + ($$yr, $$mr, $$dr) = ($3, $2, $1); + } else { + # MMDDYY + ($$yr, $$mr, $$dr) = ($3, $1, $2); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (\d{1,2}) + (\s+ | - | \. | /) + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + (?: + \2 + ( + \d\d + (?:\d\d)? + ) + ) + (:? + \s+ + | + $ + ) + ##) { + # dd Month [yr] + ($$yr, $$mr, $$dr) = ($4, $mtable{"\u\L$3"}, $1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (\d+) + (?:st|nd|rd|th)? + \s+ + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + (?: + \,? + \s+ + (\d\d\d\d) + )? + (:? + \s+ + | + $ + ) + ##) { + # day{st,nd,rd,th}, Month year + ($$yr, $$mr, $$dr) = ($3, $mtable{"\u\L$2"}, $1); + printf "%d: %s - %s - %s - %s\n", __LINE__, $1, $2, $3, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_time_only +{ + my ($tr, $hr, $mr, $sr, $tzr, %options) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s!^(?x) + (?: + (?: + ([012]\d) (?# $1) + (?: + ([0-5]\d) (?# $2) + (?: + ([0-5]\d) (?# $3) + )? + ) + \s* + ([ap]m)? (?# $4) + ) | (?: + (\d{1,2}) (?# $5) + (?: + \: + (\d\d) (?# $6) + (?: + \: + (\d\d) (?# $7) + ( + (?# don't barf on database sub-second timings) + (?:\:|\.) + \d{1,6} + )? (?# $8) + )? + ) + \s* + ([apAP][mM])? (?# $9) + ) | (?: + (\d{1,2}) (?# $10) + ([apAP][mM]) (?# ${11}) + ) + ) + (?: + \s+ + "? + ( (?# ${12}) + (?: [A-Z]{1,4}[TCW56] ) + | + IDLE + ) + )? + (?: + \s+ + | + $ + ) + !!) { #"emacs + # HH[[:]MM[:SS]]meridan [zone] + my $ampm; + $$hr = $1 || $5 || $10 || 0; # 10 is undef, but 5 is defined.. + $$mr = $2 || $6 || 0; + $$sr = $3 || $7 || 0; + if (defined($8) && exists($options{SUBSECOND}) && $options{SUBSECOND}) { + my($frac) = $8; + substr($frac,0,1) = '.'; + $$sr += $frac; + } + print "S = $$sr\n" if $debug; + $ampm = $4 || $9 || $11; + $$tzr = $12; + $$hr += 12 if $ampm and "\U$ampm" eq "PM" && $$hr != 12; + $$hr = 0 if $$hr == 12 && "\U$ampm" eq "AM"; + $$hr = 0 if $$hr == 24; + printf "matched at %d, rem = %s.\n", __LINE__, $$tr if $debug; + return 1; + } elsif ($$tr =~ s#noon(?:\s+|$ )##ix) { + # noon + ($$hr, $$mr, $$sr) = (12, 0, 0); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#midnight(?:\s+|$ )##ix) { + # midnight + ($$hr, $$mr, $$sr) = (0, 0, 0); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_time_offset +{ + my ($tr, $rsr, %options) = @_; + + $$tr =~ s/^\s+//; + + return 0 if $options{NO_RELATIVE}; + + if ($$tr =~ s#^(?xi) + ([-+]?) + \s* + (\d+) + \s* + (sec|second|min|minute|hour)s? + (?: + \s+ + | + $ + ) + ##) { + # count units + $$rsr = 0 unless defined $$rsr; + $$rsr += $umult{"\L$3"} * "$1$2"; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +# +# What to you do with a date that has a two-digit year? +# There's not much that can be done except make a guess. +# +# Some example situations to handle: +# +# now year +# +# 1999 01 +# 1999 71 +# 2010 71 +# 2110 09 +# + +sub expand_two_digit_year +{ + my ($yr, $now, %options) = @_; + + return $yr if $yr > 100; + + my ($y) = (&righttime($now, %options))[5]; + $y += 1900; + my $century = int($y / 100) * 100; + my $within = $y % 100; + + my $r = $yr + $century; + + if ($options{PREFER_PAST}) { + if ($yr > $within) { + $r = $yr + $century - 100; + } + } elsif ($options{PREFER_FUTURE}) { + # being strict here would be silly + if ($yr < $within+10) { + # it's 2019 and the date is '08' + $r = $yr + $century + 100; + } + } elsif ($options{UNAMBIGUOUS}) { + # we really shouldn't guess + return undef; + } else { + # prefer the current century in most cases + + if ($within > 80 && $within - $yr > 60) { + $r = $yr + $century + 100; + } + + if ($within < 30 && $yr - $within > 59) { + $r = $yr + $century - 100; + } + } + print "two digit year '$yr' expanded into $r\n" if $debug; + return $r; +} + + +sub calc +{ + my ($rsr, $yr, $mr, $dr, $rdr, $now, $units, $count, %options) = @_; + + $units = "\L$units"; + + if ($units eq 'day') { + $$rdr = $count; + } elsif ($units eq 'week') { + $$rdr = $count * 7; + } elsif ($umult{$units}) { + $$rsr = $count * $umult{$units}; + } elsif ($units eq 'mon' || $units eq 'month') { + ($$yr, $$mr, $$dr) = &monthoff($now, $count, %options); + $$rsr = 0 unless $$rsr; + } elsif ($units eq 'year') { + ($$yr, $$mr, $$dr) = &monthoff($now, $count * 12, %options); + $$rsr = 0 unless $$rsr; + } else { + carp "interal error"; + } + print "calced rsr $$rsr rdr $$rdr, yr $$yr mr $$mr dr $$dr.\n" if $debug; +} + +sub monthoff +{ + my ($now, $months, %options) = @_; + + # months are 0..11 + my ($d, $m11, $y) = (&righttime($now, %options)) [ 3,4,5 ] ; + + $y += 1900; + + print "m11 = $m11 + $months, y = $y\n" if $debug; + + $m11 += $months; + + print "m11 = $m11, y = $y\n" if $debug; + if ($m11 > 11 || $m11 < 0) { + $y -= 1 if $m11 < 0 && ($m11 % 12 != 0); + $y += int($m11/12); + + # this is required to work around a bug in perl 5.003 + no integer; + $m11 %= 12; + } + print "m11 = $m11, y = $y\n" if $debug; + + # + # What is "1 month from January 31st?" + # I think the answer is February 28th most years. + # + # Similarly, what is one year from February 29th, 1980? + # I think it's February 28th, 1981. + # + # If you disagree, change the following code. + # + if ($d > 30 or ($d > 28 && $m11 == 1)) { + require Time::DaysInMonth; + my $dim = Time::DaysInMonth::days_in($y, $m11+1); + print "dim($y,$m11+1)= $dim\n" if $debug; + $d = $dim if $d > $dim; + } + return ($y, $m11+1, $d); +} + +sub righttime +{ + my ($time, %options) = @_; + if ($options{GMT}) { + return gmtime($time); + } else { + return localtime($time); + } +} + +sub parse_year_only +{ + my ($tr, $yr, $now, %options) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(\d\d\d\d)(?:\s+|$)##) { + $$yr = $1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#\'(\d\d)(?:\s+|$ )##) { + $$yr = expand_two_digit_year($1, $now, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_date_offset +{ + my ($tr, $now, $yr, $mr, $dr, $rdr, $rsr, %options) = @_; + + return 0 if $options{NO_RELATIVE}; + + # now - current seconds_since_epoch + # yr - year return + # mr - month return + # dr - day return + # rdr - relatvie day return + # rsr - relative second return + + my $j; + my $wday = (&righttime($now, %options))[6]; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(?xi) + (?: + (?: + now + \s+ + )? + (\+ | \-) + \s* + )? + (\d+) + \s* + (day|week|month|year)s? + ##) { + my ($one, $two) = ($1, $2); + $one = '' unless defined $one; + $two = '' unless defined $two; + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $3, + "$one$two", %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + \s+ + after + \s+ + next + (?: \s+ | $ ) + ##) { + # Dow "after next" + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} > $wday ? 7 : 14); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + next\s+ + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ ) + ##) { + # "next" Dow + $$rdr = $wdays{"\L$1"} - $wday + + ( $wdays{"\L$1"} > $wday ? 0 : 7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + last\s+ + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ )##) { + # "last" Dow + printf "c %d - %d + ( %d < %d ? 0 : -7 \n", $wdays{"\L$1"}, $wday, $wdays{"\L$1"}, $wday if $debug; + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} < $wday ? 0 : -7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($options{PREFER_PAST} and $$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ )##) { + # Dow + printf "c %d - %d + ( %d < %d ? 0 : -7 \n", $wdays{"\L$1"}, $wday, $wdays{"\L$1"}, $wday if $debug; + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} < $wday ? 0 : -7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($options{PREFER_FUTURE} and $$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ ) + ##) { + # Dow + $$rdr = $wdays{"\L$1"} - $wday + + ( $wdays{"\L$1"} > $wday ? 0 : 7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^today(?:\s+|$ )##xi) { + # today + $$rdr = 0; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^tomorrow(?:\s+|$ )##xi) { + $$rdr = 1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^yesterday(?:\s+|$ )##xi) { + $$rdr = -1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^last\s+(week|month|year)(?:\s+|$ )##xi) { + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $1, -1, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^next\s+(week|month|year)(?:\s+|$ )##xi) { + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $1, 1, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^now (?: \s+ | $ )##x) { + $$rdr = 0; + return 1; + } + return 0; +} + +1; + +__DATA__ + +=head1 NAME + +Time::ParseDate -- date parsing both relative and absolute + +=head1 SYNOPSIS + + use Time::ParseDate; + $seconds_since_jan1_1970 = parsedate("12/11/94 2pm", NO_RELATIVE => 1) + $seconds_since_jan1_1970 = parsedate("12/11/94 2pm", %options) + +=head1 OPTIONS + +Date parsing can also use options. The options are as follows: + + FUZZY -> it's okay not to parse the entire date string + NOW -> the "current" time for relative times (defaults to time()) + ZONE -> local timezone (defaults to $ENV{TZ}) + WHOLE -> the whole input string must be parsed + GMT -> input time is assumed to be GMT, not localtime + UK -> prefer UK style dates (dd/mm over mm/dd) + DATE_REQUIRED -> do not default the date + TIME_REQUIRED -> do not default the time + NO_RELATIVE -> input time is not relative to NOW + TIMEFIRST -> try parsing time before date [not default] + PREFER_PAST -> when year or day of week is ambigueous, assume past + PREFER_FUTURE -> when year or day of week is ambigueous, assume future + SUBSECOND -> parse fraction seconds + VALIDATE -> only accept normal values for HHMMSS, YYMMDD. Otherwise + days like -1 might give the last day of the previous month. + +=head1 DATE FORMATS RECOGNIZED + +=head2 Absolute date formats + + Dow, dd Mon yy + Dow, dd Mon yyyy + Dow, dd Mon + dd Mon yy + dd Mon yyyy + Month day{st,nd,rd,th}, year + Month day{st,nd,rd,th} + Mon dd yyyy + yyyy/mm/dd + yyyy/mm + mm/dd/yy + mm/dd/yyyy + mm/yy + yy/mm (only if year > 12, or > 31 if UK) + yy/mm/dd (only if year > 12 and day < 32, or year > 31 if UK) + dd/mm/yy (only if UK, or an invalid mm/dd/yy or yy/mm/dd) + dd/mm/yyyy (only if UK, or an invalid mm/dd/yyyy) + dd/mm (only if UK, or an invalid mm/dd) + +=head2 Relative date formats: + + count "days" + count "weeks" + count "months" + count "years" + Dow "after next" + Dow (requires PREFER_PAST or PREFER_FUTURE) + "next" Dow + "tomorrow" + "today" + "yesterday" + "last" dow + "last week" + "now" + "now" "+" count units + "now" "-" count units + "+" count units + "-" count units + +=head2 Absolute time formats: + + hh:mm:ss[.ddd] + hh:mm + hh:mm[AP]M + hh[AP]M + hhmmss[[AP]M] + "noon" + "midnight" + +=head2 Relative time formats: + + count "minuts" + count "seconds" + count "hours" + "+" count units + "+" count + "-" count units + "-" count + +=head2 Timezone formats: + + [+-]dddd + GMT[+-]d+ + [+-]dddd (TZN) + TZN + +=head2 Special formats: + + [ d]d/Mon/yyyy:hh:mm:ss [[+-]dddd] + yy/mm/dd.hh:mm + +=head1 DESCRIPTION + +This module recognizes the above date/time formats. Usually a +date and a time are specified. There are numerous options for +controlling what is recognized and what is not. + +The return code is always the time in seconds since January 1st, 1970 +or undef if it was unable to parse the time. + +If a timezone is specified it must be after the time. Year specifications +can be tacked onto the end of absolute times. + +If C<parsedate()> is called from array contect, then it will return two +elements. On sucessful parses, it will return the seconds and what +remains of its input string. On unsucessful parses, it will return +C<undef> and an error string. + +=head1 EXAMPLES + + $seconds = parsedate("Mon Jan 2 04:24:27 1995"); + $seconds = parsedate("Tue Apr 4 00:22:12 PDT 1995"); + $seconds = parsedate("04.04.95 00:22", ZONE => PDT); + $seconds = parsedate("Jan 1 1999 11:23:34.578", SUBSECOND => 1); + $seconds = parsedate("122212 950404", ZONE => PDT, TIMEFIRST => 1); + $seconds = parsedate("+3 secs", NOW => 796978800); + $seconds = parsedate("2 months", NOW => 796720932); + $seconds = parsedate("last Tuesday"); + + ($seconds, $remaining) = parsedate("today is the day"); + ($seconds, $error) = parsedate("today is", WHOLE=>1); + +=head1 AUTHOR + +David Muir Sharnoff <muir@idiom.com>. Copyright (C) 1996-1999 All +Rights Reserved. Use and redistribution allowed at user's own +risk. + diff --git a/cpan/dist/Time-modules/lib/Time/Timezone.pm b/cpan/dist/Time-modules/lib/Time/Timezone.pm new file mode 100644 index 00000000..9a013fd0 --- /dev/null +++ b/cpan/dist/Time-modules/lib/Time/Timezone.pm @@ -0,0 +1,272 @@ + +package Time::Timezone; + +require 5.002; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(tz2zone tz_local_offset tz_offset tz_name); +@EXPORT_OK = qw(); + +use Carp; +use strict; + +# Parts stolen from code by Paul Foley <paul@ascent.com> + +use vars qw($VERSION); + +$VERSION = 99.062401; + +sub tz2zone +{ + my($TZ, $time, $isdst) = @_; + + use vars qw(%tzn_cache); + + $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '' + unless $TZ; + + # Hack to deal with 'PST8PDT' format of TZ + # Note that this can't deal with all the esoteric forms, but it + # does recognize the most common: [:]STDoff[DST[off][,rule]] + + if (! defined $isdst) { + my $j; + $time = time() unless $time; + ($j, $j, $j, $j, $j, $j, $j, $j, $isdst) = localtime($time); + } + + if (defined $tzn_cache{$TZ}->[$isdst]) { + return $tzn_cache{$TZ}->[$isdst]; + } + + if ($TZ =~ /^ + ( [^:\d+\-,] {3,} ) + ( [+-] ? + \d {1,2} + ( : \d {1,2} ) {0,2} + ) + ( [^\d+\-,] {3,} )? + /x + ) { + $TZ = $isdst ? $4 : $1; + $tzn_cache{$TZ} = [ $1, $4 ]; + } else { + $tzn_cache{$TZ} = [ $TZ, $TZ ]; + } + return $TZ; +} + +sub tz_local_offset +{ + my ($time) = @_; + + $time = time() unless $time; + my (@l) = localtime($time); + my $isdst = $l[8] || 0; + + if (@Timezone::tz_local && defined($Timezone::tz_local[$isdst])) { + return $Timezone::tz_local[$isdst]; + } + + $Timezone::tz_local[$isdst] = &calc_off($time); + + return $Timezone::tz_local[$isdst]; +} + +sub calc_off +{ + my ($time) = @_; + + my (@l) = localtime($time); + my (@g) = gmtime($time); + + my $off; + + $off = $l[0] - $g[0] + + ($l[1] - $g[1]) * 60 + + ($l[2] - $g[2]) * 3600; + + # subscript 7 is yday. + + if ($l[7] == $g[7]) { + # done + } elsif ($l[7] == $g[7] + 1) { + $off += 86400; + } elsif ($l[7] == $g[7] - 1) { + $off -= 86400; + } elsif ($l[7] < $g[7]) { + # crossed over a year boundry! + # localtime is beginning of year, gmt is end + # therefore local is ahead + $off += 86400; + } else { + $off -= 86400; + } + + return $off; +} + +# constants +# The rest of the file comes from Graham Barr <bodg@tiuk.ti.com> + +CONFIG: { + use vars qw(%dstZone %zoneOff %dstZoneOff %Zone); + + %dstZone = ( + # "ndt" => -2*3600-1800, # Newfoundland Daylight + "adt" => -3*3600, # Atlantic Daylight + "edt" => -4*3600, # Eastern Daylight + "cdt" => -5*3600, # Central Daylight + "mdt" => -6*3600, # Mountain Daylight + "pdt" => -7*3600, # Pacific Daylight + "ydt" => -8*3600, # Yukon Daylight + "hdt" => -9*3600, # Hawaii Daylight + "bst" => +1*3600, # British Summer + "mest" => +2*3600, # Middle European Summer + "sst" => +2*3600, # Swedish Summer + "fst" => +2*3600, # French Summer + "wadt" => +8*3600, # West Australian Daylight + # "cadt" => +10*3600+1800, # Central Australian Daylight + "eadt" => +11*3600, # Eastern Australian Daylight + "nzdt" => +13*3600, # New Zealand Daylight + ); + + %Zone = ( + "gmt" => 0, # Greenwich Mean + "ut" => 0, # Universal (Coordinated) + "utc" => 0, + "wet" => 0, # Western European + "wat" => -1*3600, # West Africa + "at" => -2*3600, # Azores + # For completeness. BST is also British Summer, and GST is also Guam Standard. + # "bst" => -3*3600, # Brazil Standard + # "gst" => -3*3600, # Greenland Standard + # "nft" => -3*3600-1800,# Newfoundland + # "nst" => -3*3600-1800,# Newfoundland Standard + "ast" => -4*3600, # Atlantic Standard + "est" => -5*3600, # Eastern Standard + "cst" => -6*3600, # Central Standard + "mst" => -7*3600, # Mountain Standard + "pst" => -8*3600, # Pacific Standard + "yst" => -9*3600, # Yukon Standard + "hst" => -10*3600, # Hawaii Standard + "cat" => -10*3600, # Central Alaska + "ahst" => -10*3600, # Alaska-Hawaii Standard + "nt" => -11*3600, # Nome + "idlw" => -12*3600, # International Date Line West + "cet" => +1*3600, # Central European + "met" => +1*3600, # Middle European + "mewt" => +1*3600, # Middle European Winter + "swt" => +1*3600, # Swedish Winter + "fwt" => +1*3600, # French Winter + "eet" => +2*3600, # Eastern Europe, USSR Zone 1 + "bt" => +3*3600, # Baghdad, USSR Zone 2 + # "it" => +3*3600+1800,# Iran + "zp4" => +4*3600, # USSR Zone 3 + "zp5" => +5*3600, # USSR Zone 4 + # "ist" => +5*3600+1800,# Indian Standard + "zp6" => +6*3600, # USSR Zone 5 + # For completeness. NST is also Newfoundland Stanard, and SST is also Swedish Summer. + # "nst" => +6*3600+1800,# North Sumatra + # "sst" => +7*3600, # South Sumatra, USSR Zone 6 + "wast" => +7*3600, # West Australian Standard + # "jt" => +7*3600+1800,# Java (3pm in Cronusland!) + "cct" => +8*3600, # China Coast, USSR Zone 7 + "jst" => +9*3600, # Japan Standard, USSR Zone 8 + # "cast" => +9*3600+1800,# Central Australian Standard + "east" => +10*3600, # Eastern Australian Standard + "gst" => +10*3600, # Guam Standard, USSR Zone 9 + "nzt" => +12*3600, # New Zealand + "nzst" => +12*3600, # New Zealand Standard + "idle" => +12*3600, # International Date Line East + ); + + %zoneOff = reverse(%Zone); + %dstZoneOff = reverse(%dstZone); + + # Preferences + + $zoneOff{0} = 'gmt'; + $dstZoneOff{3600} = 'bst'; + +} + +sub tz_offset +{ + my ($zone, $time) = @_; + + return &tz_local_offset() unless($zone); + + $time = time() unless $time; + my(@l) = localtime($time); + my $dst = $l[8]; + + $zone = lc $zone; + + if ($zone =~ /^([\-\+]\d{3,4})$/) { + my $v = 0 + $1; + return int($v / 100) * 60 + ($v % 100); + } elsif (exists $dstZone{$zone} && ($dst || !exists $Zone{$zone})) { + return $dstZone{$zone}; + } elsif(exists $Zone{$zone}) { + return $Zone{$zone}; + } + undef; +} + +sub tz_name +{ + my ($off, $time) = @_; + + $time = time() unless $time; + my(@l) = localtime($time); + my $dst = $l[8]; + + if (exists $dstZoneOff{$off} && ($dst || !exists $zoneOff{$off})) { + return $dstZoneOff{$off}; + } elsif (exists $zoneOff{$off}) { + return $zoneOff{$off}; + } + sprintf("%+05d", int($off / 60) * 100 + $off % 60); +} + +1; + +__DATA__ + +=head1 NAME + +Time::Timezone -- miscellaneous timezone manipulations routines + +=head1 SYNOPSIS + + use Time::Timezone; + print tz2zone(); + print tz2zone($ENV{'TZ'}); + print tz2zone($ENV{'TZ'}, time()); + print tz2zone($ENV{'TZ'}, undef, $isdst); + $offset = tz_local_offset(); + $offset = tz_offset($TZ); + +=head1 DESCRIPTION + +This is a collection of miscellaneous timezone manipulation routines. + +C<tz2zone()> parses the TZ environment variable and returns a timezone +string suitable for inclusion in L<date>-like output. It opionally takes +a timezone string, a time, and a is-dst flag. + +C<tz_local_offset()> determins the offset from GMT time in seconds. It +only does the calculation once. + +C<tz_offset()> determines the offset from GMT in seconds of a specified +timezone. + +C<tz_name()> determines the name of the timezone based on its offset + +=head1 AUTHORS + +Graham Barr <bodg@tiuk.ti.com> +David Muir Sharnoff <muir@idiom.com> +Paul Foley <paul@ascent.com> diff --git a/cpan/dist/Time-modules/t/datetime.t b/cpan/dist/Time-modules/t/datetime.t new file mode 100755 index 00000000..a2273c1c --- /dev/null +++ b/cpan/dist/Time-modules/t/datetime.t @@ -0,0 +1,416 @@ +#!/usr/local/bin/perl -I. -w + +# David Muir Sharnoff <muir@idiom.com> + +# find out why it died if not running under make + +$debug = 0; + +$Time::ParseDate::debug = $debug; + +BEGIN { + $okat = 12; + $ENV{'LANG'} = 'C'; + $ENV{'TZ'} = 'PST8PDT'; + + %k = ( + '%' => '%', + 'a' => 'Sat', + 'A' => 'Saturday', + 'b' => 'Nov', + 'h' => 'Nov', + 'B' => 'November', + 'c' => "Sat Nov 19 21:05:57 1994", + 'd' => '19', + 'D' => '11/19/94', + 'e' => '19', + 'f' => '.500', + 'F' => '.500000', + 'H' => '21', + 'I' => '09', + 'j' => '323', + 'k' => '21', + 'l' => ' 9', + 'm' => '11', + 'M' => '05', + 'n' => "\n", + 'o' => '19th', + 'p' => "PM", + 'r' => "09:05:57 PM", + 'R' => "21:05", + 'S' => "57", + 't' => "\t", + 'T' => "21:05:57", + 'U' => "46", + 'w' => "6", + 'W' => "46", + 'x' => "11/19/94", + 'y' => "94", + 'Y' => "1994", + 'X' => "21:05:57", + 'Z' => "PST" + ); + + $sdt_start_line = __LINE__+2; + @sdt = ( + 796969332, ['950404 00:22:12 "EDT'], + 796969332, ['950404 00:22:12.500 "EDT'], + 796969332.5, ['950404 00:22:12.500 "EDT', SUBSECOND => 1], + 786437763, ['Fri Dec 2 22:56:03 1994', NOW => 785300000], + 786408963, ['Fri Dec 2 22:56:03 GMT+0 1994', NOW => 785300000], + 786408963, ['Fri Dec 2 22:56:03.500 GMT+0 1994', NOW => 785300000], + 786408963.5, ['Fri Dec 2 22:56:03.500 GMT+0 1994', SUBSECOND => 1, NOW => 785300000], + 786437763, ['Fri Dec 2 22:56:03 GMT-8 1994', NOW => 785300000], + 786437760, ['94/12/02.22:56', NOW => 785300000], + 786437760, ['1994/12/02 10:56Pm', NOW => 785300000], + 786437760, ['1994/12/2 10:56 PM', NOW => 785300000], + 786437760, ['12/02/94 22:56', NOW => 785300000], + 786437760, ['12/2/94 10:56Pm', NOW => 785300000], + 786437760, ['94/12/2 10:56 pm', NOW => 785300000], + 786437763, ['94/12/02 22:56:03', NOW => 785300000], + 786437763, ['94/12/02 22:56:03.500', NOW => 785300000], + 786437763.5, ['94/12/02 22:56:03.500', SUBSECOND => 1, NOW => 785300000], + 786437763, ['94/12/02 10:56:03:500PM', NOW => 785300000], + 786437763.5, ['94/12/02 10:56:03:500PM', SUBSECOND => 1, NOW => 785300000], + 786437760, ['10:56Pm 94/12/02', NOW => 785300000], + 786437763, ['22:56:03 1994/12/02', NOW => 785300000], + 786437763, ['22:56:03.5 1994/12/02', NOW => 785300000], + 786437763.5, ['22:56:03.5 1994/12/02', SUBSECOND => 1, NOW => 785300000], + 786437760, ['22:56 1994/12/2', NOW => 785300000], + 786437760, ['10:56PM 12/02/94', NOW => 785300000], + 786437760, ['10:56 pm 12/2/94', NOW => 785300000], + 786437760, ['22:56 94/12/2', NOW => 785300000], + 786437760, ['10:56Pm 94/12/02', NOW => 785300000], + 796980132, ['Tue Apr 4 00:22:12 PDT 1995'], + 796980132, ['April 4th 1995 12:22:12AM', ZONE => PDT], + 827878812, ['Tue Mar 26 14:20:12 1996'], + 827878812, ['Tue Mar 26 14:20:12 1996', SUBSECOND => 1], + 827878812, ['Tue Mar 26 14:20:12.5 1996'], + 827878812.5, ['Tue Mar 26 14:20:12.5 1996', SUBSECOND => 1], + 827878812, ['Tue Mar 26 14:20:12 GMT-0800 1996'], + 827878812, ['Tue Mar 26 17:20:12 EST 1996'], + 827878812, ['Tue Mar 26 17:20:12 GMT-0500 1996'], + 827878812, ['Tue Mar 26 22:20:12 GMT 1996'], + 827878812, ['Tue Mar 26 22:20:12 +0000 (GMT) 1996'], + 827878812, ['Tue, 26 Mar 22:20:12 +0000 (GMT) 1996'], + 784394917, ['Wed, 9 Nov 1994 7:28:37'], + 784887518, ['Tue, 15 Nov 1994 0:18:38'], + 788058300, ['21 dec 17:05', NOW => 785300000], + 802940400, ['06/12/1995'], + 802940400, ['12/06/1995', UK => 1], + 802940400, ['12/06/95', UK => 1], + 802940400, ['06.12.1995'], + 803026800, ['13/06/1995'], + 803026800, ['13/06/95'], + 784394917, ['Wed, 9 Nov 1994 15:28:37 +0000 (GMT)'], + 827878812, ['Tue Mar 26 23:20:12 GMT+0100 1996'], + 827878812, ['Wed Mar 27 05:20:12 GMT+0700 1996'], + 827878812, ['Wed Mar 27 05:20:12 +0700 1996'], + 827878812, ['Wed Mar 27 05:20:12 +0700 (EST) 1996'], + 796980132, ['1995/04/04 00:22:12 PDT'], + 796720932, ['1995/04 00:22:12 PDT'], + 796980132, ['1995/04/04 00:22:12 PDT'], + 796980132, ['Tue, 4 Apr 95 00:22:12 PDT'], + 796980132, ['Tue 4 Apr 1995 00:22:12 PDT'], + 796980132, ['04 Apr 1995 00:22:12 PDT'], + 796980132, ['4 Apr 1995 00:22:12 PDT'], + 796980132, ['Tue, 04 Apr 00:22:12 PDT', NOW => 796980132], + 796980132, ['Tue 04 Apr 00:22:12 PDT', NOW => 796980132], + 796980132, ['04 Apr 00:22:12 PDT', NOW => 796980132], + 796980132, ['Apr 04 00:22:12 PDT', NOW => 796980132], + 796980132, ['Apr 4 00:22:12 PDT', NOW => 796980132], + 796980132, ['Tue, Apr 4 00:22:12 PDT', NOW => 796980132], + 796980132, ['Apr 4 1995 00:22:12 PDT'], + 796980132, ['April 4th 1995 00:22:12 PDT'], + 796980132, ["April 4th, '95 00:22:12 PDT"], + 796980132, ["April 4th 00:22:12 PDT", NOW => 796980132], + 796980132, ['95/04/04 00:22:12 PDT'], + 796980132, ['04/04/95 00:22:12 PDT'], + 796720932, ['95/04 00:22:12 PDT'], + 796720932, ['04/95 00:22:12 PDT'], + 796980132, ['04/04 00:22:12 PDT', NOW => 796980132], + 796980132, ['040495 00:22:12 PDT'], + 796980132, ['950404 00:22:12 PDT'], + 796969332, ['950404 00:22:12 EDT'], + 796980132, ['04.04.95 00:22:12', ZONE => PDT], + 796980120, ['04.04.95 00:22', ZONE => PDT], + 796978800, ['04.04.95 12AM', ZONE => PDT], + 796978800, ['04.04.95 12am', ZONE => PDT], + 796980120, ['04.04.95 0022', ZONE => PDT], + 796980132, ['04.04.95 12:22:12am', ZONE => PDT], + 797023332, ['950404 122212', ZONE => PDT], + 797023332, ['122212 950404', ZONE => PDT, TIMEFIRST => 1], + 796980120, ['04.04.95 12:22AM', ZONE => PDT], + 796978800, ['95/04/04 midnight', ZONE => PDT], + 796978800, ['95/04/04 Midnight', ZONE => PDT], + 797022000, ['95/04/04 Noon', ZONE => PDT], + 797022000, ['95/04/04 noon', ZONE => PDT], + 797022000, ['95/04/04 12Pm', ZONE => PDT], + 796978803, ['+3 secs', NOW => 796978800], + 796979600, ['+0800 seconds', NOW => 796978800], + 796986000, ['+2 hour', NOW => 796978800], + 796979400, ['+10min', NOW => 796978800], + 796979400, ['+10 minutes', NOW => 796978800], + 797011203, ['95/04/04 +3 secs', ZONE => EDT, NOW => 796935600], + 797062935, ['4 day +3 secs', ZONE => PDT, NOW => 796720932], + 797062935, ['now + 4 days +3 secs', ZONE => PDT, NOW => 796720932], + 797062935, ['now +4 days +3 secs', ZONE => PDT, NOW => 796720932], + 796720932, ['now', ZONE => PDT, NOW => 796720932], + 796720936, ['now +4 secs', ZONE => PDT, NOW => 796720932], + 796735332, ['now +4 hours', ZONE => PDT, NOW => 796720932], + 797062935, ['+4 days +3 secs', ZONE => PDT, NOW => 796720932], + 797062935, ['+ 4 days +3 secs', ZONE => PDT, NOW => 796720932], + 797062929, ['4 day -3 secs', ZONE => PDT, NOW => 796720932], + 796375329, ['-4 day -3 secs', ZONE => PDT, NOW => 796720932], + 796375329, ['now - 4 days -3 secs', ZONE => PDT, NOW => 796720932], + 796375329, ['now -4 days -3 secs', ZONE => PDT, NOW => 796720932], + 796720928, ['now -4 secs', ZONE => PDT, NOW => 796720932], + 796706532, ['now -4 hours', ZONE => PDT, NOW => 796720932], + 796375329, ['-4 days -3 secs', ZONE => PDT, NOW => 796720932], + 796375329, ['- 4 days -3 secs', ZONE => PDT, NOW => 796720932], + 797322132, ['1 week', NOW => 796720932], + 801987732, ['2 month', NOW => 796720932], + 804579732, ['3 months', NOW => 796720932], + 859879332, ['2 years', NOW => 796720932], + 797671332, ['Wed after next', NOW => 796980132], + 797498532, ['next monday', NOW => 796980132], + 797584932, ['next tuesday', NOW => 796980132], + 797066532, ['next wEd', NOW => 796980132], + 796378932, ['last tuesday', NOW => 796980132], + 796465332, ['last wednesday', NOW => 796980132], + 796893732, ['last monday', NOW => 796980132], + 797036400, ['today at 4pm', NOW => 796980132], + 797080932, ['tomorrow +4hours', NOW => 796980132], + 796950000, ['yesterday at 4pm', NOW => 796980132], + 796378932, ['last week', NOW => 796980132], + 794305332, ['last month', NOW => 796980132], + 765444132, ['last year', NOW => 796980132], + 797584932, ['next week', NOW => 796980132], + 799572132, ['next month', NOW => 796980132], + 828606132, ['next year', NOW => 796980132], + 836391600, ['July 3rd, 4:00AM 1996 ', DATE_REQUIRED =>1, TIME_REQUIRED=>1, NO_RELATIVE=>1, NOW=>796980132], + 783718105, ['Tue, 01 Nov 1994 11:28:25 -0800'], + 202779300, ['5:35 pm june 4th CST 1976'], + 236898000, ['5pm EDT 4th july 1977'], + 236898000, ['5pm EDT 4 july 1977'], + 819594300, ['21-dec 17:05', NOW => 796980132], + 788058300, ['21-dec 17:05', NOW => 796980132, PREFER_PAST => 1], + 819594300, ['21-dec 17:05', NOW => 796980132, PREFER_FUTURE => 1], + 793415100, ['21-feb 17:05', NOW => 796980132, PREFER_PAST => 1], + 824951100, ['21-feb 17:05', NOW => 796980132, PREFER_FUTURE => 1], + 819594300, ['21/dec 17:05', NOW => 796980132], + 756522300, ['21/dec/93 17:05'], + 788058300, ['dec 21 1994 17:05'], + 788058300, ['dec 21 94 17:05'], + 788058300, ['dec 21 94 17:05'], + 796465332, ['Wednesday', NOW => 796980132, PREFER_PAST => 1], + 796378932, ['Tuesday', NOW => 796980132, PREFER_PAST => 1], + 796893732, ['Monday', NOW => 796980132, PREFER_PAST => 1], + 797066532, ['Wednesday', NOW => 796980132, PREFER_FUTURE => 1], + 797584932, ['Tuesday', NOW => 796980132, PREFER_FUTURE => 1], + 797498532, ['Monday', NOW => 796980132, PREFER_FUTURE => 1], + 802915200, ['06/12/1995', ZONE => GMT], + 828860438, ['06/Apr/1996:23:00:38 -0800'], + 828860438, ['06/Apr/1996:23:00:38'], + 828943238, ['07/Apr/1996:23:00:38 -0700'], + 828878618, ['07/Apr/1996:12:03:38', ZONE => GMT], + 828856838, ['06/Apr/1996:23:00:38 -0700'], + 828946838, ['07/Apr/1996:23:00:38 -0800'], + 895474800, ['5/18/1998'], + 796980132, ['04/Apr/1995:00:22:12', ZONE => PDT], + 796983732, ['04/Apr/1995:00:22:12 -0800'], + 796983732, ['04/Apr/1995:00:22:12', ZONE => PST], + 202772100, ['5:35 pm june 4th 1976 EDT'], + 796892400, ['04/03', NOW => 796980132, PREFER_PAST => 1], + 765702000, ['04/07', NOW => 796980132, PREFER_PAST => 1], + 883641600, ['1/1/1998', VALIDATE => 1], + 852105600, ['1/1/1997'], + 852105600, ['last year', NOW => 883641600], + 820483200, ['-2 years', NOW => 883641600], + 832402800, ['-2 years', NOW => 895474800], + 891864000, ['+3 days', NOW => 891608400], + 891777600, ['+2 days', NOW => 891608400], + 902938515, ['1998-08-12 12:15:15', ZONE => 'EDT'], + 946684800, ['2000-01-01 00:00:00', ZONE => GMT], + 1262304000, ['2010-01-01 00:00:00', ZONE => GMT], + 757065600, ['12/28/93', NOW => 1262304000], + 1924675200, ['12/28/30', NOW => 1262304000], + 946751430, ['Jan 1 2000 10:30:30AM'], + 946722083, ['Sat Jan 1 02:21:23 2000'], + 0, ['1970/01/01 00:00:00', ZONE => GMT], + 796980132, ['Tue 4 Apr 1995 00:22:12 PDT 8', WHOLE => 0], + 789008700, ['dec 32 94 17:05'], + 796983072, ['1995/04/04 00:71:12 PDT'], + undef, ['1995/04/04 00:71:12 PDT', VALIDATE => 1], + undef, ['38/38/21', VALIDATE => 1], + undef, ['dec 32 94 17:05', VALIDATE => 1], + undef, ['Tue 4 Apr 1995 00:22:12 PDT 8', WHOLE => 1], + undef, ['Tue 4 Apr 199 00:22:12 PDT'], + 1924675200, ['12/28/30', NOW => 1262304000, PREFUR_FUTURE => 1], + 1924675200, ['28/12/30', NOW => 1262304000, PREFUR_FUTURE => 1, UK => 1], + -1578240000, ['12/28/19', NOW => 902938515, PREFER_PAST => 1], + -347155200, ['1959-01-01 00:00:00', ZONE => GMT], + -158083200, ['12/28/64', NOW => 902938515], + -1231084800, ['12/28/30', NOW => 1262304000, PREFER_PAST => 1], + -345600, ['1969-12-28 00:00:00', ZONE => GMT], + -1231084800, ['28/12/30', NOW => 1262304000, PREFER_PAST => 1, UK => 1], + 1577520000, ['12/28/19', NOW => 902938515, PREFER_FUTURE => 1], + 1766908800, ['12/28/25', NOW => 902938515], + ); + + %tztests = ( + "YDT" => -8*3600, # Yukon Daylight + "HDT" => -9*3600, # Hawaii Daylight + "BST" => +1*3600, # British Summer + "MEST" => +2*3600 # Middle European Summer + ); + +} + +use Time::CTime; +use Time::JulianDay; +use Time::ParseDate; +use Time::Local; +use Time::Timezone; + +my $before_big = $okat-1+scalar(keys %k)+scalar(keys %tztests); + +printf "1..%d\n", $before_big + @sdt/2; + +$epoch = ($Time::JulianDay::jd_epoch - 2440588) * 86400 + + $Time::JulianDay::jd_epoch_remainder; +print STDERR "\nEpoch = $epoch\n" if $epoch; + +$etime = 785307957.5 - $epoch; + +@x = localtime(785307957); +@y = gmtime(785307957); +my $hd = $y[2] - $x[2]; +$hd += 24 if $hd < 0; +$hd %= 24; +if ($hd != 8) { + print STDERR "\nIt seems localtime() does not honor \$ENV{TZ} when set in the test script.\nPlease set the TZ environment variable to PST8PDT and rerun.\n"; + print "hd = $hd, x = @x, y = @y\n" if $debug || -t STDOUT; + print "not ok 1\n"; +} else { + print "ok 1\n"; +} + +eval " 1/0; "; # tests a bug in ctime! +$x = ctime($etime); +print $x eq "Sat Nov 19 21:05:57 PST 1994\n" ? "ok 2\n" : "not ok 2\n"; + +print julian_day(1994,11,19) == 2449676 ? "ok 3\n" : "not ok 3\n"; + +@x = inverse_julian_day(2449676); + +print (($x[0] == 1994 and $x[1] == 11 and $x[2] == 19) ? "ok 4\n" : "not ok 4\n"); + +print "ok 5\n"; + +print day_of_week(2449676) == 6 ? "ok 6\n" : "not ok 6\n"; + +$bs = 786439995 - $epoch; + +use vars qw($isdst $wday $yday); +($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime($bs); + +$jdtgm = jd_timegm($sec,$min,$hour,$mday,$mon,$year); +$jdtl = jd_timelocal($sec,$min,$hour,$mday,$mon,$year); +$tltl = timelocal($sec,$min,$hour,$mday,$mon,$year); + +$year += 100 if $year < 70; +$jd = julian_day($year+1900, $mon+1, $mday); +$s = jd_secondsgm($jd, $hour, $min, $sec); +$lo = tz_local_offset($bs); + +print <<"" if $debug; + s = $s + bs = $bs + jdtgm = $jdtgm + jdtl = $jdtl + tltl = $tltl + lo = $lo + +print $s == $bs ? "ok 7\n" : "not ok 7\n"; + +print $jdtgm == $bs ? "ok 8\n" : "not ok 8\n"; + +print $jdtl == $bs+8*3600 ? "ok 9\n" : "not ok 9\n"; + +print $tltl == $bs+8*3600 ? "ok 10\n" : "not ok 10\n"; + +print $lo == - 28800 ? "ok 11\n" : "no ok 11\n"; + +################### make these last... +$c = $okat; + +@lt = localtime($etime); +$lt[0] += ($etime - int($etime)); +foreach $i (sort keys %k) { + $x = strftime("-%$i-", @lt); + print $x eq "-$k{$i}-" ? "ok $c\n" : "not ok $c\n"; + if ($debug && $x ne "-$k{$i}-") { + print "strftime(\"-%$i-\") = $x.\n\tshould be: $k{$i}.\n"; + exit(0); + } + $c++; +} + +foreach $i (keys %tztests) { + $tzo = tz_offset($i,799572132); + print $tzo eq $tztests{$i} ? "ok $c\n" : "not ok $c\n"; + if (($debug || -t STDOUT) && $tzo ne $tztests{$i}) { + print "tz_offset($i) = $tzo != $tztests{$i}\n"; + exit(0); + } + $c++; +} + +while (@sdt) { + $es = shift(@sdt); + $es -= $epoch if defined($es); + $ar = shift(@sdt); + $toparse = shift(@$ar); + %opts = @$ar; + if (defined $opts{NOW}) { + $opts{NOW} -= $epoch; + } + $opts{WHOLE} = 1 unless defined $opts{WHOLE}; + $s = parsedate($toparse, %opts); + if (! defined($es) && ! defined($s)) { + print "ok $c\n"; + } elsif ($es == $s) { + print "ok $c\n"; + } else { + print "not ok $c\n"; + if (-t STDOUT || $debug) { + if (defined($es)) { + print strftime("Expected($es): %c %Z\n", localtime($es)); + } else { + print "Expected undef\n"; + } + + print strftime("\tGot($s): %c %Z", localtime($s)); + print strftime(" (%m/%d %I:%M %p GMT)\n", gmtime($s)); + print "\tInput: $toparse\n"; + for my $zk (keys %opts) { + my $zv = $opts{$zk}; + if ($zk eq 'NOW') { + print strftime("\t\tNOW => %c %Z\n", localtime($zv)); + } else { + print "\t\t$zk => $zv\n"; + } + } + if (-t STDOUT) { + print "The parse...\n"; + $Time::ParseDate::debug = 1; + &parsedate($toparse, %opts); + printf "Test that failed was on line %d\n", + $c-$before_big+$sdt_start_line-1; + exit(0); + } + } + } + $c++; +} diff --git a/cpan/lib/Time/CTime.pm b/cpan/lib/Time/CTime.pm new file mode 100644 index 00000000..530a6094 --- /dev/null +++ b/cpan/lib/Time/CTime.pm @@ -0,0 +1,197 @@ +package Time::CTime; + + +require 5.000; + +use Time::Timezone; +use Time::CTime; +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(ctime asctime strftime); +@EXPORT_OK = qw(asctime_n ctime_n @DoW @MoY @DayOfWeek @MonthOfYear); + +use strict; + +# constants +use vars qw(@DoW @DayOfWeek @MoY @MonthOfYear %strftime_conversion $VERSION); +use vars qw($template $sec $min $hour $mday $mon $year $wday $yday $isdst); + +$VERSION = 99.06_22_01; + +CONFIG: { + @DoW = qw(Sun Mon Tue Wed Thu Fri Sat); + @DayOfWeek = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday); + @MoY = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + @MonthOfYear = qw(January February March April May June + July August September October November December); + + %strftime_conversion = ( + '%', sub { '%' }, + 'a', sub { $DoW[$wday] }, + 'A', sub { $DayOfWeek[$wday] }, + 'b', sub { $MoY[$mon] }, + 'B', sub { $MonthOfYear[$mon] }, + 'c', sub { asctime_n($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, "") }, + 'd', sub { sprintf("%02d", $mday); }, + 'D', sub { sprintf("%02d/%02d/%02d", $mon+1, $mday, $year%100) }, + 'e', sub { sprintf("%2d", $mday); }, + 'f', sub { fracprintf ("%3.3f", $sec); }, + 'F', sub { fracprintf ("%6.6f", $sec); }, + 'h', sub { $MoY[$mon] }, + 'H', sub { sprintf("%02d", $hour) }, + 'I', sub { sprintf("%02d", $hour % 12 || 12) }, + 'j', sub { sprintf("%03d", $yday + 1) }, + 'k', sub { sprintf("%2d", $hour); }, + 'l', sub { sprintf("%2d", $hour % 12 || 12) }, + 'm', sub { sprintf("%02d", $mon+1); }, + 'M', sub { sprintf("%02d", $min) }, + 'n', sub { "\n" }, + 'o', sub { sprintf("%d%s", $mday, (($mday < 20 && $mday > 3) ? 'th' : ($mday%10 == 1 ? "st" : ($mday%10 == 2 ? "nd" : ($mday%10 == 3 ? "rd" : "th"))))) }, + 'p', sub { $hour > 11 ? "PM" : "AM" }, + 'r', sub { sprintf("%02d:%02d:%02d %s", $hour % 12 || 12, $min, $sec, $hour > 11 ? 'PM' : 'AM') }, + 'R', sub { sprintf("%02d:%02d", $hour, $min) }, + 'S', sub { sprintf("%02d", $sec) }, + 't', sub { "\t" }, + 'T', sub { sprintf("%02d:%02d:%02d", $hour, $min, $sec) }, + 'U', sub { wkyr(0, $wday, $yday) }, + 'w', sub { $wday }, + 'W', sub { wkyr(1, $wday, $yday) }, + 'y', sub { sprintf("%02d",$year%100) }, + 'Y', sub { $year + 1900 }, + 'x', sub { sprintf("%02d/%02d/%02d", $mon + 1, $mday, $year%100) }, + 'X', sub { sprintf("%02d:%02d:%02d", $hour, $min, $sec) }, + 'Z', sub { &tz2zone(undef,undef,$isdst) } + ); + + +} + +sub fracprintf { + my($t,$s) = @_; + my($p) = sprintf($t, $s-int($s)); + $p=~s/^0+//; + $p; +} + +sub asctime_n { + my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $TZname) = @_; + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $TZname) = localtime($sec) unless defined $min; + $year += 1900; + $TZname .= ' ' + if $TZname; + sprintf("%s %s %2d %2d:%02d:%02d %s%4d", + $DoW[$wday], $MoY[$mon], $mday, $hour, $min, $sec, $TZname, $year); +} + +sub asctime +{ + return asctime_n(@_)."\n"; +} + +# is this formula right? +sub wkyr { + my($wstart, $wday, $yday) = @_; + $wday = ($wday + 7 - $wstart) % 7; + return int(($yday - $wday + 13) / 7 - 1); +} + +# ctime($time) + +sub ctime { + my($time) = @_; + asctime(localtime($time), &tz2zone(undef,$time)); +} + +sub ctime_n { + my($time) = @_; + asctime_n(localtime($time), &tz2zone(undef,$time)); +} + +# strftime($template, @time_struct) +# +# Does not support locales + +sub strftime { + local ($template, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = @_; + + undef $@; + $template =~ s/%([%aAbBcdDefFhHIjklmMnopQrRStTUwWxXyYZ])/&{$Time::CTime::strftime_conversion{$1}}()/egs; + die $@ if $@; + return $template; +} + +1; + +__DATA__ + +=head1 NAME + +Time::CTime -- format times ala POSIX asctime + +=head1 SYNOPSIS + + use Time::CTime + print ctime(time); + print asctime(localtime(time)); + print strftime(template, localtime(time)); + +=head2 strftime conversions + + %% PERCENT + %a day of the week abbr + %A day of the week + %b month abbr + %B month + %c ctime format: Sat Nov 19 21:05:57 1994 + %d DD + %D MM/DD/YY + %e numeric day of the month + %f floating point seconds (milliseconds): .314 + %F floating point seconds (microseconds): .314159 + %h month abbr + %H hour, 24 hour clock, leading 0's) + %I hour, 12 hour clock, leading 0's) + %j day of the year + %k hour + %l hour, 12 hour clock + %m month number, starting with 1 + %M minute, leading 0's + %n NEWLINE + %o ornate day of month -- "1st", "2nd", "25th", etc. + %p AM or PM + %r time format: 09:05:57 PM + %R time format: 21:05 + %S seconds, leading 0's + %t TAB + %T time format: 21:05:57 + %U week number, Sunday as first day of week + %w day of the week, numerically, Sunday == 0 + %W week number, Monday as first day of week + %x date format: 11/19/94 + %X time format: 21:05:57 + %y year (2 digits) + %Y year (4 digits) + %Z timezone in ascii. eg: PST + +=head1 DESCRIPTION + +This module provides routines to format dates. They correspond +to the libc routines. &strftime() supports a pretty good set of +coversions -- more than most C libraries. + +strftime supports a pretty good set of conversions. + +The POSIX module has very similar functionality. You should consider +using it instead if you do not have allergic reactions to system +libraries. + +=head1 GENESIS + +Written by David Muir Sharnoff <muir@idiom.com>. + +The starting point for this package was a posting by +Paul Foley <paul@ascent.com> + +Copyright (C) 1996-1999 David Muir Sharnoff. All Rights Reserved. +Use and redistribution allowed at user's own risk. + diff --git a/cpan/lib/Time/DaysInMonth.pm b/cpan/lib/Time/DaysInMonth.pm new file mode 100644 index 00000000..2139f2f8 --- /dev/null +++ b/cpan/lib/Time/DaysInMonth.pm @@ -0,0 +1,75 @@ +package Time::DaysInMonth; + +use Carp; + +require 5.000; + +@ISA = qw(Exporter); +@EXPORT = qw(days_in is_leap); +@EXPORT_OK = qw(%mltable); + +use strict; + +use vars qw($VERSION %mltable); + +$VERSION = 96.032702; + +CONFIG: { + %mltable = qw( + 1 31 + 3 31 + 4 30 + 5 31 + 6 30 + 7 31 + 8 31 + 9 30 + 10 31 + 11 30 + 12 31); +} + +sub days_in +{ + # Month is 1..12 + my ($year, $month) = @_; + return $mltable{$month+0} unless $month == 2; + return 28 unless &is_leap($year); + return 29; +} + +sub is_leap +{ + my ($year) = @_; + return 0 unless $year % 4 == 0; + return 1 unless $year % 100 == 0; + return 0 unless $year % 400 == 0; + return 1; +} + +1; + +__DATA__ + +=head1 NAME + +Time::DaysInMonth -- simply report the number of days in a month + +=head1 SYNOPSIS + + use Time::DaysInMonth; + $days = days_in($year, $month_1_to_12); + $leapyear = is_leap($year); + +=head1 DESCRIPTION + +DaysInMonth is simply a package to report the number of days in +a month. That's all it does. Really! + +=head1 AUTHOR + +David Muir Sharnoff <muir@idiom.com> + +Copyright (C) 1996-1999 David Muir Sharnoff. All Rights Reserved. +Use and redistribution allowed at user's own risk. + diff --git a/cpan/lib/Time/JulianDay.pm b/cpan/lib/Time/JulianDay.pm new file mode 100644 index 00000000..1b011da1 --- /dev/null +++ b/cpan/lib/Time/JulianDay.pm @@ -0,0 +1,200 @@ +package Time::JulianDay; + +require 5.000; + +use Carp; +use Time::Timezone; + +@ISA = qw(Exporter); +@EXPORT = qw(julian_day inverse_julian_day day_of_week + jd_secondsgm jd_secondslocal + jd_timegm jd_timelocal + gm_julian_day local_julian_day + ); +@EXPORT_OK = qw($brit_jd); + +use strict; +use integer; + +# constants +use vars qw($brit_jd $jd_epoch $jd_epoch_remainder $VERSION); + +$VERSION = 99.061501; + +# calculate the julian day, given $year, $month and $day +sub julian_day +{ + my($year, $month, $day) = @_; + my($tmp); + my($secs); + + use Carp; +# confess() unless defined $day; + + $tmp = $day - 32075 + + 1461 * ( $year + 4800 - ( 14 - $month ) / 12 )/4 + + 367 * ( $month - 2 + ( ( 14 - $month ) / 12 ) * 12 ) / 12 + - 3 * ( ( $year + 4900 - ( 14 - $month ) / 12 ) / 100 ) / 4 + ; + + return($tmp); + +} + +sub gm_julian_day +{ + my($secs) = @_; + my($sec, $min, $hour, $mon, $year, $day, $month); + ($sec, $min, $hour, $day, $mon, $year) = gmtime($secs); + $month = $mon + 1; + $year += 1900; + return julian_day($year, $month, $day) +} + +sub local_julian_day +{ + my($secs) = @_; + my($sec, $min, $hour, $mon, $year, $day, $month); + ($sec, $min, $hour, $day, $mon, $year) = localtime($secs); + $month = $mon + 1; + $year += 1900; + return julian_day($year, $month, $day) +} + +sub day_of_week +{ + my ($jd) = @_; + return (($jd + 1) % 7); # calculate weekday (0=Sun,6=Sat) +} + + +# The following defines the first day that the Gregorian calendar was used +# in the British Empire (Sep 14, 1752). The previous day was Sep 2, 1752 +# by the Julian Calendar. The year began at March 25th before this date. + +$brit_jd = 2361222; + +# Usage: ($year,$month,$day) = &inverse_julian_day($julian_day) +sub inverse_julian_day +{ + my($jd) = @_; + my($jdate_tmp); + my($m,$d,$y); + + carp("warning: julian date $jd pre-dates British use of Gregorian calendar\n") + if ($jd < $brit_jd); + + $jdate_tmp = $jd - 1721119; + $y = (4 * $jdate_tmp - 1)/146097; + $jdate_tmp = 4 * $jdate_tmp - 1 - 146097 * $y; + $d = $jdate_tmp/4; + $jdate_tmp = (4 * $d + 3)/1461; + $d = 4 * $d + 3 - 1461 * $jdate_tmp; + $d = ($d + 4)/4; + $m = (5 * $d - 3)/153; + $d = 5 * $d - 3 - 153 * $m; + $d = ($d + 5) / 5; + $y = 100 * $y + $jdate_tmp; + if($m < 10) { + $m += 3; + } else { + $m -= 9; + ++$y; + } + return ($y, $m, $d); +} + +{ + my($sec, $min, $hour, $day, $mon, $year) = gmtime(0); + $year += 1900; + if ($year == 1970 && $mon == 0 && $day == 1) { + # standard unix time format + $jd_epoch = 2440588; + } else { + $jd_epoch = julian_day($year, $mon+1, $day); + } + $jd_epoch_remainder = $hour*3600 + $min*60 + $sec; +} + +sub jd_secondsgm +{ + my($jd, $hr, $min, $sec) = @_; + + my($r) = (($jd - $jd_epoch) * 86400 + + $hr * 3600 + $min * 60 + - $jd_epoch_remainder); + + no integer; + return ($r + $sec); + use integer; +} + +sub jd_secondslocal +{ + my($jd, $hr, $min, $sec) = @_; + my $jds = jd_secondsgm($jd, $hr, $min, $sec); + return $jds - tz_local_offset($jds); +} + +# this uses a 0-11 month to correctly reverse localtime() +sub jd_timelocal +{ + my ($sec,$min,$hours,$mday,$mon,$year) = @_; + $year += 1900 unless $year > 1000; + my $jd = julian_day($year, $mon+1, $mday); + my $jds = jd_secondsgm($jd, $hours, $min, $sec); + return $jds - tz_local_offset($jds); +} + +# this uses a 0-11 month to correctly reverse gmtime() +sub jd_timegm +{ + my ($sec,$min,$hours,$mday,$mon,$year) = @_; + $year += 1900 unless $year > 1000; + my $jd = julian_day($year, $mon+1, $mday); + return jd_secondsgm($jd, $hours, $min, $sec); +} + +1; + +__DATA__ + +=head1 NAME + +Time::JulianDay -- Julian calendar manipulations + +=head1 SYNOPSIS + + use Time::JulianDay + + $jd = julian_day($year, $month_1_to_12, $day) + $jd = local_julian_day($seconds_since_1970); + $jd = gm_julian_day($seconds_since_1970); + ($year, $month_1_to_12, $day) = inverse_julian_day($jd) + $dow = day_of_week($jd) + + print (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$dow]; + + $seconds_since_jan_1_1970 = jd_secondslocal($jd, $hour, $min, $sec) + $seconds_since_jan_1_1970 = jd_secondsgm($jd, $hour, $min, $sec) + $seconds_since_jan_1_1970 = jd_timelocal($sec,$min,$hours,$mday,$month_0_to_11,$year) + $seconds_since_jan_1_1970 = jd_timegm($sec,$min,$hours,$mday,$month_0_to_11,$year) + +=head1 DESCRIPTION + +JulianDay is a package that manipulates dates as number of days since +some time a long time ago. It's easy to add and subtract time +using julian days... + +The day_of_week returned by day_of_week() is 0 for Sunday, and 6 for +Saturday and everything else is in between. + +=head1 GENESIS + +Written by David Muir Sharnoff <muir@idiom.com> with help from +previous work by +Kurt Jaeger aka PI <zrzr0111@helpdesk.rus.uni-stuttgart.de> + based on postings from: Ian Miller <ian_m@cix.compulink.co.uk>; +Gary Puckering <garyp%cognos.uucp@uunet.uu.net> + based on Collected Algorithms of the ACM ?; +and the unknown-to-me author of Time::Local. diff --git a/cpan/lib/Time/ParseDate.pm b/cpan/lib/Time/ParseDate.pm new file mode 100644 index 00000000..8f1c18fb --- /dev/null +++ b/cpan/lib/Time/ParseDate.pm @@ -0,0 +1,1194 @@ + +package Time::ParseDate; + +require 5.000; + +use Carp; +use Time::Timezone; +use Time::JulianDay; +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(parsedate); +@EXPORT_OK = qw(pd_raw %mtable %umult %wdays); + +use strict; + +# constants +use vars qw(%mtable %umult %wdays $VERSION); + +$VERSION = 100.01_03_01; + +# globals +use vars qw($debug); + +# dynamically-scoped +use vars qw($parse); + +my %mtable; +my %umult; +my %wdays; +my $y2k; + +CONFIG: { + + %mtable = qw( + Jan 1 Jan. 1 January 1 + Feb 2 Feb. 2 February 2 + Mar 3 Mar. 3 March 3 + Apr 4 Apr. 4 April 4 + May 5 + Jun 6 Jun. 6 June 6 + Jul 7 Jul. 7 July 7 + Aug 8 Aug. 8 August 8 + Sep 9 Sep. 9 September 9 + Oct 10 Oct. 10 October 10 + Nov 11 Nov. 11 November 11 + Dec 12 Dec. 12 December 12 ); + %umult = qw( + sec 1 second 1 + min 60 minute 60 + hour 3600 + day 86400 + week 604800 ); + %wdays = qw( + sun 0 sunday 0 + mon 1 monday 1 + tue 2 tuesday 2 + wed 3 wednesday 3 + thu 4 thursday 4 + fri 5 friday 5 + sat 6 saturday 6 + ); + + $y2k = 946684800; # turn of the century +} + +sub parsedate +{ + my ($t, %options) = @_; + + my ($y, $m, $d); # year, month - 1..12, day + my ($H, $M, $S); # hour, minute, second + my $tz; # timezone + my $tzo; # timezone offset + my ($rd, $rs); # relative days, relative seconds + + my $rel; # time&|date is relative + + my $isspec; + my $now = $options{NOW} || time; + my $passes = 0; + my $uk = defined($options{UK}) ? $options{UK} : 0; + + local $parse = ''; # will be dynamically scoped. + + if ($t =~ s#^ ([ \d]\d) + / (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) + / (\d\d\d\d) + : (\d\d) + : (\d\d) + : (\d\d) + (?: + [ ] + ([-+] \d\d\d\d) + (?: \("?(?:(?:[A-Z]{1,4}[TCW56])|IDLE)\))? + )? + ##xi) { #"emacs + # [ \d]/Mon/yyyy:hh:mm:ss [-+]\d\d\d\d + # This is the format for www server logging. + + ($d, $m, $y, $H, $M, $S, $tzo) = ($1, $mtable{"\u\L$2"}, $3, $4, $5, $6, $7 ? &mkoff($7) : ($tzo || undef)); + $parse .= " ".__LINE__ if $debug; + } elsif ($t =~ s#^(\d\d)/(\d\d)/(\d\d)\.(\d\d)\:(\d\d)(\s+|$)##) { + # yy/mm/dd.hh:mm + # I support this format because it's used by wbak/rbak + # on Apollo Domain OS. Silly, but historical. + + ($y, $m, $d, $H, $M, $S) = ($1, $2, $3, $4, $5, 0); + $parse .= " ".__LINE__ if $debug; + } else { + while(1) { + if (! defined $m and ! defined $rd and ! defined $y + and ! ($passes == 0 and $options{'TIMEFIRST'})) + { + # no month defined. + if (&parse_date_only(\$t, \$y, \$m, \$d, $uk)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $H and ! defined $rs) { + if (&parse_time_only(\$t, \$H, \$M, \$S, + \$tz, %options)) + { + $parse .= " ".__LINE__ if $debug; + next; + } + } + next if $passes == 0 and $options{'TIMEFIRST'}; + if (! defined $y) { + if (&parse_year_only(\$t, \$y, $now, %options)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $tz and ! defined $tzo and ! defined $rs + and (defined $m or defined $H)) + { + if (&parse_tz_only(\$t, \$tz, \$tzo)) { + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $H and ! defined $rs) { + if (&parse_time_offset(\$t, \$rs, %options)) { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (! defined $m and ! defined $rd and ! defined $y) { + if (&parse_date_offset(\$t, $now, \$y, + \$m, \$d, \$rd, \$rs, %options)) + { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + if (defined $M or defined $rd) { + if ($t =~ s/^\s*(?:at|\+)\s*(\s+|$)//x) { + $rel = 1; + $parse .= " ".__LINE__ if $debug; + next; + } + } + last; + } continue { + print "context$parse remaider = $t.\n" if $debug; + $passes++; + } + + if ($passes == 0) { + print "nothing matched\n" if $debug; + return (undef, "no match on time/date") + if wantarray(); + return undef; + } + } + + if ($debug) { + print "t: $t.\n"; + print defined($tz) ? "tz: $tz.\n" : "no tz\n"; + print defined($tzo) ? "tzo: $tzo.\n" : "no tzo\n"; + print "HMS: "; + print defined($H) ? "$H, " : "no H, "; + print defined($M) ? "$M, " : "no M, "; + print defined($S) ? "$S\n" : "no S.\n"; + print "mdy: "; + print defined($m) ? "$m, " : "no m, "; + print defined($d) ? "$d, " : "no d, "; + print defined($y) ? "$y\n" : "no y.\n"; + print defined($rs) ? "rs: $rs.\n" : "no rs\n"; + print defined($rd) ? "rd: $rd.\n" : "no rd\n"; + print "parse:$parse\n"; + print "passes: $passes\n"; + } + + $t =~ s/^\s+//; + + if ($t ne '') { + # we didn't manage to eat the string + print "NOT WHOLE\n" if $debug; + return undef if $options{WHOLE}; + return (undef, "characters left over after parse") + if wantarray(); + } + + # define a date if there isn't one already + + if (! defined $y and ! defined $m and ! defined $rd) { + print "no date defined, trying to find one." if $debug; + if (defined $rs or defined $H) { + # we do have a time. + return (undef, "no date specified") + if wantarray(); + return undef if $options{DATE_REQUIRED}; + if (defined $rs) { + print "simple offset: $rs\n" if $debug; + my $rv = $now + $rs; + return ($rv, $t) if wantarray(); + return $rv; + } + $rd = 0; + } else { + print "no time either!\n" if $debug; + return (undef, "no time specified") + if wantarray(); + return undef; + } + } + + if ($options{TIME_REQUIRED} && ! defined($rs) + && ! defined($H) && ! defined($rd)) + { + return (undef, "no time found") + if wantarray(); + return undef; + } + + my $secs; + my $jd; + + if (defined $rd) { + if (defined $rs || ! (defined($H) || defined($M) || defined($S))) { + print "fully relative\n" if $debug; + my ($j, $in, $it); + my $definedrs = defined($rs) ? $rs : 0; + my ($isdst_now, $isdst_then); + my $r = $now + $rd * 86400 + $definedrs; + # + # It's possible that there was a timezone shift + # during the time specified. If so, keep the + # hours the "same". + # + $isdst_now = (localtime($r))[8]; + $isdst_then = (localtime($now))[8]; + if (($isdst_now == $isdst_then) || $options{GMT}) + { + return ($r, $t) if wantarray(); + return $r + } + + print "localtime changed DST during time period!\n" if $debug; + } + + print "relative date\n" if $debug; + $jd = local_julian_day($now); + print "jd($now) = $jd\n" if $debug; + $jd += $rd; + } else { + unless (defined $y) { + if ($options{PREFER_PAST}) { + my ($day, $mon011); + ($day, $mon011, $y) = (&righttime($now))[3,4,5]; + + print "calc year -past $day-$d $mon011-$m $y\n" if $debug; + $y -= 1 if ($mon011+1 < $m) || + (($mon011+1 == $m) && ($day < $d)); + } elsif ($options{PREFER_FUTURE}) { + print "calc year -future\n" if $debug; + my ($day, $mon011); + ($day, $mon011, $y) = (&righttime($now))[3,4,5]; + $y += 1 if ($mon011 >= $m) || + (($mon011+1 == $m) && ($day > $d)); + } else { + print "calc year -this\n" if $debug; + $y = (localtime($now))[5]; + } + $y += 1900; + } + + $y = expand_two_digit_year($y, $now, %options) + if $y < 100; + + if ($options{VALIDATE}) { + require Time::DaysInMonth; + my $dim = Time::DaysInMonth::days_in($y, $m); + if ($y < 1000 or $m < 1 or $d < 1 + or $y > 9999 or $m > 12 or $d > $dim) + { + return (undef, "illegal YMD: $y, $m, $d") + if wantarray(); + return undef; + } + } + $jd = julian_day($y, $m, $d); + print "jd($y, $m, $d) = $jd\n" if $debug; + } + + # put time into HMS + + if (! defined($H)) { + if (defined($rd) || defined($rs)) { + ($S, $M, $H) = &righttime($now, %options); + print "HMS set to $H $M $S\n" if $debug; + } + } + + my $carry; + + print "before ", (defined($rs) ? "$rs" : ""), + " $jd $H $M $S\n" + if $debug; + # + # add in relative seconds. Do it this way because we want to + # preserve the localtime across DST changes. + # + + $S = 0 unless $S; # -w + $M = 0 unless $M; # -w + $H = 0 unless $H; # -w + + if ($options{VALIDATE} and + ($S < 0 or $M < 0 or $H < 0 or $S > 59 or $M > 59 or $H > 23)) + { + return (undef, "illegal HMS: $H, $M, $S") if wantarray(); + return undef; + } + + $S += $rs if defined $rs; + $carry = int($S / 60); + my($frac) = $S - int($S); + $S = int($S); + $S %= 60; + $S += $frac; + $M += $carry; + $carry = int($M / 60); + $M %= 60; + $H += $carry; + $carry = int($H / 24); + $H %= 24; + $jd += $carry; + + print "after rs $jd $H $M $S\n" if $debug; + + $secs = jd_secondsgm($jd, $H, $M, $S); + print "jd_secondsgm($jd, $H, $M, $S) = $secs\n" if $debug; + + # + # If we see something link 3pm CST then and we want to end + # up with a GMT seconds, then we convert the 3pm to GMT and + # subtract in the offset for CST. We subtract because we + # are converting from CST to GMT. + # + my $tzadj; + if ($tz) { + $tzadj = tz_offset($tz, $secs); + print "adjusting secs for $tz: $tzadj\n" if $debug; + $tzadj = tz_offset($tz, $secs-$tzadj); + $secs -= $tzadj; + } elsif (defined $tzo) { + print "adjusting time for offset: $tzo\n" if $debug; + $secs -= $tzo; + } else { + unless ($options{GMT}) { + if ($options{ZONE}) { + $tzadj = tz_offset($options{ZONE}, $secs); + $tzadj = tz_offset($options{ZONE}, $secs-$tzadj); + print "adjusting secs for $options{ZONE}: $tzadj\n" if $debug; + $secs -= $tzadj; + } else { + $tzadj = tz_local_offset($secs); + print "adjusting secs for local offset: $tzadj\n" if $debug; + # + # Just in case we are very close to a time + # change... + # + $tzadj = tz_local_offset($secs-$tzadj); + $secs -= $tzadj; + } + } + } + + print "returning $secs.\n" if $debug; + + return ($secs, $t) if wantarray(); + return $secs; +} + + +sub mkoff +{ + my($offset) = @_; + + if (defined $offset and $offset =~ s#^([-+])(\d\d)(\d\d)$##) { + return ($1 eq '+' ? + 3600 * $2 + 60 * $3 + : -3600 * $2 + -60 * $3 ); + } + return undef; +} + +sub parse_tz_only +{ + my($tr, $tz, $tzo) = @_; + + $$tr =~ s#^\s+##; + my $o; + + if ($$tr =~ s#^ + ([-+]\d\d\d\d) + \s+ + \( + "? + (?: + (?: + [A-Z]{1,4}[TCW56] + ) + | + IDLE + ) + \) + (?: + \s+ + | + $ + ) + ##x) { #"emacs + $$tzo = &mkoff($1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^GMT\s*([-+]\d{1,2})(\s+|$)##x) { + $o = $1; + if ($o <= 24 and $o !~ /^0/) { + # probably hours. + printf "adjusted at %d. ($o 00)\n", __LINE__ if $debug; + $o = "${o}00"; + } + $o =~ s/\b(\d\d\d)/0$1/; + $$tzo = &mkoff($o); + printf "matched at %d. ($$tzo, $o)\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?:GMT\s*)?([-+]\d\d\d\d)(\s+|$)##x) { + $o = $1; + $$tzo = &mkoff($o); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^"?((?:[A-Z]{1,4}[TCW56])|IDLE)(?:\s+|$ )##x) { #" + $$tz = $1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_date_only +{ + my ($tr, $yr, $mr, $dr, $uk) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(\d\d\d\d)([-./])(\d\d?)\2(\d\d?)(\s+|$)##) { + # yyyy/mm/dd + + ($$yr, $$mr, $$dr) = ($1, $3, $4); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)([-./])(\d\d?)\2(\d\d\d\d?)(\s+|$)##) { + # mm/dd/yyyy - is this safe? No. + # -- or dd/mm/yyyy! If $1>12, then it's umabiguous. + # Otherwise check option UK for UK style date. + if ($uk || $1>12) { + ($$yr, $$mr, $$dr) = ($4, $3, $1); + } else { + ($$yr, $$mr, $$dr) = ($4, $1, $3); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d\d\d)/(\d\d?)(?:\s|$ )##x) { + # yyyy/mm + + ($$yr, $$mr, $$dr) = ($1, $2, 1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (?: + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),? + \s+ + )? + (\d\d?) + (\s+ | - | \. | /) + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\.? + (?: + \2 + (\d\d (?:\d\d)? ) + )? + (?: + \s+ + | + $ + ) + ##) { + # [Dow,] dd Mon [yy[yy]] + ($$yr, $$mr, $$dr) = ($4, $mtable{"\u\L$3"}, $1); + + printf "%d: %s - %s - %s\n", __LINE__, $1, $2, $3 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + return 1; + } elsif ($$tr =~ s#^(?xi) + (?: + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),? + \s+ + )? + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\.? + ((\s)+ | - | \. | /) + + (\d\d?) + (?: + (?: \2|\3+) + (\d\d (?: \d\d)?) + )? + (?: + \s+ + | + $ + ) + ##) { + # [Dow,] Mon dd [yyyy] + ($$yr, $$mr, $$dr) = ($5, $mtable{"\u\L$1"}, $4); + printf "%d: %s - %s - %s\n", __LINE__, $1, $2, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + return 1; + } elsif ($$tr =~ s#^(?xi) + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + \s+ + (\d+) + (?:st|nd|rd|th)? + \,? + (?: + \s+ + (?: + (\d\d\d\d) + |(?:\' (\d\d)) + ) + )? + (?: + \s+ + | + $ + ) + ##) { + # Month day{st,nd,rd,th}, 'yy + # Month day{st,nd,rd,th}, year + ($$yr, $$mr, $$dr) = ($3 || $4, $mtable{"\u\L$1"}, $2); + printf "%d: %s - %s - %s - %s\n", __LINE__, $1, $2, $3, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)([-/.])(\d\d?)\2(\d\d?)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12 && $4 < 32)) { + # yy/mm/dd + ($$yr, $$mr, $$dr) = ($1, $3, $4); + } elsif ($1 > 12 || $uk) { + # dd/mm/yy + ($$yr, $$mr, $$dr) = ($4, $3, $1); + } else { + # mm/dd/yy + ($$yr, $$mr, $$dr) = ($4, $1, $3); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d?)/(\d\d?)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12)) { + # yy/mm + ($$yr, $$mr, $$dr) = ($1, $2, 1); + } elsif ($2 > 31 || ($uk && $2 > 12)) { + # mm/yy + ($$yr, $$mr, $$dr) = ($2, $1, 1); + } elsif ($1 > 12 || $uk) { + # dd/mm + ($$mr, $$dr) = ($2, $1); + } else { + # mm/dd + ($$mr, $$dr) = ($1, $2); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(\d\d)(\d\d)(\d\d)(\s+|$)##x) { + if ($1 > 31 || (!$uk && $1 > 12)) { + # YYMMDD + ($$yr, $$mr, $$dr) = ($1, $2, $3); + } elsif ($1 > 12 || $uk) { + # DDMMYY + ($$yr, $$mr, $$dr) = ($3, $2, $1); + } else { + # MMDDYY + ($$yr, $$mr, $$dr) = ($3, $1, $2); + } + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (\d{1,2}) + (\s+ | - | \. | /) + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + (?: + \2 + ( + \d\d + (?:\d\d)? + ) + ) + (:? + \s+ + | + $ + ) + ##) { + # dd Month [yr] + ($$yr, $$mr, $$dr) = ($4, $mtable{"\u\L$3"}, $1); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (\d+) + (?:st|nd|rd|th)? + \s+ + (January|Jan\.?|February|Feb\.?|March|Mar\.?|April|Apr\.?|May| + June|Jun\.?|July|Jul\.?|August|Aug\.?|September|Sep\.?| + October|Oct\.?|November|Nov\.?|December|Dec\.?) + (?: + \,? + \s+ + (\d\d\d\d) + )? + (:? + \s+ + | + $ + ) + ##) { + # day{st,nd,rd,th}, Month year + ($$yr, $$mr, $$dr) = ($3, $mtable{"\u\L$2"}, $1); + printf "%d: %s - %s - %s - %s\n", __LINE__, $1, $2, $3, $4 if $debug; + print "y undef\n" if ($debug && ! defined($$yr)); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_time_only +{ + my ($tr, $hr, $mr, $sr, $tzr, %options) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s!^(?x) + (?: + (?: + ([012]\d) (?# $1) + (?: + ([0-5]\d) (?# $2) + (?: + ([0-5]\d) (?# $3) + )? + ) + \s* + ([ap]m)? (?# $4) + ) | (?: + (\d{1,2}) (?# $5) + (?: + \: + (\d\d) (?# $6) + (?: + \: + (\d\d) (?# $7) + ( + (?# don't barf on database sub-second timings) + (?:\:|\.) + \d{1,6} + )? (?# $8) + )? + ) + \s* + ([apAP][mM])? (?# $9) + ) | (?: + (\d{1,2}) (?# $10) + ([apAP][mM]) (?# ${11}) + ) + ) + (?: + \s+ + "? + ( (?# ${12}) + (?: [A-Z]{1,4}[TCW56] ) + | + IDLE + ) + )? + (?: + \s+ + | + $ + ) + !!) { #"emacs + # HH[[:]MM[:SS]]meridan [zone] + my $ampm; + $$hr = $1 || $5 || $10 || 0; # 10 is undef, but 5 is defined.. + $$mr = $2 || $6 || 0; + $$sr = $3 || $7 || 0; + if (defined($8) && exists($options{SUBSECOND}) && $options{SUBSECOND}) { + my($frac) = $8; + substr($frac,0,1) = '.'; + $$sr += $frac; + } + print "S = $$sr\n" if $debug; + $ampm = $4 || $9 || $11; + $$tzr = $12; + $$hr += 12 if $ampm and "\U$ampm" eq "PM" && $$hr != 12; + $$hr = 0 if $$hr == 12 && "\U$ampm" eq "AM"; + $$hr = 0 if $$hr == 24; + printf "matched at %d, rem = %s.\n", __LINE__, $$tr if $debug; + return 1; + } elsif ($$tr =~ s#noon(?:\s+|$ )##ix) { + # noon + ($$hr, $$mr, $$sr) = (12, 0, 0); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#midnight(?:\s+|$ )##ix) { + # midnight + ($$hr, $$mr, $$sr) = (0, 0, 0); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_time_offset +{ + my ($tr, $rsr, %options) = @_; + + $$tr =~ s/^\s+//; + + return 0 if $options{NO_RELATIVE}; + + if ($$tr =~ s#^(?xi) + ([-+]?) + \s* + (\d+) + \s* + (sec|second|min|minute|hour)s? + (?: + \s+ + | + $ + ) + ##) { + # count units + $$rsr = 0 unless defined $$rsr; + $$rsr += $umult{"\L$3"} * "$1$2"; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +# +# What to you do with a date that has a two-digit year? +# There's not much that can be done except make a guess. +# +# Some example situations to handle: +# +# now year +# +# 1999 01 +# 1999 71 +# 2010 71 +# 2110 09 +# + +sub expand_two_digit_year +{ + my ($yr, $now, %options) = @_; + + return $yr if $yr > 100; + + my ($y) = (&righttime($now, %options))[5]; + $y += 1900; + my $century = int($y / 100) * 100; + my $within = $y % 100; + + my $r = $yr + $century; + + if ($options{PREFER_PAST}) { + if ($yr > $within) { + $r = $yr + $century - 100; + } + } elsif ($options{PREFER_FUTURE}) { + # being strict here would be silly + if ($yr < $within+10) { + # it's 2019 and the date is '08' + $r = $yr + $century + 100; + } + } elsif ($options{UNAMBIGUOUS}) { + # we really shouldn't guess + return undef; + } else { + # prefer the current century in most cases + + if ($within > 80 && $within - $yr > 60) { + $r = $yr + $century + 100; + } + + if ($within < 30 && $yr - $within > 59) { + $r = $yr + $century - 100; + } + } + print "two digit year '$yr' expanded into $r\n" if $debug; + return $r; +} + + +sub calc +{ + my ($rsr, $yr, $mr, $dr, $rdr, $now, $units, $count, %options) = @_; + + $units = "\L$units"; + + if ($units eq 'day') { + $$rdr = $count; + } elsif ($units eq 'week') { + $$rdr = $count * 7; + } elsif ($umult{$units}) { + $$rsr = $count * $umult{$units}; + } elsif ($units eq 'mon' || $units eq 'month') { + ($$yr, $$mr, $$dr) = &monthoff($now, $count, %options); + $$rsr = 0 unless $$rsr; + } elsif ($units eq 'year') { + ($$yr, $$mr, $$dr) = &monthoff($now, $count * 12, %options); + $$rsr = 0 unless $$rsr; + } else { + carp "interal error"; + } + print "calced rsr $$rsr rdr $$rdr, yr $$yr mr $$mr dr $$dr.\n" if $debug; +} + +sub monthoff +{ + my ($now, $months, %options) = @_; + + # months are 0..11 + my ($d, $m11, $y) = (&righttime($now, %options)) [ 3,4,5 ] ; + + $y += 1900; + + print "m11 = $m11 + $months, y = $y\n" if $debug; + + $m11 += $months; + + print "m11 = $m11, y = $y\n" if $debug; + if ($m11 > 11 || $m11 < 0) { + $y -= 1 if $m11 < 0 && ($m11 % 12 != 0); + $y += int($m11/12); + + # this is required to work around a bug in perl 5.003 + no integer; + $m11 %= 12; + } + print "m11 = $m11, y = $y\n" if $debug; + + # + # What is "1 month from January 31st?" + # I think the answer is February 28th most years. + # + # Similarly, what is one year from February 29th, 1980? + # I think it's February 28th, 1981. + # + # If you disagree, change the following code. + # + if ($d > 30 or ($d > 28 && $m11 == 1)) { + require Time::DaysInMonth; + my $dim = Time::DaysInMonth::days_in($y, $m11+1); + print "dim($y,$m11+1)= $dim\n" if $debug; + $d = $dim if $d > $dim; + } + return ($y, $m11+1, $d); +} + +sub righttime +{ + my ($time, %options) = @_; + if ($options{GMT}) { + return gmtime($time); + } else { + return localtime($time); + } +} + +sub parse_year_only +{ + my ($tr, $yr, $now, %options) = @_; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(\d\d\d\d)(?:\s+|$)##) { + $$yr = $1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#\'(\d\d)(?:\s+|$ )##) { + $$yr = expand_two_digit_year($1, $now, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } + return 0; +} + +sub parse_date_offset +{ + my ($tr, $now, $yr, $mr, $dr, $rdr, $rsr, %options) = @_; + + return 0 if $options{NO_RELATIVE}; + + # now - current seconds_since_epoch + # yr - year return + # mr - month return + # dr - day return + # rdr - relatvie day return + # rsr - relative second return + + my $j; + my $wday = (&righttime($now, %options))[6]; + + $$tr =~ s#^\s+##; + + if ($$tr =~ s#^(?xi) + (?: + (?: + now + \s+ + )? + (\+ | \-) + \s* + )? + (\d+) + \s* + (day|week|month|year)s? + ##) { + my ($one, $two) = ($1, $2); + $one = '' unless defined $one; + $two = '' unless defined $two; + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $3, + "$one$two", %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + \s+ + after + \s+ + next + (?: \s+ | $ ) + ##) { + # Dow "after next" + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} > $wday ? 7 : 14); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + next\s+ + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ ) + ##) { + # "next" Dow + $$rdr = $wdays{"\L$1"} - $wday + + ( $wdays{"\L$1"} > $wday ? 0 : 7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^(?xi) + last\s+ + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ )##) { + # "last" Dow + printf "c %d - %d + ( %d < %d ? 0 : -7 \n", $wdays{"\L$1"}, $wday, $wdays{"\L$1"}, $wday if $debug; + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} < $wday ? 0 : -7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($options{PREFER_PAST} and $$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ )##) { + # Dow + printf "c %d - %d + ( %d < %d ? 0 : -7 \n", $wdays{"\L$1"}, $wday, $wdays{"\L$1"}, $wday if $debug; + $$rdr = $wdays{"\L$1"} - $wday + ( $wdays{"\L$1"} < $wday ? 0 : -7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($options{PREFER_FUTURE} and $$tr =~ s#^(?xi) + (Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday + |Wednesday|Thursday|Friday|Saturday|Sunday) + (?:\s+|$ ) + ##) { + # Dow + $$rdr = $wdays{"\L$1"} - $wday + + ( $wdays{"\L$1"} > $wday ? 0 : 7); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^today(?:\s+|$ )##xi) { + # today + $$rdr = 0; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^tomorrow(?:\s+|$ )##xi) { + $$rdr = 1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^yesterday(?:\s+|$ )##xi) { + $$rdr = -1; + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^last\s+(week|month|year)(?:\s+|$ )##xi) { + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $1, -1, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^next\s+(week|month|year)(?:\s+|$ )##xi) { + &calc($rsr, $yr, $mr, $dr, $rdr, $now, $1, 1, %options); + printf "matched at %d.\n", __LINE__ if $debug; + return 1; + } elsif ($$tr =~ s#^now (?: \s+ | $ )##x) { + $$rdr = 0; + return 1; + } + return 0; +} + +1; + +__DATA__ + +=head1 NAME + +Time::ParseDate -- date parsing both relative and absolute + +=head1 SYNOPSIS + + use Time::ParseDate; + $seconds_since_jan1_1970 = parsedate("12/11/94 2pm", NO_RELATIVE => 1) + $seconds_since_jan1_1970 = parsedate("12/11/94 2pm", %options) + +=head1 OPTIONS + +Date parsing can also use options. The options are as follows: + + FUZZY -> it's okay not to parse the entire date string + NOW -> the "current" time for relative times (defaults to time()) + ZONE -> local timezone (defaults to $ENV{TZ}) + WHOLE -> the whole input string must be parsed + GMT -> input time is assumed to be GMT, not localtime + UK -> prefer UK style dates (dd/mm over mm/dd) + DATE_REQUIRED -> do not default the date + TIME_REQUIRED -> do not default the time + NO_RELATIVE -> input time is not relative to NOW + TIMEFIRST -> try parsing time before date [not default] + PREFER_PAST -> when year or day of week is ambigueous, assume past + PREFER_FUTURE -> when year or day of week is ambigueous, assume future + SUBSECOND -> parse fraction seconds + VALIDATE -> only accept normal values for HHMMSS, YYMMDD. Otherwise + days like -1 might give the last day of the previous month. + +=head1 DATE FORMATS RECOGNIZED + +=head2 Absolute date formats + + Dow, dd Mon yy + Dow, dd Mon yyyy + Dow, dd Mon + dd Mon yy + dd Mon yyyy + Month day{st,nd,rd,th}, year + Month day{st,nd,rd,th} + Mon dd yyyy + yyyy/mm/dd + yyyy/mm + mm/dd/yy + mm/dd/yyyy + mm/yy + yy/mm (only if year > 12, or > 31 if UK) + yy/mm/dd (only if year > 12 and day < 32, or year > 31 if UK) + dd/mm/yy (only if UK, or an invalid mm/dd/yy or yy/mm/dd) + dd/mm/yyyy (only if UK, or an invalid mm/dd/yyyy) + dd/mm (only if UK, or an invalid mm/dd) + +=head2 Relative date formats: + + count "days" + count "weeks" + count "months" + count "years" + Dow "after next" + Dow (requires PREFER_PAST or PREFER_FUTURE) + "next" Dow + "tomorrow" + "today" + "yesterday" + "last" dow + "last week" + "now" + "now" "+" count units + "now" "-" count units + "+" count units + "-" count units + +=head2 Absolute time formats: + + hh:mm:ss[.ddd] + hh:mm + hh:mm[AP]M + hh[AP]M + hhmmss[[AP]M] + "noon" + "midnight" + +=head2 Relative time formats: + + count "minuts" + count "seconds" + count "hours" + "+" count units + "+" count + "-" count units + "-" count + +=head2 Timezone formats: + + [+-]dddd + GMT[+-]d+ + [+-]dddd (TZN) + TZN + +=head2 Special formats: + + [ d]d/Mon/yyyy:hh:mm:ss [[+-]dddd] + yy/mm/dd.hh:mm + +=head1 DESCRIPTION + +This module recognizes the above date/time formats. Usually a +date and a time are specified. There are numerous options for +controlling what is recognized and what is not. + +The return code is always the time in seconds since January 1st, 1970 +or undef if it was unable to parse the time. + +If a timezone is specified it must be after the time. Year specifications +can be tacked onto the end of absolute times. + +If C<parsedate()> is called from array contect, then it will return two +elements. On sucessful parses, it will return the seconds and what +remains of its input string. On unsucessful parses, it will return +C<undef> and an error string. + +=head1 EXAMPLES + + $seconds = parsedate("Mon Jan 2 04:24:27 1995"); + $seconds = parsedate("Tue Apr 4 00:22:12 PDT 1995"); + $seconds = parsedate("04.04.95 00:22", ZONE => PDT); + $seconds = parsedate("Jan 1 1999 11:23:34.578", SUBSECOND => 1); + $seconds = parsedate("122212 950404", ZONE => PDT, TIMEFIRST => 1); + $seconds = parsedate("+3 secs", NOW => 796978800); + $seconds = parsedate("2 months", NOW => 796720932); + $seconds = parsedate("last Tuesday"); + + ($seconds, $remaining) = parsedate("today is the day"); + ($seconds, $error) = parsedate("today is", WHOLE=>1); + +=head1 AUTHOR + +David Muir Sharnoff <muir@idiom.com>. Copyright (C) 1996-1999 All +Rights Reserved. Use and redistribution allowed at user's own +risk. + diff --git a/cpan/lib/Time/Timezone.pm b/cpan/lib/Time/Timezone.pm new file mode 100644 index 00000000..9a013fd0 --- /dev/null +++ b/cpan/lib/Time/Timezone.pm @@ -0,0 +1,272 @@ + +package Time::Timezone; + +require 5.002; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(tz2zone tz_local_offset tz_offset tz_name); +@EXPORT_OK = qw(); + +use Carp; +use strict; + +# Parts stolen from code by Paul Foley <paul@ascent.com> + +use vars qw($VERSION); + +$VERSION = 99.062401; + +sub tz2zone +{ + my($TZ, $time, $isdst) = @_; + + use vars qw(%tzn_cache); + + $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '' + unless $TZ; + + # Hack to deal with 'PST8PDT' format of TZ + # Note that this can't deal with all the esoteric forms, but it + # does recognize the most common: [:]STDoff[DST[off][,rule]] + + if (! defined $isdst) { + my $j; + $time = time() unless $time; + ($j, $j, $j, $j, $j, $j, $j, $j, $isdst) = localtime($time); + } + + if (defined $tzn_cache{$TZ}->[$isdst]) { + return $tzn_cache{$TZ}->[$isdst]; + } + + if ($TZ =~ /^ + ( [^:\d+\-,] {3,} ) + ( [+-] ? + \d {1,2} + ( : \d {1,2} ) {0,2} + ) + ( [^\d+\-,] {3,} )? + /x + ) { + $TZ = $isdst ? $4 : $1; + $tzn_cache{$TZ} = [ $1, $4 ]; + } else { + $tzn_cache{$TZ} = [ $TZ, $TZ ]; + } + return $TZ; +} + +sub tz_local_offset +{ + my ($time) = @_; + + $time = time() unless $time; + my (@l) = localtime($time); + my $isdst = $l[8] || 0; + + if (@Timezone::tz_local && defined($Timezone::tz_local[$isdst])) { + return $Timezone::tz_local[$isdst]; + } + + $Timezone::tz_local[$isdst] = &calc_off($time); + + return $Timezone::tz_local[$isdst]; +} + +sub calc_off +{ + my ($time) = @_; + + my (@l) = localtime($time); + my (@g) = gmtime($time); + + my $off; + + $off = $l[0] - $g[0] + + ($l[1] - $g[1]) * 60 + + ($l[2] - $g[2]) * 3600; + + # subscript 7 is yday. + + if ($l[7] == $g[7]) { + # done + } elsif ($l[7] == $g[7] + 1) { + $off += 86400; + } elsif ($l[7] == $g[7] - 1) { + $off -= 86400; + } elsif ($l[7] < $g[7]) { + # crossed over a year boundry! + # localtime is beginning of year, gmt is end + # therefore local is ahead + $off += 86400; + } else { + $off -= 86400; + } + + return $off; +} + +# constants +# The rest of the file comes from Graham Barr <bodg@tiuk.ti.com> + +CONFIG: { + use vars qw(%dstZone %zoneOff %dstZoneOff %Zone); + + %dstZone = ( + # "ndt" => -2*3600-1800, # Newfoundland Daylight + "adt" => -3*3600, # Atlantic Daylight + "edt" => -4*3600, # Eastern Daylight + "cdt" => -5*3600, # Central Daylight + "mdt" => -6*3600, # Mountain Daylight + "pdt" => -7*3600, # Pacific Daylight + "ydt" => -8*3600, # Yukon Daylight + "hdt" => -9*3600, # Hawaii Daylight + "bst" => +1*3600, # British Summer + "mest" => +2*3600, # Middle European Summer + "sst" => +2*3600, # Swedish Summer + "fst" => +2*3600, # French Summer + "wadt" => +8*3600, # West Australian Daylight + # "cadt" => +10*3600+1800, # Central Australian Daylight + "eadt" => +11*3600, # Eastern Australian Daylight + "nzdt" => +13*3600, # New Zealand Daylight + ); + + %Zone = ( + "gmt" => 0, # Greenwich Mean + "ut" => 0, # Universal (Coordinated) + "utc" => 0, + "wet" => 0, # Western European + "wat" => -1*3600, # West Africa + "at" => -2*3600, # Azores + # For completeness. BST is also British Summer, and GST is also Guam Standard. + # "bst" => -3*3600, # Brazil Standard + # "gst" => -3*3600, # Greenland Standard + # "nft" => -3*3600-1800,# Newfoundland + # "nst" => -3*3600-1800,# Newfoundland Standard + "ast" => -4*3600, # Atlantic Standard + "est" => -5*3600, # Eastern Standard + "cst" => -6*3600, # Central Standard + "mst" => -7*3600, # Mountain Standard + "pst" => -8*3600, # Pacific Standard + "yst" => -9*3600, # Yukon Standard + "hst" => -10*3600, # Hawaii Standard + "cat" => -10*3600, # Central Alaska + "ahst" => -10*3600, # Alaska-Hawaii Standard + "nt" => -11*3600, # Nome + "idlw" => -12*3600, # International Date Line West + "cet" => +1*3600, # Central European + "met" => +1*3600, # Middle European + "mewt" => +1*3600, # Middle European Winter + "swt" => +1*3600, # Swedish Winter + "fwt" => +1*3600, # French Winter + "eet" => +2*3600, # Eastern Europe, USSR Zone 1 + "bt" => +3*3600, # Baghdad, USSR Zone 2 + # "it" => +3*3600+1800,# Iran + "zp4" => +4*3600, # USSR Zone 3 + "zp5" => +5*3600, # USSR Zone 4 + # "ist" => +5*3600+1800,# Indian Standard + "zp6" => +6*3600, # USSR Zone 5 + # For completeness. NST is also Newfoundland Stanard, and SST is also Swedish Summer. + # "nst" => +6*3600+1800,# North Sumatra + # "sst" => +7*3600, # South Sumatra, USSR Zone 6 + "wast" => +7*3600, # West Australian Standard + # "jt" => +7*3600+1800,# Java (3pm in Cronusland!) + "cct" => +8*3600, # China Coast, USSR Zone 7 + "jst" => +9*3600, # Japan Standard, USSR Zone 8 + # "cast" => +9*3600+1800,# Central Australian Standard + "east" => +10*3600, # Eastern Australian Standard + "gst" => +10*3600, # Guam Standard, USSR Zone 9 + "nzt" => +12*3600, # New Zealand + "nzst" => +12*3600, # New Zealand Standard + "idle" => +12*3600, # International Date Line East + ); + + %zoneOff = reverse(%Zone); + %dstZoneOff = reverse(%dstZone); + + # Preferences + + $zoneOff{0} = 'gmt'; + $dstZoneOff{3600} = 'bst'; + +} + +sub tz_offset +{ + my ($zone, $time) = @_; + + return &tz_local_offset() unless($zone); + + $time = time() unless $time; + my(@l) = localtime($time); + my $dst = $l[8]; + + $zone = lc $zone; + + if ($zone =~ /^([\-\+]\d{3,4})$/) { + my $v = 0 + $1; + return int($v / 100) * 60 + ($v % 100); + } elsif (exists $dstZone{$zone} && ($dst || !exists $Zone{$zone})) { + return $dstZone{$zone}; + } elsif(exists $Zone{$zone}) { + return $Zone{$zone}; + } + undef; +} + +sub tz_name +{ + my ($off, $time) = @_; + + $time = time() unless $time; + my(@l) = localtime($time); + my $dst = $l[8]; + + if (exists $dstZoneOff{$off} && ($dst || !exists $zoneOff{$off})) { + return $dstZoneOff{$off}; + } elsif (exists $zoneOff{$off}) { + return $zoneOff{$off}; + } + sprintf("%+05d", int($off / 60) * 100 + $off % 60); +} + +1; + +__DATA__ + +=head1 NAME + +Time::Timezone -- miscellaneous timezone manipulations routines + +=head1 SYNOPSIS + + use Time::Timezone; + print tz2zone(); + print tz2zone($ENV{'TZ'}); + print tz2zone($ENV{'TZ'}, time()); + print tz2zone($ENV{'TZ'}, undef, $isdst); + $offset = tz_local_offset(); + $offset = tz_offset($TZ); + +=head1 DESCRIPTION + +This is a collection of miscellaneous timezone manipulation routines. + +C<tz2zone()> parses the TZ environment variable and returns a timezone +string suitable for inclusion in L<date>-like output. It opionally takes +a timezone string, a time, and a is-dst flag. + +C<tz_local_offset()> determins the offset from GMT time in seconds. It +only does the calculation once. + +C<tz_offset()> determines the offset from GMT in seconds of a specified +timezone. + +C<tz_name()> determines the name of the timezone based on its offset + +=head1 AUTHORS + +Graham Barr <bodg@tiuk.ti.com> +David Muir Sharnoff <muir@idiom.com> +Paul Foley <paul@ascent.com> |
