summaryrefslogtreecommitdiff
path: root/cpan
diff options
context:
space:
mode:
authorfukachan <fukachan>2001-05-01 03:13:32 +0000
committerfukachan <fukachan>2001-05-01 03:13:32 +0000
commitfe9a0dcf78d2eb27e33b02b113bae28e05be4623 (patch)
tree615a4056c8f0d95babd7bfaae64ed6d8ce7be3f1 /cpan
parent12059d51b53964f2397d7b9e72a32f2becc85603 (diff)
downloadfml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.tar.gz
fml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.tar.bz2
fml8-fe9a0dcf78d2eb27e33b02b113bae28e05be4623.zip
Initial revision
Diffstat (limited to 'cpan')
-rw-r--r--cpan/dist/Time-modules/CHANGELOG74
-rw-r--r--cpan/dist/Time-modules/MANIFEST10
-rw-r--r--cpan/dist/Time-modules/Makefile.PL11
-rw-r--r--cpan/dist/Time-modules/README25
-rw-r--r--cpan/dist/Time-modules/lib/Time/CTime.pm197
-rw-r--r--cpan/dist/Time-modules/lib/Time/DaysInMonth.pm75
-rw-r--r--cpan/dist/Time-modules/lib/Time/JulianDay.pm200
-rw-r--r--cpan/dist/Time-modules/lib/Time/ParseDate.pm1194
-rw-r--r--cpan/dist/Time-modules/lib/Time/Timezone.pm272
-rwxr-xr-xcpan/dist/Time-modules/t/datetime.t416
-rw-r--r--cpan/lib/Time/CTime.pm197
-rw-r--r--cpan/lib/Time/DaysInMonth.pm75
-rw-r--r--cpan/lib/Time/JulianDay.pm200
-rw-r--r--cpan/lib/Time/ParseDate.pm1194
-rw-r--r--cpan/lib/Time/Timezone.pm272
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>