package DateManip; # Copyright (c) 1995-2005 Sullivan Beck. All rights reserved. # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. ########################################################################### ########################################################################### use vars qw($OS %Lang %Holiday %Events %Curr %Cnf %Zone $VERSION @ISA @EXPORT); # Determine the type of OS... $OS="Unix"; $OS="Windows" if ((defined $^O and $^O =~ /MSWin32/i || $^O =~ /Windows_95/i || $^O =~ /Windows_NT/i) || (defined $ENV{OS} and $ENV{OS} =~ /MSWin32/i || $ENV{OS} =~ /Windows_95/i || $ENV{OS} =~ /Windows_NT/i)); $OS="Unix" if (defined $^O and $^O =~ /cygwin/i); $OS="Netware" if (defined $^O and $^O =~ /NetWare/i); $OS="Mac" if ((defined $^O and $^O =~ /MacOS/i) || (defined $ENV{OS} and $ENV{OS} =~ /MacOS/i)); $OS="MPE" if (defined $^O and $^O =~ /MPE/i); $OS="OS2" if (defined $^O and $^O =~ /os2/i); $OS="VMS" if (defined $^O and $^O =~ /VMS/i); # Determine if we're doing taint checking $Date::Manip::NoTaint = eval { local $^W=0; eval("#" . substr($^X, 0, 0)); 1 }; ########################################################################### # CUSTOMIZATION ########################################################################### # # See the section of the POD documentation section CUSTOMIZING DATE::MANIP # below for a complete description of each of these variables. # Location of a the global config file. Tilde (~) expansions are allowed. # This should be set in Date_Init arguments. $Cnf{"GlobalCnf"}=""; $Cnf{"IgnoreGlobalCnf"}=""; # Name of a personal config file and the path to search for it. Tilde (~) # expansions are allowed. This should be set in Date_Init arguments or in # the global config file. @Date::Manip::DatePath=(); if ($OS eq "Windows") { $Cnf{"PathSep"} = ";"; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "."; } elsif ($OS eq "Netware") { $Cnf{"PathSep"} = ";"; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "."; } elsif ($OS eq "MPE") { $Cnf{"PathSep"} = ":"; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "."; } elsif ($OS eq "OS2") { $Cnf{"PathSep"} = ":"; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "."; } elsif ($OS eq "Mac") { $Cnf{"PathSep"} = ":"; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "."; } elsif ($OS eq "VMS") { # VMS doesn't like files starting with "." $Cnf{"PathSep"} = ","; $Cnf{"PersonalCnf"} = "Manip.cnf"; $Cnf{"PersonalCnfPath"} = "/sys\$login"; } else { # Unix $Cnf{"PathSep"} = ":"; $Cnf{"PersonalCnf"} = ".DateManip.cnf"; $Cnf{"PersonalCnfPath"} = ".:~"; @Date::Manip::DatePath=qw(/bin /usr/bin /usr/local/bin); } ### Date::Manip variables set in the global or personal config file # Which language to use when parsing dates. $Cnf{"Language"}="English"; # 12/10 = Dec 10 (US) or Oct 12 (anything else) $Cnf{"DateFormat"}="US"; # Local timezone $Cnf{"TZ"}=""; # Timezone to work in (""=local, "IGNORE", or a timezone) $Cnf{"ConvTZ"}=""; # Date::Manip internal format (0=YYYYMMDDHH:MN:SS, 1=YYYYHHMMDDHHMNSS) $Cnf{"Internal"}=0; # First day of the week (1=monday, 7=sunday). ISO 8601 says monday. $Cnf{"FirstDay"}=1; # First and last day of the work week (1=monday, 7=sunday) $Cnf{"WorkWeekBeg"}=1; $Cnf{"WorkWeekEnd"}=5; # If non-nil, a work day is treated as 24 hours long (WorkDayBeg/WorkDayEnd # ignored) $Cnf{"WorkDay24Hr"}=0; # Start and end time of the work day (any time format allowed, seconds # ignored) $Cnf{"WorkDayBeg"}="08:00"; $Cnf{"WorkDayEnd"}="17:00"; # If "today" is a holiday, we look either to "tomorrow" or "yesterday" for # the nearest business day. By default, we'll always look "tomorrow" # first. $Cnf{"TomorrowFirst"}=1; # Erase the old holidays $Cnf{"EraseHolidays"}=""; # Set this to non-zero to be produce completely backwards compatible deltas $Cnf{"DeltaSigns"}=0; # If this is 0, use the ISO 8601 standard that Jan 4 is in week 1. If 1, # make week 1 contain Jan 1. $Cnf{"Jan1Week1"}=0; # 2 digit years fall into the 100 year period given by [ CURR-N, # CURR+(99-N) ] where N is 0-99. Default behavior is 89, but other useful # numbers might be 0 (forced to be this year or later) and 99 (forced to be # this year or earlier). It can also be set to "c" (current century) or # "cNN" (i.e. c18 forces the year to bet 1800-1899). Also accepts the # form cNNNN to give the 100 year period NNNN to NNNN+99. $Cnf{"YYtoYYYY"}=89; # Set this to 1 if you want a long-running script to always update the # timezone. This will slow Date::Manip down. Read the POD documentation. $Cnf{"UpdateCurrTZ"}=0; # Use an international character set. $Cnf{"IntCharSet"}=0; # Use this to force the current date to be set to this: $Cnf{"ForceDate"}=""; # Use this to make "today" mean "today at midnight". $Cnf{"TodayIsMidnight"}=0; ########################################################################### require 5.000; require Exporter; @ISA = qw(Exporter); @EXPORT = qw( DateManipVersion Date_Init ParseDateString ParseDate ParseRecur Date_Cmp DateCalc ParseDateDelta UnixDate Delta_Format Date_GetPrev Date_GetNext Date_SetTime Date_SetDateField Date_IsHoliday Events_List Date_DaysInMonth Date_DayOfWeek Date_SecsSince1970 Date_SecsSince1970GMT Date_DaysSince1BC Date_DayOfYear Date_DaysInYear Date_WeekOfYear Date_LeapYear Date_DaySuffix Date_ConvTZ Date_TimeZone Date_IsWorkDay Date_NextWorkDay Date_PrevWorkDay Date_NearestWorkDay Date_NthDayOfYear ); use strict; use integer; use Carp; use IO::File; $VERSION="5.44"; ######################################################################## ######################################################################## $Curr{"InitLang"} = 1; # Whether a language is being init'ed $Curr{"InitDone"} = 0; # Whether Init_Date has been called $Curr{"InitFilesRead"} = 0; $Curr{"ResetWorkDay"} = 1; $Curr{"Debug"} = ""; $Curr{"DebugVal"} = ""; $Holiday{"year"} = 0; $Holiday{"dates"} = {}; $Holiday{"desc"} = {}; $Events{"raw"} = []; $Events{"parsed"} = 0; $Events{"dates"} = []; $Events{"recur"} = []; ######################################################################## ######################################################################## # THESE ARE THE MAIN ROUTINES ######################################################################## ######################################################################## # Get rid of a problem with old versions of perl no strict "vars"; # This sorts from longest to shortest element sub sortByLength { return (length $b <=> length $a); } use strict "vars"; sub DateManipVersion { print "DEBUG: DateManipVersion\n" if ($Curr{"Debug"} =~ /trace/); return $VERSION; } sub Date_Init { print "DEBUG: Date_Init\n" if ($Curr{"Debug"} =~ /trace/); $Curr{"Debug"}=""; my(@args)=@_; $Curr{"InitDone"}=1; local($_)=(); my($internal,$firstday)=(); my($var,$val,$file,@tmp)=(); # InitFilesRead = 0 : no conf files read yet # 1 : global read, no personal read # 2 : personal read $Cnf{"EraseHolidays"}=0; foreach (@args) { s/\s*$//; s/^\s*//; /^(\S+) \s* = \s* (.*)$/x; ($var,$val)=($1,$2); if ($var =~ /^GlobalCnf$/i) { $Cnf{"GlobalCnf"}=$val; if ($val) { $Curr{"InitFilesRead"}=0; &EraseHolidays(); } } elsif ($var =~ /^PathSep$/i) { $Cnf{"PathSep"}=$val; } elsif ($var =~ /^PersonalCnf$/i) { $Cnf{"PersonalCnf"}=$val; $Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==2); } elsif ($var =~ /^PersonalCnfPath$/i) { $Cnf{"PersonalCnfPath"}=$val; $Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==2); } elsif ($var =~ /^IgnoreGlobalCnf$/i) { $Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==0); $Cnf{"IgnoreGlobalCnf"}=1; } elsif ($var =~ /^EraseHolidays$/i) { &EraseHolidays(); } else { push(@tmp,$_); } } @args=@tmp; # Read global config file if ($Curr{"InitFilesRead"}<1 && ! $Cnf{"IgnoreGlobalCnf"}) { $Curr{"InitFilesRead"}=1; if ($Cnf{"GlobalCnf"}) { $file=&ExpandTilde($Cnf{"GlobalCnf"}); &Date_InitFile($file) if ($file); } } # Read personal config file if ($Curr{"InitFilesRead"}<2) { $Curr{"InitFilesRead"}=2; if ($Cnf{"PersonalCnf"} and $Cnf{"PersonalCnfPath"}) { $file=&SearchPath($Cnf{"PersonalCnf"},$Cnf{"PersonalCnfPath"},"r"); &Date_InitFile($file) if ($file); } } foreach (@args) { s/\s*$//; s/^\s*//; /^(\S+) \s* = \s* (.*)$/x; ($var,$val)=($1,$2); $val="" if (! defined $val); &Date_SetConfigVariable($var,$val); } confess "ERROR: Unknown FirstDay in Date::Manip.\n" if (! &IsInt($Cnf{"FirstDay"},1,7)); confess "ERROR: Unknown WorkWeekBeg in Date::Manip.\n" if (! &IsInt($Cnf{"WorkWeekBeg"},1,7)); confess "ERROR: Unknown WorkWeekEnd in Date::Manip.\n" if (! &IsInt($Cnf{"WorkWeekEnd"},1,7)); confess "ERROR: Invalid WorkWeek in Date::Manip.\n" if ($Cnf{"WorkWeekEnd"} <= $Cnf{"WorkWeekBeg"}); my(%lang, $tmp,%tmp,$tmp2,@tmp2, $i,$j,@tmp3, $zonesrfc,@zones)=(); my($L)=$Cnf{"Language"}; if ($Curr{"InitLang"}) { $Curr{"InitLang"}=0; if ($L eq "English") { &Date_Init_English(\%lang); } elsif ($L eq "French") { &Date_Init_French(\%lang); } elsif ($L eq "Swedish") { &Date_Init_Swedish(\%lang); } elsif ($L eq "German") { &Date_Init_German(\%lang); } elsif ($L eq "Polish") { &Date_Init_Polish(\%lang); } elsif ($L eq "Dutch" || $L eq "Nederlands") { &Date_Init_Dutch(\%lang); } elsif ($L eq "Spanish") { &Date_Init_Spanish(\%lang); } elsif ($L eq "Portuguese") { &Date_Init_Portuguese(\%lang); } elsif ($L eq "Romanian") { &Date_Init_Romanian(\%lang); } elsif ($L eq "Italian") { &Date_Init_Italian(\%lang); } elsif ($L eq "Russian") { &Date_Init_Russian(\%lang); } elsif ($L eq "Turkish") { &Date_Init_Turkish(\%lang); } elsif ($L eq "Danish") { &Date_Init_Danish(\%lang); } elsif ($L eq "Catalan") { &Date_Init_Catalan(\%lang); } else { confess "ERROR: Unknown language in Date::Manip.\n"; } # variables for months # Month = "(jan|january|feb|february ... )" # MonL = [ "Jan","Feb",... ] # MonthL = [ "January","February", ... ] # MonthH = { "january"=>1, "jan"=>1, ... } $Lang{$L}{"MonthH"}={}; $Lang{$L}{"MonthL"}=[]; $Lang{$L}{"MonL"}=[]; &Date_InitLists([$lang{"month_name"}, $lang{"month_abb"}], \$Lang{$L}{"Month"},"lc,sort,back", [$Lang{$L}{"MonthL"}, $Lang{$L}{"MonL"}], [$Lang{$L}{"MonthH"},1]); # variables for day of week # Week = "(mon|monday|tue|tuesday ... )" # WL = [ "M","T",... ] # WkL = [ "Mon","Tue",... ] # WeekL = [ "Monday","Tudesday",... ] # WeekH = { "monday"=>1,"mon"=>1,"m"=>1,... } $Lang{$L}{"WeekH"}={}; $Lang{$L}{"WeekL"}=[]; $Lang{$L}{"WkL"}=[]; $Lang{$L}{"WL"}=[]; &Date_InitLists([$lang{"day_name"}, $lang{"day_abb"}], \$Lang{$L}{"Week"},"lc,sort,back", [$Lang{$L}{"WeekL"}, $Lang{$L}{"WkL"}], [$Lang{$L}{"WeekH"},1]); &Date_InitLists([$lang{"day_char"}], "","lc", [$Lang{$L}{"WL"}], [\%tmp,1]); %{ $Lang{$L}{"WeekH"} } = (%{ $Lang{$L}{"WeekH"} },%tmp); # variables for last # Last = "(last)" # LastL = [ "last" ] # Each = "(each)" # EachL = [ "each" ] # variables for day of month # DoM = "(1st|first ... 31st)" # DoML = [ "1st","2nd",... "31st" ] # DoMH = { "1st"=>1,"first"=>1, ... "31st"=>31 } # variables for week of month # WoM = "(1st|first| ... 5th|last)" # WoMH = { "1st"=>1, ... "5th"=>5,"last"=>-1 } $Lang{$L}{"LastL"}=$lang{"last"}; &Date_InitStrings($lang{"last"}, \$Lang{$L}{"Last"},"lc,sort"); $Lang{$L}{"EachL"}=$lang{"each"}; &Date_InitStrings($lang{"each"}, \$Lang{$L}{"Each"},"lc,sort"); $Lang{$L}{"DoMH"}={}; $Lang{$L}{"DoML"}=[]; &Date_InitLists([$lang{"num_suff"}, $lang{"num_word"}], \$Lang{$L}{"DoM"},"lc,sort,back,escape", [$Lang{$L}{"DoML"}, \@tmp], [$Lang{$L}{"DoMH"},1]); @tmp=(); foreach $tmp (keys %{ $Lang{$L}{"DoMH"} }) { $tmp2=$Lang{$L}{"DoMH"}{$tmp}; if ($tmp2<6) { $Lang{$L}{"WoMH"}{$tmp} = $tmp2; push(@tmp,$tmp); } } foreach $tmp (@{ $Lang{$L}{"LastL"} }) { $Lang{$L}{"WoMH"}{$tmp} = -1; push(@tmp,$tmp); } &Date_InitStrings(\@tmp,\$Lang{$L}{"WoM"}, "lc,sort,back,escape"); # variables for AM or PM # AM = "(am)" # PM = "(pm)" # AmPm = "(am|pm)" # AMstr = "AM" # PMstr = "PM" &Date_InitStrings($lang{"am"},\$Lang{$L}{"AM"},"lc,sort,escape"); &Date_InitStrings($lang{"pm"},\$Lang{$L}{"PM"},"lc,sort,escape"); &Date_InitStrings([ @{$lang{"am"}},@{$lang{"pm"}} ],\$Lang{$L}{"AmPm"}, "lc,back,sort,escape"); $Lang{$L}{"AMstr"}=$lang{"am"}[0]; $Lang{$L}{"PMstr"}=$lang{"pm"}[0]; # variables for expressions used in parsing deltas # Yabb = "(?:y|yr|year|years)" # Mabb = similar for months # Wabb = similar for weeks # Dabb = similar for days # Habb = similar for hours # MNabb = similar for minutes # Sabb = similar for seconds # Repl = { "abb"=>"replacement" } # Whenever an abbreviation could potentially refer to two different # strings (M standing for Minutes or Months), the abbreviation must # be listed in Repl instead of in the appropriate Xabb values. This # only applies to abbreviations which are substrings of other values # (so there is no confusion between Mn and Month). &Date_InitStrings($lang{"years"} ,\$Lang{$L}{"Yabb"}, "lc,sort"); &Date_InitStrings($lang{"months"} ,\$Lang{$L}{"Mabb"}, "lc,sort"); &Date_InitStrings($lang{"weeks"} ,\$Lang{$L}{"Wabb"}, "lc,sort"); &Date_InitStrings($lang{"days"} ,\$Lang{$L}{"Dabb"}, "lc,sort"); &Date_InitStrings($lang{"hours"} ,\$Lang{$L}{"Habb"}, "lc,sort"); &Date_InitStrings($lang{"minutes"},\$Lang{$L}{"MNabb"},"lc,sort"); &Date_InitStrings($lang{"seconds"},\$Lang{$L}{"Sabb"}, "lc,sort"); $Lang{$L}{"Repl"}={}; &Date_InitHash($lang{"replace"},undef,"lc",$Lang{$L}{"Repl"}); # variables for special dates that are offsets from now # Now = "now" # Today = "today" # Offset = "(yesterday|tomorrow)" # OffsetH = { "yesterday"=>"-0:0:0:1:0:0:0",... ] # Times = "(noon|midnight)" # TimesH = { "noon"=>"12:00:00","midnight"=>"00:00:00" } # SepHM = hour/minute separator # SepMS = minute/second separator # SepSS = second/fraction separator $Lang{$L}{"TimesH"}={}; &Date_InitHash($lang{"times"}, \$Lang{$L}{"Times"},"lc,sort,back", $Lang{$L}{"TimesH"}); &Date_InitStrings($lang{"now"},\$Lang{$L}{"Now"},"lc,sort"); &Date_InitStrings($lang{"today"},\$Lang{$L}{"Today"},"lc,sort"); $Lang{$L}{"OffsetH"}={}; &Date_InitHash($lang{"offset"}, \$Lang{$L}{"Offset"},"lc,sort,back", $Lang{$L}{"OffsetH"}); $Lang{$L}{"SepHM"}=$lang{"sephm"}; $Lang{$L}{"SepMS"}=$lang{"sepms"}; $Lang{$L}{"SepSS"}=$lang{"sepss"}; # variables for time zones # zones = regular expression with all zone names (EST) # n2o = a hash of all parsable zone names with their offsets # tzones = reguar expression with all tzdata timezones (US/Eastern) # tz2z = hash of all tzdata timezones to full timezone (EST#EDT) $zonesrfc= "idlw -1200 ". # International Date Line West "nt -1100 ". # Nome "hst -1000 ". # Hawaii Standard "cat -1000 ". # Central Alaska "ahst -1000 ". # Alaska-Hawaii Standard "akst -0900 ". # Alaska Standard "yst -0900 ". # Yukon Standard "hdt -0900 ". # Hawaii Daylight "akdt -0800 ". # Alaska Daylight "ydt -0800 ". # Yukon Daylight "pst -0800 ". # Pacific Standard "pdt -0700 ". # Pacific Daylight "mst -0700 ". # Mountain Standard "mdt -0600 ". # Mountain Daylight "cst -0600 ". # Central Standard "cdt -0500 ". # Central Daylight "est -0500 ". # Eastern Standard "act -0500 ". # Brazil, Acre "sat -0400 ". # Chile "clst -0400 ". # Chile Standard "bot -0400 ". # Bolivia "amt -0400 ". # Brazil, Amazon "acst -0400 ". # Brazil, Acre Daylight "edt -0400 ". # Eastern Daylight "ast -0400 ". # Atlantic Standard #"nst -0330 ". # Newfoundland Standard nst=North Sumatra +0630 "nft -0330 ". # Newfoundland #"gst -0300 ". # Greenland Standard gst=Guam Standard +1000 "cldt -0300 ". # Chile Daylight #"bst -0300 ". # Brazil Standard bst=British Summer +0100 "brt -0300 ". # Brazil Standard (official time) #"brst -0300 ". # Brazil Standard "adt -0300 ". # Atlantic Daylight "art -0300 ". # Argentina "amst -0300 ". # Brazil, Amazon Daylight "uyt -0300 ". # Uruguay "ndt -0230 ". # Newfoundland Daylight "brst -0200 ". # Brazil Daylight (official time) "fnt -0200 ". # Brazil, Fernando de Noronha "at -0200 ". # Azores "wat -0100 ". # West Africa "fnst -0100 ". # Brazil, Fernando de Noronha Daylight "gmt +0000 ". # Greenwich Mean "ut +0000 ". # Universal "utc +0000 ". # Universal (Coordinated) "wet +0000 ". # Western European "cet +0100 ". # Central European "fwt +0100 ". # French Winter "met +0100 ". # Middle European "mez +0100 ". # Middle European "mewt +0100 ". # Middle European Winter "swt +0100 ". # Swedish Winter "bst +0100 ". # British Summer bst=Brazil standard -0300 "gb +0100 ". # GMT with daylight savings "west +0000 ". # Western European Daylight "eet +0200 ". # Eastern Europe, USSR Zone 1 "cest +0200 ". # Central European Summer "fst +0200 ". # French Summer "ist +0200 ". # Israel standard "mest +0200 ". # Middle European Summer "mesz +0200 ". # Middle European Summer "metdst +0200 ". # An alias for mest used by HP-UX "sast +0200 ". # South African Standard "sst +0200 ". # Swedish Summer sst=South Sumatra +0700 "bt +0300 ". # Baghdad, USSR Zone 2 "eest +0300 ". # Eastern Europe Summer "eetedt +0300 ". # Eastern Europe, USSR Zone 1 "idt +0300 ". # Israel Daylight "msk +0300 ". # Moscow "eat +0300 ". # East Africa "it +0330 ". # Iran "zp4 +0400 ". # USSR Zone 3 "msd +0400 ". # Moscow Daylight "zp5 +0500 ". # USSR Zone 4 "ist +0530 ". # Indian Standard "zp6 +0600 ". # USSR Zone 5 "novst +0600 ". # Novosibirsk time zone, Russia "nst +0630 ". # North Sumatra nst=Newfoundland Std -0330 #"sst +0700 ". # South Sumatra, USSR Zone 6 sst=Swedish Summer +0200 "javt +0700 ". # Java "ict +0700 ". # Indo China Time "hkt +0800 ". # Hong Kong "sgt +0800 ". # Singapore "cct +0800 ". # China Coast, USSR Zone 7 "awst +0800 ". # Australian Western Standard "wst +0800 ". # West Australian Standard "pht +0800 ". # Asia Manila "kst +0900 ". # Republic of Korea "jst +0900 ". # Japan Standard, USSR Zone 8 "rok +0900 ". # Republic of Korea "acst +0930 ". # Australian Central Standard "cast +0930 ". # Central Australian Standard "aest +1000 ". # Australian Eastern Standard "east +1000 ". # Eastern Australian Standard "gst +1000 ". # Guam Standard, USSR Zone 9 gst=Greenland Std -0300 "chst +1000 ". # Guam Standard, USSR Zone 9 gst=Greenland Std -0300 "acdt +1030 ". # Australian Central Daylight "cadt +1030 ". # Central Australian Daylight "aedt +1100 ". # Australian Eastern Daylight "eadt +1100 ". # Eastern Australian Daylight "idle +1200 ". # International Date Line East "nzst +1200 ". # New Zealand Standard "nzt +1200 ". # New Zealand "nzdt +1300 ". # New Zealand Daylight "z +0000 ". "a +0100 b +0200 c +0300 d +0400 e +0500 f +0600 g +0700 h +0800 ". "i +0900 k +1000 l +1100 m +1200 ". "n -0100 o -0200 p -0300 q -0400 r -0500 s -0600 t -0700 u -0800 ". "v -0900 w -1000 x -1100 y -1200"; $Zone{"n2o"} = {}; ($Zone{"zones"},%{ $Zone{"n2o"} })= &Date_Regexp($zonesrfc,"sort,lc,under,back", "keys"); $tmp= "US/Pacific PST8PDT ". "US/Mountain MST7MDT ". "US/Central CST6CDT ". "US/Eastern EST5EDT ". "Canada/Pacific PST8PDT ". "Canada/Mountain MST7MDT ". "Canada/Central CST6CDT ". "Canada/Eastern EST5EDT"; $Zone{"tz2z"} = {}; ($Zone{"tzones"},%{ $Zone{"tz2z"} })= &Date_Regexp($tmp,"lc,under,back","keys"); $Cnf{"TZ"}=&Date_TimeZone; # misc. variables # At = "(?:at)" # Of = "(?:in|of)" # On = "(?:on)" # Future = "(?:in)" # Later = "(?:later)" # Past = "(?:ago)" # Next = "(?:next)" # Prev = "(?:last|previous)" &Date_InitStrings($lang{"at"}, \$Lang{$L}{"At"}, "lc,sort"); &Date_InitStrings($lang{"on"}, \$Lang{$L}{"On"}, "lc,sort"); &Date_InitStrings($lang{"future"},\$Lang{$L}{"Future"}, "lc,sort"); &Date_InitStrings($lang{"later"}, \$Lang{$L}{"Later"}, "lc,sort"); &Date_InitStrings($lang{"past"}, \$Lang{$L}{"Past"}, "lc,sort"); &Date_InitStrings($lang{"next"}, \$Lang{$L}{"Next"}, "lc,sort"); &Date_InitStrings($lang{"prev"}, \$Lang{$L}{"Prev"}, "lc,sort"); &Date_InitStrings($lang{"of"}, \$Lang{$L}{"Of"}, "lc,sort"); # calc mode variables # Approx = "(?:approximately)" # Exact = "(?:exactly)" # Business = "(?:business)" &Date_InitStrings($lang{"exact"}, \$Lang{$L}{"Exact"}, "lc,sort"); &Date_InitStrings($lang{"approx"}, \$Lang{$L}{"Approx"}, "lc,sort"); &Date_InitStrings($lang{"business"},\$Lang{$L}{"Business"},"lc,sort"); ############### END OF LANGUAGE INITIALIZATION } if ($Curr{"ResetWorkDay"}) { my($h1,$m1,$h2,$m2)=(); if ($Cnf{"WorkDay24Hr"}) { ($Curr{"WDBh"},$Curr{"WDBm"})=(0,0); ($Curr{"WDEh"},$Curr{"WDEm"})=(24,0); $Curr{"WDlen"}=24*60; $Cnf{"WorkDayBeg"}="00:00"; $Cnf{"WorkDayEnd"}="23:59"; } else { confess "ERROR: Invalid WorkDayBeg in Date::Manip.\n" if (! (($h1,$m1)=&CheckTime($Cnf{"WorkDayBeg"}))); $Cnf{"WorkDayBeg"}="$h1:$m1"; confess "ERROR: Invalid WorkDayEnd in Date::Manip.\n" if (! (($h2,$m2)=&CheckTime($Cnf{"WorkDayEnd"}))); $Cnf{"WorkDayEnd"}="$h2:$m2"; ($Curr{"WDBh"},$Curr{"WDBm"})=($h1,$m1); ($Curr{"WDEh"},$Curr{"WDEm"})=($h2,$m2); # Work day length = h1:m1 or 0:len (len minutes) $h1=$h2-$h1; $m1=$m2-$m1; if ($m1<0) { $h1--; $m1+=60; } $Curr{"WDlen"}=$h1*60+$m1; } $Curr{"ResetWorkDay"}=0; } # current time my($s,$mn,$h,$d,$m,$y,$wday,$yday,$isdst,$ampm,$wk)=(); if ($Cnf{"ForceDate"}=~ /^(\d{4})-(\d{2})-(\d{2})-(\d{2}):(\d{2}):(\d{2})$/) { ($y,$m,$d,$h,$mn,$s)=($1,$2,$3,$4,$5,$6); } else { ($s,$mn,$h,$d,$m,$y,$wday,$yday,$isdst)=localtime(time); $y+=1900; $m++; } &Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk); $Curr{"Y"}=$y; $Curr{"M"}=$m; $Curr{"D"}=$d; $Curr{"H"}=$h; $Curr{"Mn"}=$mn; $Curr{"S"}=$s; $Curr{"AmPm"}=$ampm; $Curr{"Now"}=&Date_Join($y,$m,$d,$h,$mn,$s); if ($Cnf{"TodayIsMidnight"}) { $Curr{"Today"}=&Date_Join($y,$m,$d,0,0,0); } else { $Curr{"Today"}=$Curr{"Now"}; } $Curr{"Debug"}=$Curr{"DebugVal"}; # If we're in array context, let's return a list of config variables # that could be passed to Date_Init to get the same state as we're # currently in. if (wantarray) { # Some special variables that have to be in a specific order my(@special)=qw(IgnoreGlobalCnf GlobalCnf PersonalCnf PersonalCnfPath); my(%tmp)=map { $_,1 } @special; my(@tmp,$key,$val); foreach $key (@special) { $val=$Cnf{$key}; push(@tmp,"$key=$val"); } foreach $key (keys %Cnf) { next if (exists $tmp{$key}); $val=$Cnf{$key}; push(@tmp,"$key=$val"); } return @tmp; } return (); } sub ParseDateString { print "DEBUG: ParseDateString\n" if ($Curr{"Debug"} =~ /trace/); local($_)=@_; return "" if (! $_); my($y,$m,$d,$h,$mn,$s,$i,$wofm,$dofw,$wk,$tmp,$z,$num,$err,$iso,$ampm)=(); my($date,$z2,$delta,$from,$falsefrom,$to,$which,$midnight)=(); # We only need to reinitialize if we have to determine what NOW is. &Date_Init() if (! $Curr{"InitDone"} or $Cnf{"UpdateCurrTZ"}); my($L)=$Cnf{"Language"}; my($type)=$Cnf{"DateFormat"}; # Mode is set in DateCalc. ParseDate only overrides it if the string # contains a mode. if ($Lang{$L}{"Exact"} && s/$Lang{$L}{"Exact"}//) { $Curr{"Mode"}=0; } elsif ($Lang{$L}{"Approx"} && s/$Lang{$L}{"Approx"}//) { $Curr{"Mode"}=1; } elsif ($Lang{$L}{"Business"} && s/$Lang{$L}{"Business"}//) { $Curr{"Mode"}=2; } elsif (! exists $Curr{"Mode"}) { $Curr{"Mode"}=0; } # Unfortunately, some deltas can be parsed as dates. An example is # 1 second == 1 2nd == 1 2 # But, some dates can be parsed as deltas. The most important being: # 1998010101:00:00 # # We'll check to see if a "date" can be parsed as a delta. If so, we'll # assume that it is a delta (since they are much simpler, it is much # less likely that we'll mistake a delta for a date than vice versa) # unless it is an ISO-8601 date. # # This is important because we are using DateCalc to test whether a # string is a date or a delta. Dates are tested first, so we need to # be able to pass a delta into this routine and have it correctly NOT # interpreted as a date. # # We will insist that the string contain something other than digits and # colons so that the following will get correctly interpreted as a date # rather than a delta: # 12:30 # 19980101 $delta=""; $delta=&ParseDateDelta($_) if (/[^:0-9]/); # Put parse in a simple loop for an easy exit. PARSE: { my(@tmp)=&Date_Split($_); if (@tmp) { ($y,$m,$d,$h,$mn,$s)=@tmp; last PARSE; } # Fundamental regular expressions my($month)=$Lang{$L}{"Month"}; # (jan|january|...) my(%month)=%{ $Lang{$L}{"MonthH"} }; # { jan=>1, ... } my($week)=$Lang{$L}{"Week"}; # (mon|monday|...) my(%week)=%{ $Lang{$L}{"WeekH"} }; # { mon=>1, monday=>1, ... } my($wom)=$Lang{$L}{"WoM"}; # (1st|...|fifth|last) my(%wom)=%{ $Lang{$L}{"WoMH"} }; # { 1st=>1,... fifth=>5,last=>-1 } my($dom)=$Lang{$L}{"DoM"}; # (1st|first|...31st) my(%dom)=%{ $Lang{$L}{"DoMH"} }; # { 1st=>1, first=>1, ... } my($ampmexp)=$Lang{$L}{"AmPm"}; # (am|pm) my($timeexp)=$Lang{$L}{"Times"}; # (noon|midnight) my($now)=$Lang{$L}{"Now"}; # now my($today)=$Lang{$L}{"Today"}; # today my($offset)=$Lang{$L}{"Offset"}; # (yesterday|tomorrow) my($zone)=$Zone{"zones"}; # (edt|est|...) my($day)='\s*'.$Lang{$L}{"Dabb"}; # \s*(?:d|day|days) my($mabb)='\s*'.$Lang{$L}{"Mabb"}; # \s*(?:mon|month|months) my($wkabb)='\s*'.$Lang{$L}{"Wabb"}; # \s*(?:w|wk|week|weeks) my($next)='\s*'.$Lang{$L}{"Next"}; # \s*(?:next) my($prev)='\s*'.$Lang{$L}{"Prev"}; # \s*(?:last|previous) my($past)='\s*'.$Lang{$L}{"Past"}; # \s*(?:ago) my($future)='\s*'.$Lang{$L}{"Future"}; # \s*(?:in) my($later)='\s*'.$Lang{$L}{"Later"}; # \s*(?:later) my($at)=$Lang{$L}{"At"}; # (?:at) my($of)='\s*'.$Lang{$L}{"Of"}; # \s*(?:in|of) my($on)='(?:\s*'.$Lang{$L}{"On"}.'\s*|\s+)'; # \s*(?:on)\s* or \s+ my($last)='\s*'.$Lang{$L}{"Last"}; # \s*(?:last) my($hm)=$Lang{$L}{"SepHM"}; # : my($ms)=$Lang{$L}{"SepMS"}; # : my($ss)=$Lang{$L}{"SepSS"}; # . # Other regular expressions my($D4)='(\d{4})'; # 4 digits (yr) my($YY)='(\d{4}|\d{2})'; # 2 or 4 digits (yr) my($DD)='(\d{2})'; # 2 digits (mon/day/hr/min/sec) my($D) ='(\d{1,2})'; # 1 or 2 digit (mon/day/hr) my($FS)="(?:$ss\\d+)?"; # fractional secs my($sep)='[\/.-]'; # non-ISO8601 m/d/yy separators # absolute time zone +0700 (GMT) my($hzone)='(?:[0-1][0-9]|2[0-3])'; # 00 - 23 my($mzone)='(?:[0-5][0-9])'; # 00 - 59 my($zone2)='(?:\s*([+-](?:'."$hzone$mzone|$hzone:$mzone|$hzone))". # +0700 +07:00 -07 '(?:\s*\([^)]+\))?)'; # (GMT) # A regular expression for the time EXCEPT for the hour part my($mnsec)="$hm$DD(?:$ms$DD$FS)?(?:\\s*$ampmexp)?"; # A special regular expression for /YYYY:HH:MN:SS used by Apache my($apachetime)='(/\d{4}):' . "$DD$hm$DD$ms$DD"; my($time)=""; $ampm=""; $date=""; # Substitute all special time expressions. if (/(^|[^a-z])$timeexp($|[^a-z])/i) { $tmp=$2; $tmp=$Lang{$L}{"TimesH"}{lc($tmp)}; s/(^|[^a-z])$timeexp($|[^a-z])/$1 $tmp $3/i; } # Remove some punctuation s/[,]/ /g; # When we have a digit followed immediately by a timezone (7EST), we # will put a space between the digit, EXCEPT in the case of a single # character military timezone. If the single character is followed # by anything, no space is added. $tmp = ""; while ( s/^(.*?\d)$zone(\s|$|[0-9])/$3/i ) { my($bef,$z,$aft) = ($1,$2,$3); if (length($z) != 1 || length($aft) == 0) { $tmp .= "$bef $z"; } else { $tmp .= "$bef$z"; } } $_ = "$tmp$_"; $zone = '\s+' . $zone . '(?:\s+|$)'; # Remove the time $iso=1; $midnight=0; $from="24${hm}00(?:${ms}00)?"; $falsefrom="${hm}24${ms}00"; # Don't trap XX:24:00 $to="00${hm}00${ms}00"; $midnight=1 if (!/$falsefrom/ && s/$from/$to/); $h=$mn=$s=0; if (/$D$mnsec/i || /$ampmexp/i) { $iso=0; $tmp=0; $tmp=1 if (/$mnsec$zone2?\s*$/i or /$mnsec$zone\s*$/i); $tmp=0 if (/$ampmexp/i); if (s/$apachetime$zone()/$1 /i || s/$apachetime$zone2?/$1 /i || s/(^|[^a-z])$at\s*$D$mnsec$zone()/$1 /i || s/(^|[^a-z])$at\s*$D$mnsec$zone2?/$1 /i || s/(^|[^0-9])(\d)$mnsec$zone()/$1 /i || s/(^|[^0-9])(\d)$mnsec$zone2?/$1 /i || (s/(t)$D$mnsec$zone()/$1 /i and (($iso=$tmp) || 1)) || (s/(t)$D$mnsec$zone2?/$1 /i and (($iso=$tmp) || 1)) || (s/()$DD$mnsec$zone()/ /i and (($iso=$tmp) || 1)) || (s/()$DD$mnsec$zone2?/ /i and (($iso=$tmp) || 1)) || s/(^|$at\s*|\s+)$D()()\s*$ampmexp$zone()/ /i || s/(^|$at\s*|\s+)$D()()\s*$ampmexp$zone2?/ /i || 0 ) { ($h,$mn,$s,$ampm,$z,$z2)=($2,$3,$4,$5,$6,$7); if (defined ($z)) { if ($z =~ /^[+-]\d{2}:\d{2}$/) { $z=~ s/://; } elsif ($z =~ /^[+-]\d{2}$/) { $z .= "00"; } } $time=1; &Date_TimeCheck(\$h,\$mn,\$s,\$ampm); $y=$m=$d=""; # We're going to be calling TimeCheck again below (when we check the # final date), so get rid of $ampm so that we don't have an error # due to "15:30:00 PM". It'll get reset below. $ampm=""; if (/^\s*$/) { &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); last PARSE; } } } $time=0 if ($time ne "1"); s/\s+$//; s/^\s+//; # if a zone was found, get rid of the regexps if ($z) { $zone=""; $zone2=""; } # dateTtime ISO 8601 formats my($orig)=$_; # Parse ISO 8601 dates now (which may still have a zone stuck to it). if ( ($iso && /^([0-9-]+(?:W[0-9-]+)?)$zone?$/i) || ($iso && /^([0-9-]+(?:W[0-9-]+)?)$zone2?$/i) || ($iso && /^([0-9-]+(?:T[0-9-]+)?)$zone?$/i) || ($iso && /^([0-9-]+(?:T[0-9-]+)?)$zone2?$/i) || ($iso && /^([0-9-]+T)$zone?$/i) || ($iso && /^([0-9-]+T)$zone2?$/i) || 0) { # If we already got a timezone, don't get another one. my(@z); if ($z) { @z=($z,$z2); $z=""; } ($_,$z,$z2) = ($1,$2,$3); ($z,$z2)=@z if (@z); s,([0-9])\s*-,$1 ,g; # Change all ISO8601 seps to spaces s/^\s+//; s/\s+$//; if (/^$D4\s*$DD\s*$DD\s*t?$DD(?:$DD(?:$DD(\d*))?)?$/i || /^$DD\s+$DD\s*$DD\s*t?$DD(?:$DD(?:$DD(\d*))?)?$/i || 0 ) { # ISO 8601 Dates with times # YYYYMMDDtHHMNSSFFFF... # YYYYMMDDtHHMNSS # YYYYMMDDtHHMN # YYYYMMDDtHH # YY MMDDtHHMNSSFFFF... # YY MMDDtHHMNSS # YY MMDDtHHMN # YY MMDDtHH # The t is an optional letter "t". ($y,$m,$d,$h,$mn,$s,$tmp)=($1,$2,$3,$4,$5,$6,$7); if ($h==24 && (! defined $mn || $mn==0) && (! defined $s || $s==0)) { $h=0; $midnight=1; } $z = "" if (! defined $h); return "" if ($time && defined $h); last PARSE; } elsif (/^$D4(?:\s*$DD(?:\s*$DD)?)?$/ || /^$DD(?:\s+$DD(?:\s*$DD)?)?$/) { # ISO 8601 Dates # YYYYMMDD # YYYYMM # YYYY # YY MMDD # YY MM # YY ($y,$m,$d)=($1,$2,$3); last PARSE; } elsif (/^$YY\s+$D\s+$D/) { # YY-M-D ($y,$m,$d)=($1,$2,$3); last PARSE; } elsif (/^$YY\s*W$DD\s*(\d)?$/i) { # YY-W##-D ($y,$wofm,$dofw)=($1,$2,$3); ($y,$m,$d)=&Date_NthWeekOfYear($y,$wofm,$dofw); last PARSE; } elsif (/^$D4\s*(\d{3})$/ || /^$DD\s*(\d{3})$/) { # YYDOY ($y,$which)=($1,$2); ($y,$m,$d)=&Date_NthDayOfYear($y,$which); last PARSE; } elsif ($iso<0) { # We confused something like 1999/August12:00:00 # with a dateTtime format $_=$orig; } else { return ""; } } # All deltas that are not ISO-8601 dates are NOT dates. return "" if ($Curr{"InCalc"} && $delta); if ($delta) { &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); return &DateCalc_DateDelta($Curr{"Now"},$delta); } # Check for some special types of dates (next, prev) foreach $from (keys %{ $Lang{$L}{"Repl"} }) { $to=$Lang{$L}{"Repl"}{$from}; s/(^|[^a-z])$from($|[^a-z])/$1$to$2/i; } if (/$wom/i || /$future/i || /$later/i || /$past/i || /$next/i || /$prev/i || /^$week$/i || /$wkabb/i) { $tmp=0; if (/^$wom\s*$week$of\s*$month\s*$YY?$/i) { # last friday in October 95 ($wofm,$dofw,$m,$y)=($1,$2,$3,$4); # fix $m, $y return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); $dofw=$week{lc($dofw)}; $wofm=$wom{lc($wofm)}; # Get the first day of the month $date=&Date_Join($y,$m,1,$h,$mn,$s); if ($wofm==-1) { $date=&DateCalc_DateDelta($date,"+0:1:0:0:0:0:0",\$err,0); $date=&Date_GetPrev($date,$dofw,0); } else { for ($i=0; $i<$wofm; $i++) { if ($i==0) { $date=&Date_GetNext($date,$dofw,1); } else { $date=&Date_GetNext($date,$dofw,0); } } } last PARSE; } elsif (/^$last$day$of\s*$month(?:$of?\s*$YY)?/i) { # last day in month ($m,$y)=($1,$2); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $y=&Date_FixYear($y) if (! defined $y or length($y)<4); $m=$month{lc($m)}; $d=&Date_DaysInMonth($m,$y); last PARSE; } elsif (/^$week$/i) { # friday ($dofw)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&Date_GetPrev($Curr{"Now"},$Cnf{"FirstDay"},1); $date=&Date_GetNext($date,$dofw,1,$h,$mn,$s); last PARSE; } elsif (/^$next\s*$week$/i) { # next friday ($dofw)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&Date_GetNext($Curr{"Now"},$dofw,0,$h,$mn,$s); last PARSE; } elsif (/^$prev\s*$week$/i) { # last friday ($dofw)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&Date_GetPrev($Curr{"Now"},$dofw,0,$h,$mn,$s); last PARSE; } elsif (/^$next$wkabb$/i) { # next week &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"+0:0:1:0:0:0:0",\$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$prev$wkabb$/i) { # last week &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"-0:0:1:0:0:0:0",\$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$next$mabb$/i) { # next month &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"+0:1:0:0:0:0:0",\$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$prev$mabb$/i) { # last month &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"-0:1:0:0:0:0:0",\$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$future\s*(\d+)$day$/i || /^(\d+)$day$later$/i) { # in 2 days # 2 days later ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"+0:0:0:$num:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^(\d+)$day$past$/i) { # 2 days ago ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"-0:0:0:$num:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$future\s*(\d+)$wkabb$/i || /^(\d+)$wkabb$later$/i) { # in 2 weeks # 2 weeks later ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"+0:0:$num:0:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^(\d+)$wkabb$past$/i) { # 2 weeks ago ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"-0:0:$num:0:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$future\s*(\d+)$mabb$/i || /^(\d+)$mabb$later$/i) { # in 2 months # 2 months later ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"+0:$num:0:0:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^(\d+)$mabb$past$/i) { # 2 months ago ($num)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"},"-0:$num:0:0:0:0:0", \$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if (defined $h); last PARSE; } elsif (/^$week$future\s*(\d+)$wkabb$/i || /^$week\s*(\d+)$wkabb$later$/i) { # friday in 2 weeks # friday 2 weeks later ($dofw,$num)=($1,$2); $tmp="+"; } elsif (/^$week\s*(\d+)$wkabb$past$/i) { # friday 2 weeks ago ($dofw,$num)=($1,$2); $tmp="-"; } elsif (/^$future\s*(\d+)$wkabb$on$week$/i || /^(\d+)$wkabb$later$on$week$/i) { # in 2 weeks on friday # 2 weeks later on friday ($num,$dofw)=($1,$2); $tmp="+" } elsif (/^(\d+)$wkabb$past$on$week$/i) { # 2 weeks ago on friday ($num,$dofw)=($1,$2); $tmp="-"; } elsif (/^$week\s*$wkabb$/i) { # monday week (British date: in 1 week on monday) $dofw=$1; $num=1; $tmp="+"; } elsif ( (/^$now\s*$wkabb$/i && ($tmp="Now")) || (/^$today\s*$wkabb$/ && ($tmp="Today")) ) { # now week (British date: 1 week from now) # today week (British date: 1 week from today) &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{$tmp},"+0:0:1:0:0:0:0",\$err,0); $date=&Date_SetTime($date,$h,$mn,$s) if ($time); last PARSE; } elsif (/^$offset\s*$wkabb$/i) { # tomorrow week (British date: 1 week from tomorrow) ($offset)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $offset=$Lang{$L}{"OffsetH"}{lc($offset)}; $date=&DateCalc_DateDelta($Curr{"Now"},$offset,\$err,0); $date=&DateCalc_DateDelta($date,"+0:0:1:0:0:0:0",\$err,0); if ($time) { return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); $date=&Date_SetTime($date,$h,$mn,$s); } last PARSE; } if ($tmp) { &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=&DateCalc_DateDelta($Curr{"Now"}, $tmp . "0:0:$num:0:0:0:0",\$err,0); $date=&Date_GetPrev($date,$Cnf{"FirstDay"},1); $date=&Date_GetNext($date,$dofw,1,$h,$mn,$s); last PARSE; } } # Change (2nd, second) to 2 $tmp=0; if (/(^|[^a-z0-9])$dom($|[^a-z0-9])/i) { if (/^\s*$dom\s*$/) { ($d)=($1); $d=$dom{lc($d)}; $m=$Curr{"M"}; last PARSE; } my $from = $2; my $to = $dom{ lc($from) }; s/(^|[^a-z])$from($|[^a-z])/$1 $to $2/i; s/^\s+//; s/\s+$//; } # Another set of special dates (Nth week) if (/^$D\s*$week(?:$of?\s*$YY)?$/i) { # 22nd sunday in 1996 ($which,$dofw,$y)=($1,$2,$3); $y=$Curr{"Y"} if (! $y); $y--; # previous year $tmp=&Date_GetNext("$y-12-31",$dofw,0); if ($which>1) { $tmp=&DateCalc_DateDelta($tmp,"+0:0:".($which-1).":0:0:0:0",\$err,0); } ($y,$m,$d)=(&Date_Split($tmp, 1))[0..2]; last PARSE; } elsif (/^$week$wkabb\s*$D(?:$of?\s*$YY)?$/i || /^$week\s*$D$wkabb(?:$of?\s*$YY)?$/i) { # sunday week 22 in 1996 # sunday 22nd week in 1996 ($dofw,$which,$y)=($1,$2,$3); ($y,$m,$d)=&Date_NthWeekOfYear($y,$which,$dofw); last PARSE; } # Get rid of day of week if (/(^|[^a-z])$week($|[^a-z])/i) { $wk=$2; (s/(^|[^a-z])$week,/$1 /i) || s/(^|[^a-z])$week($|[^a-z])/$1 $3/i; s/^\s+//; s/\s+$//; } { # So that we can handle negative epoch times, let's convert # things like "epoch -" to "epochNEGATIVE " before we strip out # the $sep chars, which include '-'. s,epoch\s*-,epochNEGATIVE ,g; # Non-ISO8601 dates s,\s*$sep\s*, ,g; # change all non-ISO8601 seps to spaces s,^\s*,,; # remove leading/trailing space s,\s*$,,; if (/^$D\s+$D(?:\s+$YY)?$/) { # MM DD YY (DD MM YY non-US) ($m,$d,$y)=($1,$2,$3); ($m,$d)=($d,$m) if ($type ne "US"); last PARSE; } elsif (/^$D4\s*$D\s*$D$/) { # YYYY MM DD ($y,$m,$d)=($1,$2,$3); last PARSE; } elsif (s/(^|[^a-z])$month($|[^a-z])/$1 $3/i) { ($m)=($2); if (/^\s*$D(?:\s+$YY)?\s*$/) { # mmm DD YY # DD mmm YY # DD YY mmm ($d,$y)=($1,$2); last PARSE; } elsif (/^\s*$D$D4\s*$/) { # mmm DD YYYY # DD mmm YYYY # DD YYYY mmm ($d,$y)=($1,$2); last PARSE; } elsif (/^\s*$D4\s*$D\s*$/) { # mmm YYYY DD # YYYY mmm DD # YYYY DD mmm ($y,$d)=($1,$2); last PARSE; } elsif (/^\s*$D4\s*$/) { # mmm YYYY # YYYY mmm ($y,$d)=($1,1); last PARSE; } else { return ""; } } elsif (/^epochNEGATIVE (\d+)$/) { $s=$1; $date=&DateCalc("1970-01-01 00:00 GMT","-0:0:$s"); } elsif (/^epoch\s*(\d+)$/i) { $s=$1; $date=&DateCalc("1970-01-01 00:00 GMT","+0:0:$s"); } elsif ( (/^$now$/i && ($tmp="Now")) || (/^$today$/ && ($tmp="Today")) ) { # now, today &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $date=$Curr{$tmp}; if ($time) { return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); $date=&Date_SetTime($date,$h,$mn,$s); } last PARSE; } elsif (/^$offset$/i) { # yesterday, tomorrow ($offset)=($1); &Date_Init() if (! $Cnf{"UpdateCurrTZ"}); $offset=$Lang{$L}{"OffsetH"}{lc($offset)}; $date=&DateCalc_DateDelta($Curr{"Now"},$offset,\$err,0); if ($time) { return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); $date=&Date_SetTime($date,$h,$mn,$s); } last PARSE; } else { return ""; } } } if (! $date) { return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); $date=&Date_Join($y,$m,$d,$h,$mn,$s); } $date=&Date_ConvTZ($date,$z); if ($midnight) { $date=&DateCalc_DateDelta($date,"+0:0:0:1:0:0:0"); } return $date; } sub ParseDate { print "DEBUG: ParseDate\n" if ($Curr{"Debug"} =~ /trace/); &Date_Init() if (! $Curr{"InitDone"}); my($args,@args,@a,$ref,$date)=(); @a=@_; # @a : is the list of args to ParseDate. Currently, only one argument # is allowed and it must be a scalar (or a reference to a scalar) # or a reference to an array. if ($#a!=0) { print "ERROR: Invalid number of arguments to ParseDate.\n"; return ""; } $args=$a[0]; $ref=ref $args; if (! $ref) { return $args if (&Date_Split($args)); @args=($args); } elsif ($ref eq "ARRAY") { @args=@$args; } elsif ($ref eq "SCALAR") { return $$args if (&Date_Split($$args)); @args=($$args); } else { print "ERROR: Invalid arguments to ParseDate.\n"; return ""; } @a=@args; # @args : a list containing all the arguments (dereferenced if appropriate) # @a : a list containing all the arguments currently being examined # $ref : nil, "SCALAR", or "ARRAY" depending on whether a scalar, a # reference to a scalar, or a reference to an array was passed in # $args : the scalar or refererence passed in PARSE: while($#a>=0) { $date=join(" ",@a); $date=&ParseDateString($date); last if ($date); pop(@a); } # PARSE splice(@args,0,$#a + 1); @$args= @args if (defined $ref and $ref eq "ARRAY"); $date; } sub Date_Cmp { my($D1,$D2)=@_; my($date1)=&ParseDateString($D1); my($date2)=&ParseDateString($D2); return $date1 cmp $date2; } # **NOTE** # The calc routines all call parse routines, so it is never necessary to # call Date_Init in the calc routines. sub DateCalc { print "DEBUG: DateCalc\n" if ($Curr{"Debug"} =~ /trace/); my($D1,$D2,@arg)=@_; my($ref,$err,$errref,$mode)=(); $errref=shift(@arg); $ref=0; if (defined $errref) { if (ref $errref) { $mode=shift(@arg); $ref=1; } else { $mode=$errref; $errref=""; } } my(@date,@delta,$ret,$tmp,$oldincalc,$oldmode)=(); if (exists $Curr{"Mode"}) { $oldmode = $Curr{"Mode"}; } else { $oldmode = 0; } if (defined $mode and $mode>=0 and $mode<=3) { $Curr{"Mode"}=$mode; } else { $Curr{"Mode"}=0; } if (exists $Curr{"InCalc"}) { $oldincalc = $Curr{"InCalc"}; } else { $oldincalc = 0; } $Curr{"InCalc"}=1; if ($tmp=&ParseDateString($D1)) { # If we've already parsed the date, we don't want to do it a second # time (so we don't convert timezones twice). if (&Date_Split($D1)) { push(@date,$D1); } else { push(@date,$tmp); } } elsif ($tmp=&ParseDateDelta($D1)) { push(@delta,$tmp); } else { $$errref=1 if ($ref); $Curr{"InCalc"} = $oldincalc; $Curr{"Mode"} = $oldmode; return; } if ($tmp=&ParseDateString($D2)) { if (&Date_Split($D2)) { push(@date,$D2); } else { push(@date,$tmp); } } elsif ($tmp=&ParseDateDelta($D2)) { push(@delta,$tmp); } else { $$errref=2 if ($ref); $Curr{"InCalc"} = $oldincalc; $Curr{"Mode"} = $oldmode; return; } $Curr{"InCalc"} = $oldincalc; $Curr{"Mode"} = $oldmode; if ($#date==1) { $ret=&DateCalc_DateDate(@date,$mode); } elsif ($#date==0) { $ret=&DateCalc_DateDelta(@date,@delta,\$err,$mode); $$errref=$err if ($ref); } else { $ret=&DateCalc_DeltaDelta(@delta,$mode); } $ret; } sub ParseDateDelta { print "DEBUG: ParseDateDelta\n" if ($Curr{"Debug"} =~ /trace/); my($args,@args,@a,$ref)=(); local($_)=(); @a=@_; # @a : is the list of args to ParseDateDelta. Currently, only one argument # is allowed and it must be a scalar (or a reference to a scalar) # or a reference to an array. if ($#a!=0) { print "ERROR: Invalid number of arguments to ParseDateDelta.\n"; return ""; } $args=$a[0]; $ref=ref $args; if (! $ref) { @args=($args); } elsif ($ref eq "ARRAY") { @args=@$args; } elsif ($ref eq "SCALAR") { @args=($$args); } else { print "ERROR: Invalid arguments to ParseDateDelta.\n"; return ""; } @a=@args; # @args : a list containing all the arguments (dereferenced if appropriate) # @a : a list containing all the arguments currently being examined # $ref : nil, "SCALAR", or "ARRAY" depending on whether a scalar, a # reference to a scalar, or a reference to an array was passed in # $args : the scalar or refererence passed in my(@colon,@delta,$delta,$dir,$colon,$sign,$val)=(); my($len,$tmp,$tmp2,$tmpl)=(); my($from,$to)=(); my($workweek)=$Cnf{"WorkWeekEnd"}-$Cnf{"WorkWeekBeg"}+1; &Date_Init() if (! $Curr{"InitDone"}); # A sign can be a sequence of zero or more + and - signs, this # allows for deltas like '+ -2 days'. my($signexp)='((?:[+-]\s*)*)'; my($numexp)='(\d+)'; my($exp1)="(?: \\s* $signexp \\s* $numexp \\s*)"; my($yexp,$mexp,$wexp,$dexp,$hexp,$mnexp,$sexp,$i)=(); $yexp=$mexp=$wexp=$dexp=$hexp=$mnexp=$sexp="()()"; $yexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Yabb"} .")?"; $mexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Mabb"} .")?"; $wexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Wabb"} .")?"; $dexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Dabb"} .")?"; $hexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Habb"} .")?"; $mnexp="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"MNabb"}.")?"; $sexp ="(?: $exp1 ". $Lang{$Cnf{"Language"}}{"Sabb"} ."?)?"; my($future)=$Lang{$Cnf{"Language"}}{"Future"}; my($later)=$Lang{$Cnf{"Language"}}{"Later"}; my($past)=$Lang{$Cnf{"Language"}}{"Past"}; $delta=""; PARSE: while (@a) { $_ = join(" ", grep {defined;} @a); s/\s+$//; last if ($_ eq ""); # Mode is set in DateCalc. ParseDateDelta only overrides it if the # string contains a mode. if ($Lang{$Cnf{"Language"}}{"Exact"} && s/$Lang{$Cnf{"Language"}}{"Exact"}//) { $Curr{"Mode"}=0; } elsif ($Lang{$Cnf{"Language"}}{"Approx"} && s/$Lang{$Cnf{"Language"}}{"Approx"}//) { $Curr{"Mode"}=1; } elsif ($Lang{$Cnf{"Language"}}{"Business"} && s/$Lang{$Cnf{"Language"}}{"Business"}//) { $Curr{"Mode"}=2; } elsif (! exists $Curr{"Mode"}) { $Curr{"Mode"}=0; } $workweek=7 if ($Curr{"Mode"} != 2); foreach $from (keys %{ $Lang{$Cnf{"Language"}}{"Repl"} }) { $to=$Lang{$Cnf{"Language"}}{"Repl"}{$from}; s/(^|[^a-z])$from($|[^a-z])/$1$to$2/i; } # in or ago # # We need to make sure that $later, $future, and $past don't contain each # other... Romanian pointed this out where $past is "in urma" and $future # is "in". When they do, we have to take this into account. # $len length of best match (greatest wins) # $tmp string after best match # $dir direction (prior, after) of best match # # $tmp2 string before/after current match # $tmpl length of current match $len=0; $tmp=$_; $dir=1; $tmp2=$_; if ($tmp2 =~ s/(^|[^a-z])($future)($|[^a-z])/$1 $3/i) { $tmpl=length($2); if ($tmpl>$len) { $tmp=$tmp2; $dir=1; $len=$tmpl; } } $tmp2=$_; if ($tmp2 =~ s/(^|[^a-z])($later)($|[^a-z])/$1 $3/i) { $tmpl=length($2); if ($tmpl>$len) { $tmp=$tmp2; $dir=1; $len=$tmpl; } } $tmp2=$_; if ($tmp2 =~ s/(^|[^a-z])($past)($|[^a-z])/$1 $3/i) { $tmpl=length($2); if ($tmpl>$len) { $tmp=$tmp2; $dir=-1; $len=$tmpl; } } $_ = $tmp; s/\s*$//; # the colon part of the delta $colon=""; if (s/($signexp?$numexp?(:($signexp?$numexp)?){1,6})$//) { $colon=$1; s/\s+$//; } @colon=split(/:/,$colon); # the non-colon part of the delta $sign="+"; @delta=(); $i=6; foreach $exp1 ($yexp,$mexp,$wexp,$dexp,$hexp,$mnexp,$sexp) { last if ($#colon>=$i--); $val=0; if (s/^$exp1//ix) { $val=$2 if ($2); $sign=$1 if ($1); } # Collapse a sign like '+ -' into a single character like '-', # by counting the occurrences of '-'. # $sign =~ s/\s+//g; $sign =~ tr/+//d; my $count = ($sign =~ tr/-//d); die "bad characters in sign: $sign" if length $sign; $sign = $count % 2 ? '-' : '+'; push(@delta,"$sign$val"); } if (! /^\s*$/) { pop(@a); next PARSE; } # make sure that the colon part has a sign for ($i=0; $i<=$#colon; $i++) { $val=0; if ($colon[$i] =~ /^$signexp$numexp?/) { $val=$2 if ($2); $sign=$1 if ($1); } $colon[$i] = "$sign$val"; } # combine the two push(@delta,@colon); if ($dir<0) { for ($i=0; $i<=$#delta; $i++) { $delta[$i] =~ tr/-+/+-/; } } # form the delta and shift off the valid part $delta=join(":",@delta); splice(@args,0,$#a+1); @$args=@args if (defined $ref and $ref eq "ARRAY"); last PARSE; } $delta=&Delta_Normalize($delta,$Curr{"Mode"}); return $delta; } sub UnixDate { print "DEBUG: UnixDate\n" if ($Curr{"Debug"} =~ /trace/); my($date,@format)=@_; local($_)=(); my($format,%f,$out,@out,$c,$date1,$date2,$tmp)=(); my($scalar)=(); $date=&ParseDateString($date); return if (! $date); my($y,$m,$d,$h,$mn,$s)=($f{"Y"},$f{"m"},$f{"d"},$f{"H"},$f{"M"},$f{"S"})= &Date_Split($date, 1); $f{"y"}=substr $f{"Y"},2; &Date_Init() if (! $Curr{"InitDone"}); if (! wantarray) { $format=join(" ",@format); @format=($format); $scalar=1; } # month, week $_=$m; s/^0//; $f{"b"}=$f{"h"}=$Lang{$Cnf{"Language"}}{"MonL"}[$_-1]; $f{"B"}=$Lang{$Cnf{"Language"}}{"MonthL"}[$_-1]; $_=$m; s/^0/ /; $f{"f"}=$_; $f{"U"}=&Date_WeekOfYear($m,$d,$y,7); $f{"W"}=&Date_WeekOfYear($m,$d,$y,1); # check week 52,53 and 0 $f{"G"}=$f{"L"}=$y; if ($f{"W"}>=52 || $f{"U"}>=52) { my($dd,$mm,$yy)=($d,$m,$y); $dd+=7; if ($dd>31) { $dd-=31; $mm=1; $yy++; if (&Date_WeekOfYear($mm,$dd,$yy,1)==2) { $f{"G"}=$yy; $f{"W"}=1; } if (&Date_WeekOfYear($mm,$dd,$yy,7)==2) { $f{"L"}=$yy; $f{"U"}=1; } } } if ($f{"W"}==0) { my($dd,$mm,$yy)=($d,$m,$y); $dd-=7; $dd+=31 if ($dd<1); $yy--; $mm=12; $f{"G"}=$yy; $f{"W"}=&Date_WeekOfYear($mm,$dd,$yy,1)+1; } if ($f{"U"}==0) { my($dd,$mm,$yy)=($d,$m,$y); $dd-=7; $dd+=31 if ($dd<1); $yy--; $mm=12; $f{"L"}=$yy; $f{"U"}=&Date_WeekOfYear($mm,$dd,$yy,7)+1; } $f{"U"}="0".$f{"U"} if (length $f{"U"} < 2); $f{"W"}="0".$f{"W"} if (length $f{"W"} < 2); # day $f{"j"}=&Date_DayOfYear($m,$d,$y); $f{"j"} = "0" . $f{"j"} while (length($f{"j"})<3); $_=$d; s/^0/ /; $f{"e"}=$_; $f{"w"}=&Date_DayOfWeek($m,$d,$y); $f{"v"}=$Lang{$Cnf{"Language"}}{"WL"}[$f{"w"}-1]; $f{"v"}=" ".$f{"v"} if (length $f{"v"} < 2); $f{"a"}=$Lang{$Cnf{"Language"}}{"WkL"}[$f{"w"}-1]; $f{"A"}=$Lang{$Cnf{"Language"}}{"WeekL"}[$f{"w"}-1]; $f{"E"}=&Date_DaySuffix($f{"e"}); # hour $_=$h; s/^0/ /; $f{"k"}=$_; $f{"i"}=$f{"k"}+1; $f{"i"}=$f{"k"}; $f{"i"}=12 if ($f{"k"}==0); $f{"i"}=$f{"k"}-12 if ($f{"k"}>12); $f{"i"}=$f{"i"}-12 if ($f{"i"}>12); $f{"i"}=" ".$f{"i"} if (length($f{"i"})<2); $f{"I"}=$f{"i"}; $f{"I"}=~ s/^ /0/; $f{"p"}=$Lang{$Cnf{"Language"}}{"AMstr"}; $f{"p"}=$Lang{$Cnf{"Language"}}{"PMstr"} if ($f{"k"}>11); # minute, second, timezone $f{"o"}=&Date_SecsSince1970($m,$d,$y,$h,$mn,$s); $f{"s"}=&Date_SecsSince1970GMT($m,$d,$y,$h,$mn,$s); $f{"Z"}=($Cnf{"ConvTZ"} eq "IGNORE" or $Cnf{"ConvTZ"} eq "") ? $Cnf{"TZ"} : $Cnf{"ConvTZ"}; $f{"z"}=($f{"Z"}=~/^[+-]\d{4}/) ? $f{"Z"} : ($Zone{"n2o"}{lc $f{"Z"}} || ""); # date, time $f{"c"}=qq|$f{"a"} $f{"b"} $f{"e"} $h:$mn:$s $y|; $f{"C"}=$f{"u"}= qq|$f{"a"} $f{"b"} $f{"e"} $h:$mn:$s $f{"z"} $y|; $f{"g"}=qq|$f{"a"}, $d $f{"b"} $y $h:$mn:$s $f{"z"}|; $f{"D"}=$f{"x"}=qq|$m/$d/$f{"y"}|; $f{"x"}=qq|$d/$m/$f{"y"}| if ($Cnf{"DateFormat"} ne "US"); $f{"r"}=qq|$f{"I"}:$mn:$s $f{"p"}|; $f{"R"}=qq|$h:$mn|; $f{"T"}=$f{"X"}=qq|$h:$mn:$s|; $f{"V"}=qq|$m$d$h$mn$f{"y"}|; $f{"Q"}="$y$m$d"; $f{"q"}=qq|$y$m$d$h$mn$s|; $f{"P"}=qq|$y$m$d$h:$mn:$s|; $f{"O"}=qq|$y-$m-${d}T$h:$mn:$s|; $f{"F"}=qq|$f{"A"}, $f{"B"} $f{"e"}, $f{"Y"}|; if ($f{"W"}==0) { $y--; $tmp=&Date_WeekOfYear(12,31,$y,1); $tmp="0$tmp" if (length($tmp) < 2); $f{"J"}=qq|$y-W$tmp-$f{"w"}|; } else { $f{"J"}=qq|$f{"G"}-W$f{"W"}-$f{"w"}|; } $f{"K"}=qq|$y-$f{"j"}|; # %l is a special case. Since it requires the use of the calculator # which requires this routine, an infinite recursion results. To get # around this, %l is NOT determined every time this is called so the # recursion breaks. # other formats $f{"n"}="\n"; $f{"t"}="\t"; $f{"%"}="%"; $f{"+"}="+"; foreach $format (@format) { $format=reverse($format); $out=""; while ($format ne "") { $c=chop($format); if ($c eq "%") { $c=chop($format); if ($c eq "l") { &Date_Init(); $date1=&DateCalc_DateDelta($Curr{"Now"},"-0:6:0:0:0:0:0"); $date2=&DateCalc_DateDelta($Curr{"Now"},"+0:6:0:0:0:0:0"); if (&Date_Cmp($date,$date1)>=0 && &Date_Cmp($date,$date2)<=0) { $f{"l"}=qq|$f{"b"} $f{"e"} $h:$mn|; } else { $f{"l"}=qq|$f{"b"} $f{"e"} $f{"Y"}|; } $out .= $f{"$c"}; } elsif (exists $f{"$c"}) { $out .= $f{"$c"}; } else { $out .= $c; } } else { $out .= $c; } } push(@out,$out); } if ($scalar) { return $out[0]; } else { return (@out); } } # Can't be in "use integer" because we're doing decimal arithmatic no integer; sub Delta_Format { print "DEBUG: Delta_Format\n" if ($Curr{"Debug"} =~ /trace/); my($delta,@arg)=@_; my($mode); if (lc($arg[0]) eq "approx") { $mode = "approx"; shift(@arg); } else { $mode = "exact"; } my($dec,@format) = @arg; $delta=&ParseDateDelta($delta); return "" if (! $delta); my(@out,%f,$out,$c1,$c2,$scalar,$format)=(); local($_)=$delta; my($y,$M,$w,$d,$h,$m,$s)=&Delta_Split($delta); # Get rid of positive signs. ($y,$M,$w,$d,$h,$m,$s)=map { 1*$_; }($y,$M,$w,$d,$h,$m,$s); if (defined $dec && $dec>0) { $dec="%." . ($dec*1) . "f"; } else { $dec="%f"; } if (! wantarray) { $format=join(" ",@format); @format=($format); $scalar=1; } # Length of each unit in seconds my($sl,$ml,$hl,$dl,$wl,$Ml,$yl)=(); $sl = 1; $ml = $sl*60; $hl = $ml*60; $dl = $hl*24; $wl = $dl*7; $yl = $dl*365.25; $Ml = $yl/12; # The decimal amount of each unit contained in all smaller units my($yd,$Md,$sd,$md,$hd,$dd,$wd)=(); if ($mode eq "exact") { $yd = $M/12; $Md = 0; } else { $yd = ($M*$Ml + $w*$wl + $d*$dl + $h*$hl + $m*$ml + $s*$sl)/$yl; $Md = ($w*$wl + $d*$dl + $h*$hl + $m*$ml + $s*$sl)/$Ml; } $wd = ($d*$dl + $h*$hl + $m*$ml + $s*$sl)/$wl; $dd = ($h*$hl + $m*$ml + $s*$sl)/$dl; $hd = ($m*$ml + $s*$sl)/$hl; $md = ($s*$sl)/$ml; $sd = 0; # The amount of each unit contained in higher units. my($yh,$Mh,$sh,$mh,$hh,$dh,$wh)=(); $yh = 0; $Mh = ($yh+$y)*12; if ($mode eq "exact") { $wh = 0; $dh = ($wh+$w)*7; } else { $wh = ($yh+$y+$M/12)*365.25/7; $dh = ($wh+$w)*7; } $hh = ($dh+$d)*24; $mh = ($hh+$h)*60; $sh = ($mh+$m)*60; # Set up the formats $f{"yv"} = $y; $f{"Mv"} = $M; $f{"wv"} = $w; $f{"dv"} = $d; $f{"hv"} = $h; $f{"mv"} = $m; $f{"sv"} = $s; $f{"yh"} = $y+$yh; $f{"Mh"} = $M+$Mh; $f{"wh"} = $w+$wh; $f{"dh"} = $d+$dh; $f{"hh"} = $h+$hh; $f{"mh"} = $m+$mh; $f{"sh"} = $s+$sh; $f{"yd"} = sprintf($dec,$y+$yd); $f{"Md"} = sprintf($dec,$M+$Md); $f{"wd"} = sprintf($dec,$w+$wd); $f{"dd"} = sprintf($dec,$d+$dd); $f{"hd"} = sprintf($dec,$h+$hd); $f{"md"} = sprintf($dec,$m+$md); $f{"sd"} = sprintf($dec,$s+$sd); $f{"yt"} = sprintf($dec,$yh+$y+$yd); $f{"Mt"} = sprintf($dec,$Mh+$M+$Md); $f{"wt"} = sprintf($dec,$wh+$w+$wd); $f{"dt"} = sprintf($dec,$dh+$d+$dd); $f{"ht"} = sprintf($dec,$hh+$h+$hd); $f{"mt"} = sprintf($dec,$mh+$m+$md); $f{"st"} = sprintf($dec,$sh+$s+$sd); $f{"%"} = "%"; foreach $format (@format) { $format=reverse($format); $out=""; PARSE: while ($format) { $c1=chop($format); if ($c1 eq "%") { $c1=chop($format); if (exists($f{$c1})) { $out .= $f{$c1}; next PARSE; } $c2=chop($format); if (exists($f{"$c1$c2"})) { $out .= $f{"$c1$c2"}; next PARSE; } $out .= $c1; $format .= $c2; } else { $out .= $c1; } } push(@out,$out); } if ($scalar) { return $out[0]; } else { return (@out); } } use integer; sub ParseRecur { print "DEBUG: ParseRecur\n" if ($Curr{"Debug"} =~ /trace/); &Date_Init() if (! $Curr{"InitDone"}); my($recur,$dateb,$date0,$date1,$flag)=@_; local($_)=$recur; my($recur_0,$recur_1,@recur0,@recur1)=(); my(@tmp,$tmp,$each,$num,$y,$m,$d,$w,$h,$mn,$s,$delta,$y0,$y1,$yb)=(); my($yy,$n,$dd,@d,@tmp2,$date,@date,@w,@tmp3,@m,@y,$tmp2,$d2,@flags)=(); # $date0, $date1, $dateb, $flag : passed in (these are always the final say # in determining whether a date matches a # recurrence IF they are present. # $date_b, $date_0, $date_1 : if a value can be determined from the # $flag_t recurrence, they are stored here. # # If values can be determined from the recurrence AND are passed in, the # following are used: # max($date0,$date_0) i.e. the later of the two dates # min($date1,$date_1) i.e. the earlier of the two dates # # The base date that is used is the first one defined from # $dateb $date_b # The base date is only used if necessary (as determined by the recur). # For example, "every other friday" requires a base date, but "2nd # friday of every month" doesn't. my($date_b,$date_0,$date_1,$flag_t); # # Check the arguments passed in. # $date0="" if (! defined $date0); $date1="" if (! defined $date1); $dateb="" if (! defined $dateb); $flag ="" if (! defined $flag); if ($dateb) { $dateb=&ParseDateString($dateb); return "" if (! $dateb); } if ($date0) { $date0=&ParseDateString($date0); return "" if (! $date0); } if ($date1) { $date1=&ParseDateString($date1); return "" if (! $date1); } # # Parse the recur. $date_b, $date_0, and $date_e are values obtained # from the recur. # @tmp=&Recur_Split($_); if (@tmp) { ($recur_0,$recur_1,$flag_t,$date_b,$date_0,$date_1)=@tmp; $recur_0 = "" if (! defined $recur_0); $recur_1 = "" if (! defined $recur_1); $flag_t = "" if (! defined $flag_t); $date_b = "" if (! defined $date_b); $date_0 = "" if (! defined $date_0); $date_1 = "" if (! defined $date_1); @recur0 = split(/:/,$recur_0); @recur1 = split(/:/,$recur_1); return "" if ($#recur0 + $#recur1 + 2 != 7); if ($date_b) { $date_b=&ParseDateString($date_b); return "" if (! $date_b); } if ($date_0) { $date_0=&ParseDateString($date_0); return "" if (! $date_0); } if ($date_1) { $date_1=&ParseDateString($date_1); return "" if (! $date_1); } } else { my($mmm)='\s*'.$Lang{$Cnf{"Language"}}{"Month"}; # \s*(jan|january|...) my(%mmm)=%{ $Lang{$Cnf{"Language"}}{"MonthH"} }; # { jan=>1, ... } my($wkexp)='\s*'.$Lang{$Cnf{"Language"}}{"Week"}; # \s*(mon|monday|...) my(%week)=%{ $Lang{$Cnf{"Language"}}{"WeekH"} }; # { monday=>1, ... } my($day)='\s*'.$Lang{$Cnf{"Language"}}{"Dabb"}; # \s*(?:d|day|days) my($month)='\s*'.$Lang{$Cnf{"Language"}}{"Mabb"}; # \s*(?:mon|month|months) my($week)='\s*'.$Lang{$Cnf{"Language"}}{"Wabb"}; # \s*(?:w|wk|week|weeks) my($daysexp)=$Lang{$Cnf{"Language"}}{"DoM"}; # (1st|first|...31st) my(%dayshash)=%{ $Lang{$Cnf{"Language"}}{"DoMH"} }; # { 1st=>1,first=>1,...} my($of)='\s*'.$Lang{$Cnf{"Language"}}{"Of"}; # \s*(?:in|of) my($lastexp)=$Lang{$Cnf{"Language"}}{"Last"}; # (?:last) my($each)=$Lang{$Cnf{"Language"}}{"Each"}; # (?:each|every) my($D)='\s*(\d+)'; my($Y)='\s*(\d{4}|\d{2})'; # Change 1st to 1 if (/(^|[^a-z])$daysexp($|[^a-z])/i) { $tmp=lc($2); $tmp=$dayshash{"$tmp"}; s/(^|[^a-z])$daysexp($|[^a-z])/$1 $tmp $3/i; } s/\s*$//; # Get rid of "each" if (/(^|[^a-z])$each($|[^a-z])/i) { s/(^|[^a-z])$each($|[^a-z])/$1 $2/i; $each=1; } else { $each=0; } if ($each) { if (/^$D?$day(?:$of$mmm?$Y)?$/i || /^$D?$day(?:$of$mmm())?$/i) { # every [2nd] day in [june] 1997 # every [2nd] day [in june] ($num,$m,$y)=($1,$2,$3); $num=1 if (! defined $num); $m="" if (! defined $m); $y="" if (! defined $y); $y=$Curr{"Y"} if (! $y); if ($m) { $m=$mmm{lc($m)}; $date_0=&Date_Join($y,$m,1,0,0,0); $date_1=&DateCalc_DateDelta($date_0,"+0:1:0:0:0:0:0",0); } else { $date_0=&Date_Join($y, 1,1,0,0,0); $date_1=&Date_Join($y+1,1,1,0,0,0); } $date_b=&DateCalc($date_0,"-0:0:0:1:0:0:0",0); @recur0=(0,0,0,$num,0,0,0); @recur1=(); } elsif (/^$D$day?$of$month(?:$of?$Y)?$/) { # 2nd [day] of every month [in 1997] ($num,$y)=($1,$2); $y=$Curr{"Y"} if (! $y); $date_0=&Date_Join($y, 1,1,0,0,0); $date_1=&Date_Join($y+1,1,1,0,0,0); $date_b=$date_0; @recur0=(0,1,0); @recur1=($num,0,0,0); } elsif (/^$D$wkexp$of$month(?:$of?$Y)?$/ || /^($lastexp)$wkexp$of$month(?:$of?$Y)?$/) { # 2nd tuesday of every month [in 1997] # last tuesday of every month [in 1997] ($num,$d,$y)=($1,$2,$3); $y=$Curr{"Y"} if (! $y); $d=$week{lc($d)}; $num=-1 if ($num !~ /^$D$/); $date_0=&Date_Join($y,1,1,0,0,0); $date_1=&Date_Join($y+1,1,1,0,0,0); $date_b=$date_0; @recur0=(0,1); @recur1=($num,$d,0,0,0); } elsif (/^$D?$wkexp(?:$of$mmm?$Y)?$/i || /^$D?$wkexp(?:$of$mmm())?$/i) { # every tuesday in june 1997 # every 2nd tuesday in june 1997 ($num,$d,$m,$y)=($1,$2,$3,$4); $y=$Curr{"Y"} if (! $y); $num=1 if (! defined $num); $m="" if (! defined $m); $d=$week{lc($d)}; if ($m) { $m=$mmm{lc($m)}; $date_0=&Date_Join($y,$m,1,0,0,0); $date_1=&DateCalc_DateDelta($date_0,"+0:1:0:0:0:0:0",0); } else { $date_0=&Date_Join($y,1,1,0,0,0); $date_1=&Date_Join($y+1,1,1,0,0,0); } $date_b=&DateCalc($date_0,"-0:0:0:1:0:0:0",0); @recur0=(0,0,$num); @recur1=($d,0,0,0); } else { return ""; } $date_0="" if ($date0); $date_1="" if ($date1); } else { return ""; } } # # Override with any values passed in # $date0 = $date_0 if (! $date0); $date1 = $date_1 if (! $date1); $dateb = $date_b if (! $dateb); if ($flag =~ s/^\+//) { $flag = "$flag_t,$flag" if ($flag_t); } $flag = $flag_t if (! $flag); $flag = "" if (! $flag); if (! wantarray) { $tmp = join(":",@recur0); $tmp .= "*" . join(":",@recur1) if (@recur1); $tmp .= "*$flag*$dateb*$date0*$date1"; return $tmp; } if (@recur0) { return () if (! $date0 || ! $date1); # dateb is NOT required in all case } # # Some flags affect parsing. # @flags = split(/,/,$flag); my($f); foreach $f (@flags) { if ($f =~ /^EASTER$/i) { ($y,$m,$w,$d,$h,$mn,$s)=(@recur0,@recur1); # We want something that will return Jan 1 for the given years. if ($#recur0==-1) { @recur1=($y,1,0,1,$h,$mn,$s); } elsif ($#recur0<=3) { @recur0=($y,0,0,0); @recur1=($h,$mn,$s); } elsif ($#recur0==4) { @recur0=($y,0,0,0,0); @recur1=($mn,$s); } elsif ($#recur0==5) { @recur0=($y,0,0,0,0,0); @recur1=($s); } else { @recur0=($y,0,0,0,0,0,0); } } } # # Determine the dates referenced by the recur. Also, fix the base date # as necessary for the recurrences which require it. # ($y,$m,$w,$d,$h,$mn,$s)=(@recur0,@recur1); @y=@m=@w=@d=(); my(@time)=($h,$mn,$s); RECUR: while (1) { if ($#recur0==-1) { # * 0-M-W-D-H-MN-S => 0 * M-W-D-H-MN-S if ($y eq "0") { push(@recur0,1); shift(@recur1); next RECUR; } # Y-M-W-D-H-MN-S @y=&ReturnList($y); foreach $y (@y) { $y=&Date_FixYear($y) if (length($y)==2); return () if (length($y)!=4 || ! &IsInt($y)); } $date0=&ParseDate("0000-01-01") if (! $date0); $date1=&ParseDate("9999-12-31 23:59:59") if (! $date1); if ($m eq "0" and $w eq "0") { # * Y-0-0-0-H-MN-S # * Y-0-0-DOY-H-MN-S if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { return () if (! &IsInt($d,-366,366) || $d==0); } } @date=(); foreach $yy (@y) { my $diy = &Date_DaysInYear($yy); foreach $d (@d) { my $tmpd = $d; $tmpd += ($diy+1) if ($tmpd < 0); next if (! &IsInt($tmpd,1,$diy)); ($y,$m,$dd)=&Date_NthDayOfYear($yy,$tmpd); push(@date, &Date_Join($y,$m,$dd,0,0,0)); } } last RECUR; } elsif ($w eq "0") { # * Y-M-0-0-H-MN-S # * Y-M-0-DOM-H-MN-S @m=&ReturnList($m); return () if (! @m); foreach $m (@m) { return () if (! &IsInt($m,1,12)); } if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { return () if (! &IsInt($d,-31,31) || $d==0); } } @date=(); foreach $y (@y) { foreach $m (@m) { my $dim = &Date_DaysInMonth($m,$y); foreach $d (@d) { my $tmpd = $d; $tmpd += ($dim+1) if ($d<0); next if (! &IsInt($tmpd,1,$dim)); $date=&Date_Join($y,$m,$tmpd,0,0,0); push(@date,$date); } } } last RECUR; } elsif ($m eq "0") { # * Y-0-WOY-DOW-H-MN-S # * Y-0-WOY-0-H-MN-S @w=&ReturnList($w); return () if (! @w); foreach $w (@w) { return () if (! &IsInt($w,-53,53) || $w==0); } if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { $d += 8 if ($d<0); return () if (! &IsInt($d,1,7)); } } @date=(); foreach $y (@y) { foreach $w (@w) { foreach $d (@d) { my($tmpw,$del); if ($w<0) { $date="$y-12-31-00:00:00"; $tmpw = (-$w)-1; $del="-0:0:$tmpw:0:0:0:0"; $date=Date_GetPrev($date,$d,1); } else { $date="$y-01-01-00:00:00"; $tmpw = ($w)-1; $del="0:0:$tmpw:0:0:0:0"; $date=Date_GetNext($date,$d,1); } $date=&DateCalc_DateDelta($date,$del); push(@date,$date) if ( (&Date_Split($date))[0] == $y); } } } last RECUR; } else { # * Y-M-WOM-DOW-H-MN-S # * Y-M-WOM-0-H-MN-S @m=&ReturnList($m); return () if (! @m); @w=&ReturnList($w); return () if (! @w); if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); } @date=&Date_Recur_WoM(\@y,\@m,\@w,\@d); last RECUR; } } if ($#recur0==0) { # Y * M-W-D-H-MN-S $n=$y; $n=1 if ($n==0); if ($m eq "0") { # Y * 0-W-D-H-MN-S => Y-0 * W-D-H-MN-S push(@recur0,0); shift(@recur1); } elsif ($w eq "0") { # Y * M-0-DOM-H-MN-S return () if (! $dateb && $y != 1); @m=&ReturnList($m); return () if (! @m); foreach $m (@m) { return () if (! &IsInt($m,1,12)); } if ($d eq "0") { @d = (1); } else { @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { return () if (! &IsInt($d,-31,31) || $d==0); } } # We need to find years that are a multiple of $n from $y(base) ($y0)=( &Date_Split($date0, 1) )[0]; ($y1)=( &Date_Split($date1, 1) )[0]; if ($dateb) { ($yb)=( &Date_Split($dateb, 1) )[0]; } else { # If $y=1, there is no base year $yb=0; } @date=(); for ($yy=$y0; $yy<=$y1; $yy++) { if (($yy-$yb)%$n == 0) { foreach $m (@m) { foreach $d (@d) { my $dim = &Date_DaysInMonth($m,$yy); my $tmpd = $d; if ($tmpd < 0) { $tmpd += ($dim+1); } next if (! &IsInt($tmpd,1,$dim)); $date=&Date_Join($yy,$m,$tmpd,0,0,0); push(@date,$date); } } } } last RECUR; } else { # Y * M-WOM-DOW-H-MN-S # Y * M-WOM-0-H-MN-S return () if (! $dateb && $y != 1); @m=&ReturnList($m); return () if (! @m); @w=&ReturnList($w); return () if (! @w); if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); } ($y0)=( &Date_Split($date0, 1) )[0]; ($y1)=( &Date_Split($date1, 1) )[0]; if ($dateb) { ($yb)=( &Date_Split($dateb, 1) )[0]; } else { # If $y=1, there is no base year $yb=0; } @y=(); for ($yy=$y0; $yy<=$y1; $yy++) { if (($yy-$yb)%$n == 0) { push(@y,$yy); } } @date=&Date_Recur_WoM(\@y,\@m,\@w,\@d); last RECUR; } } if ($#recur0==1) { # Y-M * W-D-H-MN-S if ($w eq "0") { # Y-M * 0-D-H-MN-S => Y-M-0 * D-H-MN-S push(@recur0,0); shift(@recur1); } elsif ($m==0) { # Y-0 * WOY-0-H-MN-S # Y-0 * WOY-DOW-H-MN-S return () if (! $dateb && $y != 1); $n=$y; $n=1 if ($n==0); @w=&ReturnList($w); return () if (! @w); foreach $w (@w) { return () if ($w==0 || ! &IsInt($w,-53,53)); } if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { $d += 8 if ($d<0); return () if (! &IsInt($d,1,7)); } } # We need to find years that are a multiple of $n from $y(base) ($y0)=( &Date_Split($date0, 1) )[0]; ($y1)=( &Date_Split($date1, 1) )[0]; if ($dateb) { ($yb)=( &Date_Split($dateb, 1) )[0]; } else { # If $y=1, there is no base year $yb=0; } @date=(); for ($yy=$y0; $yy<=$y1; $yy++) { if (($yy-$yb)%$n == 0) { foreach $w (@w) { foreach $d (@d) { my($tmpw,$del); if ($w<0) { $date="$yy-12-31-00:00:00"; $tmpw = (-$w)-1; $del="-0:0:$tmpw:0:0:0:0"; $date=Date_GetPrev($date,$d,1); } else { $date="$yy-01-01-00:00:00"; $tmpw = ($w)-1; $del="0:0:$tmpw:0:0:0:0"; $date=Date_GetNext($date,$d,1); } $date=&DateCalc($date,$del); next if ((&Date_Split($date))[0] != $yy); push(@date,$date); } } } } last RECUR; } else { # Y-M * WOM-0-H-MN-S # Y-M * WOM-DOW-H-MN-S return () if (! $dateb); @tmp=(@recur0); push(@tmp,0) while ($#tmp<6); $delta=join(":",@tmp); @tmp=&Date_Recur($date0,$date1,$dateb,$delta); @w=&ReturnList($w); @m=(); if ($d eq "0") { @d=(1); } else { @d=&ReturnList($d); } @date=&Date_Recur_WoM(\@tmp,\@m,\@w,\@d); last RECUR; } } if ($#recur0==2) { # Y-M-W * D-H-MN-S if ($d eq "0") { # Y-M-W * 0-H-MN-S return () if (! $dateb); $y=1 if ($y==0 && $m==0 && $w==0); $delta="$y:$m:$w:0:0:0:0"; @date=&Date_Recur($date0,$date1,$dateb,$delta); last RECUR; } elsif ($m==0 && $w==0) { # Y-0-0 * DOY-H-MN-S $y=1 if ($y==0); $n=$y; return () if (! $dateb && $y!=1); @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { return () if (! &IsInt($d,-366,366) || $d==0); } # We need to find years that are a multiple of $n from $y(base) ($y0)=( &Date_Split($date0, 1) )[0]; ($y1)=( &Date_Split($date1, 1) )[0]; if ($dateb) { ($yb)=( &Date_Split($dateb, 1) )[0]; } else { # If $y=1, there is no base year $yb=0; } @date=(); for ($yy=$y0; $yy<=$y1; $yy++) { my $diy = &Date_DaysInYear($yy); if (($yy-$yb)%$n == 0) { foreach $d (@d) { my $tmpd = $d; $tmpd += ($diy+1) if ($tmpd<0); next if (! &IsInt($tmpd,1,$diy)); ($y,$m,$dd)=&Date_NthDayOfYear($yy,$tmpd); push(@date, &Date_Join($y,$m,$dd,0,0,0)); } } } last RECUR; } elsif ($w>0) { # Y-M-W * DOW-H-MN-S return () if (! $dateb); @tmp=(@recur0); push(@tmp,0) while ($#tmp<6); $delta=join(":",@tmp); @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { $d += 8 if ($d<0); return () if (! &IsInt($d,1,7)); } # Find out what DofW the basedate is. @tmp2=&Date_Split($dateb, 1); $tmp=&Date_DayOfWeek($tmp2[1],$tmp2[2],$tmp2[0]); @date=(); foreach $d (@d) { $date_b=$dateb; # Move basedate to DOW in the same week if ($d != $tmp) { if (($tmp>=$Cnf{"FirstDay"} && $d<$Cnf{"FirstDay"}) || ($tmp>=$Cnf{"FirstDay"} && $d>$tmp) || ($tmp<$d && $d<$Cnf{"FirstDay"})) { $date_b=&Date_GetNext($date_b,$d); } else { $date_b=&Date_GetPrev($date_b,$d); } } push(@date,&Date_Recur($date0,$date1,$date_b,$delta)); } last RECUR; } elsif ($m>0) { # Y-M-0 * DOM-H-MN-S return () if (! $dateb); @tmp=(@recur0); push(@tmp,0) while ($#tmp<6); $delta=join(":",@tmp); @d=&ReturnList($d); return () if (! @d); foreach $d (@d) { return () if ($d==0 || ! &IsInt($d,-31,31)); } @tmp2=&Date_Recur($date0,$date1,$dateb,$delta); @date=(); foreach $date (@tmp2) { ($y,$m)=( &Date_Split($date, 1) )[0..1]; my $dim=&Date_DaysInMonth($m,$y); foreach $d (@d) { my $tmpd = $d; $tmpd += ($dim+1) if ($tmpd<0); next if (! &IsInt($tmpd,1,$dim)); push(@date,&Date_Join($y,$m,$tmpd,0,0,0)); } } last RECUR; } else { return (); } } if ($#recur0>2) { # Y-M-W-D * H-MN-S # Y-M-W-D-H * MN-S # Y-M-W-D-H-MN * S # Y-M-W-D-H-S return () if (! $dateb); @tmp=(@recur0); push(@tmp,0) while ($#tmp<6); $delta=join(":",@tmp); return () if ($delta !~ /[1-9]/); # return if "0:0:0:0:0:0:0" @date=&Date_Recur($date0,$date1,$dateb,$delta); if (@recur1) { unshift(@recur1,-1) while ($#recur1<2); @time=@recur1; } else { shift(@date); pop(@date); @time=(); } } last RECUR; } @date=&Date_RecurSetTime($date0,$date1,\@date,@time) if (@time); # # We've got a list of dates. Operate on them with the flags. # my($sign,$forw,$today,$df,$db,$work,$i); if (@flags) { FLAG: foreach $f (@flags) { $f = uc($f); if ($f =~ /^(P|N)(D|T)([1-7])$/) { @tmp=($1,$2,$3); $forw =($tmp[0] eq "P" ? 0 : 1); $today=($tmp[1] eq "D" ? 0 : 1); $d=$tmp[2]; @tmp=(); foreach $date (@date) { if ($forw) { push(@tmp, &Date_GetNext($date,$d,$today)); } else { push(@tmp, &Date_GetPrev($date,$d,$today)); } } @date=@tmp; next FLAG; } # We want to go forward exact amounts of time instead of # business mode calculations so that we don't change the time # (which may have been set in the recur). if ($f =~ /^(F|B)(D|W)(\d+)$/) { @tmp=($1,$2,$3); $sign="+"; $sign="-" if ($tmp[0] eq "B"); $work=0; $work=1 if ($tmp[1] eq "W"); $n=$tmp[2]; @tmp=(); foreach $date (@date) { for ($i=1; $i<=$n; $i++) { while (1) { $date=&DateCalc($date,"${sign}0:0:0:1:0:0:0"); last if (! $work || &Date_IsWorkDay($date,0)); } } push(@tmp,$date); } @date=@tmp; next FLAG; } if ($f =~ /^CW(N|P|D)$/ || $f =~ /^(N|P|D)W(D)$/) { $tmp=$1; my $noalt = $2 ? 1 : 0; if ($tmp eq "N" || ($tmp eq "D" && $Cnf{"TomorrowFirst"})) { $forw=1; } else { $forw=0; } @tmp=(); DATE: foreach $date (@date) { $df=$db=$date; if (&Date_IsWorkDay($date)) { push(@tmp,$date); next DATE; } while (1) { if ($forw) { $d=$df=&DateCalc($df,"+0:0:0:1:0:0:0"); } else { $d=$db=&DateCalc($db,"-0:0:0:1:0:0:0"); } if (&Date_IsWorkDay($d)) { push(@tmp,$d); next DATE; } $forw=1-$forw if (! $noalt); } } @date=@tmp; next FLAG; } if ($f eq "EASTER") { @tmp=(); foreach $date (@date) { ($y,$m,$d,$h,$mn,$s)=&Date_Split($date, 1); ($m,$d)=&Date_Easter($y); $date=&Date_Join($y,$m,$d,$h,$mn,$s); next if (&Date_Cmp($date,$date0)<0 || &Date_Cmp($date,$date1)>0); push(@tmp,$date); } @date=@tmp; } } } @date = sort { Date_Cmp($a,$b) } @date; return @date; } sub Date_GetPrev { print "DEBUG: Date_GetPrev\n" if ($Curr{"Debug"} =~ /trace/); my($date,$dow,$today,$hr,$min,$sec)=@_; &Date_Init() if (! $Curr{"InitDone"}); my($y,$m,$d,$h,$mn,$s,$err,$curr_dow,%dow,$num,$delta,$th,$tm,$ts, $adjust,$curr)=(); $hr="00" if (defined $hr && $hr eq "0"); $min="00" if (defined $min && $min eq "0"); $sec="00" if (defined $sec && $sec eq "0"); if (! &Date_Split($date)) { $date=&ParseDateString($date); return "" if (! $date); } $curr=$date; ($y,$m,$d)=( &Date_Split($date, 1) )[0..2]; if ($dow) { $curr_dow=&Date_DayOfWeek($m,$d,$y); %dow=%{ $Lang{$Cnf{"Language"}}{"WeekH"} }; if (&IsInt($dow)) { return "" if ($dow<1 || $dow>7); } else { return "" if (! exists $dow{lc($dow)}); $dow=$dow{lc($dow)}; } if ($dow == $curr_dow) { $date=&DateCalc_DateDelta($date,"-0:0:1:0:0:0:0",\$err,0) if (! $today); $adjust=1 if ($today==2); } else { $dow -= 7 if ($dow>$curr_dow); # make sure previous day is less $num = $curr_dow - $dow; $date=&DateCalc_DateDelta($date,"-0:0:0:$num:0:0:0",\$err,0); } $date=&Date_SetTime($date,$hr,$min,$sec) if (defined $hr); $date=&DateCalc_DateDelta($date,"-0:0:1:0:0:0:0",\$err,0) if ($adjust && &Date_Cmp($date,$curr)>0); } else { ($h,$mn,$s)=( &Date_Split($date, 1) )[3..5]; ($th,$tm,$ts)=&Date_ParseTime($hr,$min,$sec); if ($hr) { ($hr,$min,$sec)=($th,$tm,$ts); $delta="-0:0:0:1:0:0:0"; } elsif ($min) { ($hr,$min,$sec)=($h,$tm,$ts); $delta="-0:0:0:0:1:0:0"; } elsif ($sec) { ($hr,$min,$sec)=($h,$mn,$ts); $delta="-0:0:0:0:0:1:0"; } else { confess "ERROR: invalid arguments in Date_GetPrev.\n"; } $d=&Date_SetTime($date,$hr,$min,$sec); if ($today) { $d=&DateCalc_DateDelta($d,$delta,\$err,0) if (&Date_Cmp($d,$date)>0); } else { $d=&DateCalc_DateDelta($d,$delta,\$err,0) if (&Date_Cmp($d,$date)>=0); } $date=$d; } return $date; } sub Date_GetNext { print "DEBUG: Date_GetNext\n" if ($Curr{"Debug"} =~ /trace/); my($date,$dow,$today,$hr,$min,$sec)=@_; &Date_Init() if (! $Curr{"InitDone"}); my($y,$m,$d,$h,$mn,$s,$err,$curr_dow,%dow,$num,$delta,$th,$tm,$ts, $adjust,$curr)=(); $hr="00" if (defined $hr && $hr eq "0"); $min="00" if (defined $min && $min eq "0"); $sec="00" if (defined $sec && $sec eq "0"); if (! &Date_Split($date)) { $date=&ParseDateString($date); return "" if (! $date); } $curr=$date; ($y,$m,$d)=( &Date_Split($date, 1) )[0..2]; if ($dow) { $curr_dow=&Date_DayOfWeek($m,$d,$y); %dow=%{ $Lang{$Cnf{"Language"}}{"WeekH"} }; if (&IsInt($dow)) { return "" if ($dow<1 || $dow>7); } else { return "" if (! exists $dow{lc($dow)}); $dow=$dow{lc($dow)}; } if ($dow == $curr_dow) { $date=&DateCalc_DateDelta($date,"+0:0:1:0:0:0:0",\$err,0) if (! $today); $adjust=1 if ($today==2); } else { $curr_dow -= 7 if ($curr_dow>$dow); # make sure next date is greater $num = $dow - $curr_dow; $date=&DateCalc_DateDelta($date,"+0:0:0:$num:0:0:0",\$err,0); } $date=&Date_SetTime($date,$hr,$min,$sec) if (defined $hr); $date=&DateCalc_DateDelta($date,"+0:0:1:0:0:0:0",\$err,0) if ($adjust && &Date_Cmp($date,$curr)<0); } else { ($h,$mn,$s)=( &Date_Split($date, 1) )[3..5]; ($th,$tm,$ts)=&Date_ParseTime($hr,$min,$sec); if ($hr) { ($hr,$min,$sec)=($th,$tm,$ts); $delta="+0:0:0:1:0:0:0"; } elsif ($min) { ($hr,$min,$sec)=($h,$tm,$ts); $delta="+0:0:0:0:1:0:0"; } elsif ($sec) { ($hr,$min,$sec)=($h,$mn,$ts); $delta="+0:0:0:0:0:1:0"; } else { confess "ERROR: invalid arguments in Date_GetNext.\n"; } $d=&Date_SetTime($date,$hr,$min,$sec); if ($today) { $d=&DateCalc_DateDelta($d,$delta,\$err,0) if (&Date_Cmp($d,$date)<0); } else { $d=&DateCalc_DateDelta($d,$delta,\$err,0) if (&Date_Cmp($d,$date)<1); } $date=$d; } return $date; } sub Date_IsHoliday { print "DEBUG: Date_IsHoliday\n" if ($Curr{"Debug"} =~ /trace/); my($date)=@_; &Date_Init() if (! $Curr{"InitDone"}); $date=&ParseDateString($date); return undef if (! $date); $date=&Date_SetTime($date,0,0,0); my($y)=(&Date_Split($date, 1))[0]; &Date_UpdateHolidays($y) if (! exists $Holiday{"dates"}{$y}); return undef if (! exists $Holiday{"dates"}{$y}{$date}); my($name)=$Holiday{"dates"}{$y}{$date}; return "" if (! $name); $name; } sub Events_List { print "DEBUG: Events_List\n" if ($Curr{"Debug"} =~ /trace/); my(@args)=@_; &Date_Init() if (! $Curr{"InitDone"}); &Events_ParseRaw(); my($tmp,$date0,$date1,$flag); $date0=&ParseDateString($args[0]); warn "Invalid date $args[0]", return undef if (! $date0); if ($#args == 0) { return &Events_Calc($date0); } if ($args[1]) { $date1=&ParseDateString($args[1]); warn "Invalid date $args[1]\n", return undef if (! $date1); if (&Date_Cmp($date0,$date1)>0) { $tmp=$date1; $date1=$date0; $date0=$tmp; } } else { $date0=&Date_SetTime($date0,"00:00:00"); $date1=&DateCalc_DateDelta($date0,"+0:0:0:1:0:0:0"); } $tmp=&Events_Calc($date0,$date1); $flag=$args[2]; return $tmp if (! $flag); my(@tmp,%ret,$delta)=(); @tmp=@$tmp; push(@tmp,$date1); if ($flag==1) { while ($#tmp>0) { ($date0,$tmp)=splice(@tmp,0,2); $date1=$tmp[0]; $delta=&DateCalc_DateDate($date0,$date1); foreach $flag (@$tmp) { if (exists $ret{$flag}) { $ret{$flag}=&DateCalc_DeltaDelta($ret{$flag},$delta); } else { $ret{$flag}=$delta; } } } return \%ret; } elsif ($flag==2) { while ($#tmp>0) { ($date0,$tmp)=splice(@tmp,0,2); $date1=$tmp[0]; $delta=&DateCalc_DateDate($date0,$date1); $flag=join("+",sort { Date_Cmp($a,$b) } @$tmp); next if (! $flag); if (exists $ret{$flag}) { $ret{$flag}=&DateCalc_DeltaDelta($ret{$flag},$delta); } else { $ret{$flag}=$delta; } } return \%ret; } warn "Invalid flag $flag\n"; return undef; } ### # NOTE: The following routines may be called in the routines below with very # little time penalty. ### sub Date_SetTime { print "DEBUG: Date_SetTime\n" if ($Curr{"Debug"} =~ /trace/); my($date,$h,$mn,$s)=@_; &Date_Init() if (! $Curr{"InitDone"}); my($y,$m,$d)=(); if (! &Date_Split($date)) { $date=&ParseDateString($date); return "" if (! $date); } ($y,$m,$d)=( &Date_Split($date, 1) )[0..2]; ($h,$mn,$s)=&Date_ParseTime($h,$mn,$s); my($ampm,$wk); return "" if (&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk)); &Date_Join($y,$m,$d,$h,$mn,$s); } sub Date_SetDateField { print "DEBUG: Date_SetDateField\n" if ($Curr{"Debug"} =~ /trace/); my($date,$field,$val,$nocheck)=@_; my($y,$m,$d,$h,$mn,$s)=(); $nocheck=0 if (! defined $nocheck); ($y,$m,$d,$h,$mn,$s)=&Date_Split($date); if (! $y) { $date=&ParseDateString($date); return "" if (! $date); ($y,$m,$d,$h,$mn,$s)=&Date_Split($date, 1); } if (lc($field) eq "y") { $y=$val; } elsif (lc($field) eq "m") { $m=$val; } elsif (lc($field) eq "d") { $d=$val; } elsif (lc($field) eq "h") { $h=$val; } elsif (lc($field) eq "mn") { $mn=$val; } elsif (lc($field) eq "s") { $s=$val; } else { confess "ERROR: Date_SetDateField: invalid field: $field\n"; } $date=&Date_Join($y,$m,$d,$h,$mn,$s); return $date if ($nocheck || &Date_Split($date)); return ""; } ######################################################################## # OTHER SUBROUTINES ######################################################################## # NOTE: These routines should not call any of the routines above as # there will be a severe time penalty (and the possibility of # infinite recursion). The last couple routines above are # exceptions. # NOTE: Date_Init is a special case. It should be called (conditionally) # in every routine that uses any variable from the Date::Manip # namespace. ######################################################################## sub Date_DaysInMonth { print "DEBUG: Date_DaysInMonth\n" if ($Curr{"Debug"} =~ /trace/); my($m,$y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); my(@d_in_m)=(0,31,28,31,30,31,30,31,31,30,31,30,31); $d_in_m[2]=29 if (&Date_LeapYear($y)); return $d_in_m[$m]; } sub Date_DayOfWeek { print "DEBUG: Date_DayOfWeek\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); my($dayofweek,$dec31)=(); $dec31=5; # Dec 31, 1BC was Friday $dayofweek=(&Date_DaysSince1BC($m,$d,$y)+$dec31) % 7; $dayofweek=7 if ($dayofweek==0); return $dayofweek; } # Can't be in "use integer" because the numbers are too big. no integer; sub Date_SecsSince1970 { print "DEBUG: Date_SecsSince1970\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y,$h,$mn,$s)=@_; $y=&Date_FixYear($y) if (length($y)!=4); my($sec_now,$sec_70)=(); $sec_now=(&Date_DaysSince1BC($m,$d,$y)-1)*24*3600 + $h*3600 + $mn*60 + $s; # $sec_70 =(&Date_DaysSince1BC(1,1,1970)-1)*24*3600; $sec_70 =62167219200; return ($sec_now-$sec_70); } sub Date_SecsSince1970GMT { print "DEBUG: Date_SecsSince1970GMT\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y,$h,$mn,$s)=@_; &Date_Init() if (! $Curr{"InitDone"}); $y=&Date_FixYear($y) if (length($y)!=4); my($sec)=&Date_SecsSince1970($m,$d,$y,$h,$mn,$s); return $sec if ($Cnf{"ConvTZ"} eq "IGNORE"); my($tz)=$Cnf{"ConvTZ"}; $tz=$Cnf{"TZ"} if (! $tz); $tz=$Zone{"n2o"}{lc($tz)} if ($tz !~ /^[+-]\d{4}$/); my($tzs)=1; $tzs=-1 if ($tz<0); $tz=~/.(..)(..)/; my($tzh,$tzm)=($1,$2); $sec - $tzs*($tzh*3600+$tzm*60); } use integer; sub Date_DaysSince1BC { print "DEBUG: Date_DaysSince1BC\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); my($Ny,$N4,$N100,$N400,$dayofyear,$days)=(); my($cc,$yy)=(); $y=~ /(\d{2})(\d{2})/; ($cc,$yy)=($1,$2); # Number of full years since Dec 31, 1BC (counting the year 0000). $Ny=$y; # Number of full 4th years (incl. 0000) since Dec 31, 1BC $N4=($Ny-1)/4 + 1; $N4=0 if ($y==0); # Number of full 100th years (incl. 0000) $N100=$cc + 1; $N100-- if ($yy==0); $N100=0 if ($y==0); # Number of full 400th years (incl. 0000) $N400=($N100-1)/4 + 1; $N400=0 if ($y==0); $dayofyear=&Date_DayOfYear($m,$d,$y); $days= $Ny*365 + $N4 - $N100 + $N400 + $dayofyear; return $days; } sub Date_DayOfYear { print "DEBUG: Date_DayOfYear\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); # DinM = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) my(@days) = ( 0, 31, 59, 90,120,151,181,212,243,273,304,334,365); my($ly)=0; $ly=1 if ($m>2 && &Date_LeapYear($y)); return ($days[$m-1]+$d+$ly); } sub Date_DaysInYear { print "DEBUG: Date_DaysInYear\n" if ($Curr{"Debug"} =~ /trace/); my($y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); return 366 if (&Date_LeapYear($y)); return 365; } sub Date_WeekOfYear { print "DEBUG: Date_WeekOfYear\n" if ($Curr{"Debug"} =~ /trace/); my($m,$d,$y,$f)=@_; &Date_Init() if (! $Curr{"InitDone"}); $y=&Date_FixYear($y) if (length($y)!=4); my($day,$dow,$doy)=(); $doy=&Date_DayOfYear($m,$d,$y); # The current DayOfYear and DayOfWeek if ($Cnf{"Jan1Week1"}) { $day=1; } else { $day=4; } $dow=&Date_DayOfWeek(1,$day,$y); # Move back to the first day of week 1. $f-=7 if ($f>$dow); $day-= ($dow-$f); return 0 if ($day>$doy); # Day is in last week of previous year return (($doy-$day)/7 + 1); } sub Date_LeapYear { print "DEBUG: Date_LeapYear\n" if ($Curr{"Debug"} =~ /trace/); my($y)=@_; $y=&Date_FixYear($y) if (length($y)!=4); return 0 unless $y % 4 == 0; return 1 unless $y % 100 == 0; return 0 unless $y % 400 == 0; return 1; } sub Date_DaySuffix { print "DEBUG: Date_DaySuffix\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; &Date_Init() if (! $Curr{"InitDone"}); return $Lang{$Cnf{"Language"}}{"DoML"}[$d-1]; } sub Date_ConvTZ { print "DEBUG: Date_ConvTZ\n" if ($Curr{"Debug"} =~ /trace/); my($date,$from,$to,$level)=@_; if (not Date_Split($date)) { my $err = "date passed in ('$date') is not a Date::Manip object"; if (! $level) { croak $err; } elsif ($level==1) { carp $err; } return ""; } &Date_Init() if (! $Curr{"InitDone"}); my($gmt)=(); if (! $from) { if (! $to) { # TZ -> ConvTZ return $date if ($Cnf{"ConvTZ"} eq "IGNORE" or ! $Cnf{"ConvTZ"}); $from=$Cnf{"TZ"}; $to=$Cnf{"ConvTZ"}; } else { # ConvTZ,TZ -> $to $from=$Cnf{"ConvTZ"}; $from=$Cnf{"TZ"} if (! $from); } } else { if (! $to) { # $from -> ConvTZ,TZ return $date if ($Cnf{"ConvTZ"} eq "IGNORE"); $to=$Cnf{"ConvTZ"}; $to=$Cnf{"TZ"} if (! $to); } else { # $from -> $to } } $to=$Zone{"n2o"}{lc($to)} if (exists $Zone{"n2o"}{lc($to)}); $from=$Zone{"n2o"}{lc($from)} if (exists $Zone{"n2o"}{lc($from)}); $gmt=$Zone{"n2o"}{"gmt"}; return $date if ($from !~ /^[+-]\d{4}$/ or $to !~ /^[+-]\d{4}$/); return $date if ($from eq $to); my($s1,$h1,$m1,$s2,$h2,$m2,$d,$h,$m,$sign,$delta,$err,$yr,$mon,$sec)=(); # We're going to try to do the calculation without calling DateCalc. ($yr,$mon,$d,$h,$m,$sec)=&Date_Split($date, 1); # Convert $date from $from to GMT $from=~/([+-])(\d{2})(\d{2})/; ($s1,$h1,$m1)=($1,$2,$3); $s1= ($s1 eq "-" ? "+" : "-"); # switch sign $sign=$s1 . "1"; # + or - 1 # and from GMT to $to $to=~/([+-])(\d{2})(\d{2})/; ($s2,$h2,$m2)=($1,$2,$3); if ($s1 eq $s2) { # Both the same sign $m+= $sign*($m1+$m2); $h+= $sign*($h1+$h2); } else { $sign=($s2 eq "-" ? +1 : -1) if ($h1<$h2 || ($h1==$h2 && $m1<$m2)); $m+= $sign*($m1-$m2); $h+= $sign*($h1-$h2); } if ($m>59) { $h+= $m/60; $m-= ($m/60)*60; } elsif ($m<0) { $h+= ($m/60 - 1); $m-= ($m/60 - 1)*60; } if ($h>23) { $delta=$h/24; $h -= $delta*24; if (($d + $delta) > 28) { $date=&Date_Join($yr,$mon,$d,$h,$m,$sec); return &DateCalc_DateDelta($date,"+0:0:0:$delta:0:0:0",\$err,0); } $d+= $delta; } elsif ($h<0) { $delta=-$h/24 + 1; $h += $delta*24; if (($d - $delta) < 1) { $date=&Date_Join($yr,$mon,$d,$h,$m,$sec); return &DateCalc_DateDelta($date,"-0:0:0:$delta:0:0:0",\$err,0); } $d-= $delta; } return &Date_Join($yr,$mon,$d,$h,$m,$sec); } sub Date_TimeZone { print "DEBUG: Date_TimeZone\n" if ($Curr{"Debug"} =~ /trace/); my($null,$tz,@tz,$std,$dst,$time,$isdst,$tmp,$in)=(); &Date_Init() if (! $Curr{"InitDone"}); # Get timezones from all of the relevant places push(@tz,$Cnf{"TZ"}) if (defined $Cnf{"TZ"}); # TZ config var push(@tz,$ENV{"TZ"}) if (defined $ENV{"TZ"}); # TZ environ var push(@tz,$ENV{'SYS$TIMEZONE_RULE'}) if defined $ENV{'SYS$TIMEZONE_RULE'}; # VMS TZ environ var push(@tz,$ENV{'SYS$TIMEZONE_NAME'}) if defined $ENV{'SYS$TIMEZONE_NAME'}; # VMS TZ name environ var push(@tz,$ENV{'UCX$TZ'}) if defined $ENV{'UCX$TZ'}; # VMS TZ environ var push(@tz,$ENV{'TCPIP$TZ'}) if defined $ENV{'TCPIP$TZ'}; # VMS TZ environ var # The `date` command... if we're doing taint checking, we need to # always call it with a full path... otherwise, use the user's path. # # Microsoft operating systems don't have a date command built in. Try # to trap all the various ways of knowing we are on one of these systems. # # We'll try `date +%Z` first, and if that fails, we'll take just the # `date` program and assume the output is of the format: # Thu Aug 31 14:57:46 EDT 2000 unless (($^X =~ /perl\.exe$/i) or ($OS eq "Windows") or ($OS eq "Netware") or ($OS eq "VMS")) { if ($Date::Manip::NoTaint) { if ($OS eq "VMS") { $tz=$ENV{'SYS$TIMEZONE_NAME'}; if (! $tz) { $tz=$ENV{'MULTINET_TIMEZONE'}; if (! $tz) { $tz=$ENV{'SYS$TIMEZONE_DIFFERENTIAL'}/3600.; # e.g. '-4' for EDT } } } else { $tz=`date +%Z 2> /dev/null`; chomp($tz); if (! $tz) { $tz=`date 2> /dev/null`; chomp($tz); $tz=(split(/\s+/,$tz))[4]; } } push(@tz,$tz); } else { # We need to satisfy taint checking, but also look in all the # directories in @DatePath. # local $ENV{PATH} = join(':', @Date::Manip::DatePath); local $ENV{BASH_ENV} = ''; $tz=`date +%Z 2> /dev/null`; chomp($tz); if (! $tz) { $tz=`date 2> /dev/null`; chomp($tz); $tz=(split(/\s+/,$tz))[4]; } push(@tz,$tz); } } push(@tz,$main::TZ) if (defined $main::TZ); # $main::TZ if (-s "/etc/TIMEZONE") { # /etc/TIMEZONE $in=new IO::File; $in->open("/etc/TIMEZONE","r"); while (! eof($in)) { $tmp=<$in>; if ($tmp =~ /^TZ\s*=\s*(.*?)\s*$/) { push(@tz,$1); last; } } $in->close; } if (-s "/etc/timezone") { # /etc/timezone $in=new IO::File; $in->open("/etc/timezone","r"); while (! eof($in)) { $tmp=<$in>; next if ($tmp =~ /^\s*\043/); chomp($tmp); if ($tmp =~ /^\s*(.*?)\s*$/) { push(@tz,$1); last; } } $in->close; } # Now parse each one to find the first valid one. foreach $tz (@tz) { $tz =~ s/\s*$//; $tz =~ s/^\s*//; next if ($tz eq ""); return uc($tz) if (defined $Zone{"n2o"}{lc($tz)}); if ($tz =~ /^[+-]\d{4}$/) { return $tz; } elsif ($tz =~ /^([+-]\d{2})(?::(\d{2}))?$/) { my($h,$m)=($1,$2); $m="00" if (! $m); return "$h$m"; } # Handle US/Eastern format if ($tz =~ /^$Zone{"tzones"}$/i) { $tmp=lc $1; $tz=$Zone{"tz2z"}{$tmp}; } # Handle STD#DST# format (and STD-#DST-# formats) if ($tz =~ /^([a-z]+)-?\d([a-z]+)-?\d?$/i) { ($std,$dst)=($1,$2); next if (! defined $Zone{"n2o"}{lc($std)} or ! defined $Zone{"n2o"}{lc($dst)}); $time = time(); ($null,$null,$null,$null,$null,$null,$null,$null,$isdst) = localtime($time); return uc($dst) if ($isdst); return uc($std); } } confess "ERROR: Date::Manip unable to determine TimeZone.\n"; } # Returns 1 if $date is a work day. If $time is non-zero, the time is # also checked to see if it falls within work hours. Returns "" if # an invalid date is passed in. sub Date_IsWorkDay { print "DEBUG: Date_IsWorkDay\n" if ($Curr{"Debug"} =~ /trace/); my($date,$time)=@_; &Date_Init() if (! $Curr{"InitDone"}); $date=&ParseDateString($date); return "" if (! $date); my($d)=$date; $d=&Date_SetTime($date,$Cnf{"WorkDayBeg"}) if (! $time); my($y,$mon,$day,$h,$m,$s,$dow)=(); ($y,$mon,$day,$h,$m,$s)=&Date_Split($d, 1); $dow=&Date_DayOfWeek($mon,$day,$y); return 0 if ($dow<$Cnf{"WorkWeekBeg"} or $dow>$Cnf{"WorkWeekEnd"} or "$h:$m" lt $Cnf{"WorkDayBeg"} or "$h:$m" ge $Cnf{"WorkDayEnd"}); if (! exists $Holiday{"dates"}{$y}) { # There will be recursion problems if we ever end up here twice. $Holiday{"dates"}{$y}={}; &Date_UpdateHolidays($y) } $d=&Date_SetTime($date,"00:00:00"); return 0 if (exists $Holiday{"dates"}{$y}{$d}); 1; } # Finds the day $off work days from now. If $time is passed in, we must # also take into account the time of day. # # If $time is not passed in, day 0 is today (if today is a workday) or the # next work day if it isn't. In any case, the time of day is unaffected. # # If $time is passed in, day 0 is now (if now is part of a workday) or the # start of the very next work day. sub Date_NextWorkDay { print "DEBUG: Date_NextWorkDay\n" if ($Curr{"Debug"} =~ /trace/); my($date,$off,$time)=@_; &Date_Init() if (! $Curr{"InitDone"}); $date=&ParseDateString($date); my($err)=(); if (! &Date_IsWorkDay($date,$time)) { if ($time) { while (1) { $date=&Date_GetNext($date,undef,0,$Cnf{"WorkDayBeg"}); last if (&Date_IsWorkDay($date,$time)); } } else { while (1) { $date=&DateCalc_DateDelta($date,"+0:0:0:1:0:0:0",\$err,0); last if (&Date_IsWorkDay($date,$time)); } } } while ($off>0) { while (1) { $date=&DateCalc_DateDelta($date,"+0:0:0:1:0:0:0",\$err,0); last if (&Date_IsWorkDay($date,$time)); } $off--; } return $date; } # Finds the day $off work days before now. If $time is passed in, we must # also take into account the time of day. # # If $time is not passed in, day 0 is today (if today is a workday) or the # previous work day if it isn't. In any case, the time of day is unaffected. # # If $time is passed in, day 0 is now (if now is part of a workday) or the # end of the previous work period. Note that since the end of a work day # will automatically be turned into the start of the next one, this time # may actually be treated as AFTER the current time. sub Date_PrevWorkDay { print "DEBUG: Date_PrevWorkDay\n" if ($Curr{"Debug"} =~ /trace/); my($date,$off,$time)=@_; &Date_Init() if (! $Curr{"InitDone"}); $date=&ParseDateString($date); my($err)=(); if (! &Date_IsWorkDay($date,$time)) { if ($time) { while (1) { $date=&Date_GetPrev($date,undef,0,$Cnf{"WorkDayEnd"}); last if (&Date_IsWorkDay($date,$time)); } while (1) { $date=&Date_GetNext($date,undef,0,$Cnf{"WorkDayBeg"}); last if (&Date_IsWorkDay($date,$time)); } } else { while (1) { $date=&DateCalc_DateDelta($date,"-0:0:0:1:0:0:0",\$err,0); last if (&Date_IsWorkDay($date,$time)); } } } while ($off>0) { while (1) { $date=&DateCalc_DateDelta($date,"-0:0:0:1:0:0:0",\$err,0); last if (&Date_IsWorkDay($date,$time)); } $off--; } return $date; } # This finds the nearest workday to $date. If $date is a workday, it # is returned. sub Date_NearestWorkDay { print "DEBUG: Date_NearestWorkDay\n" if ($Curr{"Debug"} =~ /trace/); my($date,$tomorrow)=@_; &Date_Init() if (! $Curr{"InitDone"}); $date=&ParseDateString($date); my($a,$b,$dela,$delb,$err)=(); $tomorrow=$Cnf{"TomorrowFirst"} if (! defined $tomorrow); return $date if (&Date_IsWorkDay($date)); # Find the nearest one. if ($tomorrow) { $dela="+0:0:0:1:0:0:0"; $delb="-0:0:0:1:0:0:0"; } else { $dela="-0:0:0:1:0:0:0"; $delb="+0:0:0:1:0:0:0"; } $a=$b=$date; while (1) { $a=&DateCalc_DateDelta($a,$dela,\$err); return $a if (&Date_IsWorkDay($a)); $b=&DateCalc_DateDelta($b,$delb,\$err); return $b if (&Date_IsWorkDay($b)); } } # &Date_NthDayOfYear($y,$n); # Returns a list of (YYYY,MM,DD,HH,MM,SS) for the Nth day of the year. sub Date_NthDayOfYear { no integer; print "DEBUG: Date_NthDayOfYear\n" if ($Curr{"Debug"} =~ /trace/); my($y,$n)=@_; $y=$Curr{"Y"} if (! $y); $n=1 if (! defined $n or $n eq ""); $n+=0; # to turn 023 into 23 $y=&Date_FixYear($y) if (length($y)<4); my $leap=&Date_LeapYear($y); return () if ($n<1); return () if ($n >= ($leap ? 367 : 366)); my(@d_in_m)=(31,28,31,30,31,30,31,31,30,31,30,31); $d_in_m[1]=29 if ($leap); # Calculate the hours, minutes, and seconds into the day. my $remain=($n - int($n))*24; my $h=int($remain); $remain=($remain - $h)*60; my $mn=int($remain); $remain=($remain - $mn)*60; my $s=$remain; # Calculate the month and the day. my($m,$d)=(0,0); $n=int($n); while ($n>0) { $m++; if ($n<=$d_in_m[0]) { $d=int($n); $n=0; } else { $n-= $d_in_m[0]; shift(@d_in_m); } } ($y,$m,$d,$h,$mn,$s); } ######################################################################## # NOT FOR EXPORT ######################################################################## # This is used in Date_Init to fill in a hash based on international # data. It takes a list of keys and values and returns both a hash # with these values and a regular expression of keys. # # IN: # $data = [ key1 val1 key2 val2 ... ] # $opts = lc : lowercase the keys in the regexp # sort : sort (by length) the keys in the regexp # back : create a regexp with a back reference # escape : escape all strings in the regexp # # OUT: # $regexp = '(?:key1|key2|...)' # $hash = { key1=>val1 key2=>val2 ... } sub Date_InitHash { print "DEBUG: Date_InitHash\n" if ($Curr{"Debug"} =~ /trace/); my($data,$regexp,$opts,$hash)=@_; my(@data)=@$data; my($key,$val,@list)=(); # Parse the options my($lc,$sort,$back,$escape)=(0,0,0,0); $lc=1 if ($opts =~ /lc/i); $sort=1 if ($opts =~ /sort/i); $back=1 if ($opts =~ /back/i); $escape=1 if ($opts =~ /escape/i); # Create the hash while (@data) { ($key,$val,@data)=@data; $key=lc($key) if ($lc); $$hash{$key}=$val; } # Create the regular expression if ($regexp) { @list=keys(%$hash); @list=sort sortByLength(@list) if ($sort); if ($escape) { foreach $val (@list) { $val="\Q$val\E"; } } if ($back) { $$regexp="(" . join("|",@list) . ")"; } else { $$regexp="(?:" . join("|",@list) . ")"; } } } # This is used in Date_Init to fill in regular expressions, lists, and # hashes based on international data. It takes a list of lists which have # to be stored as regular expressions (to find any element in the list), # lists, and hashes (indicating the location in the lists). # # IN: # $data = [ [ [ valA1 valA2 ... ][ valA1' valA2' ... ] ... ] # [ [ valB1 valB2 ... ][ valB1' valB2' ... ] ... ] # ... # [ [ valZ1 valZ2 ... ] [valZ1' valZ1' ... ] ... ] ] # $lists = [ \@listA \@listB ... \@listZ ] # $opts = lc : lowercase the values in the regexp # sort : sort (by length) the values in the regexp # back : create a regexp with a back reference # escape : escape all strings in the regexp # $hash = [ \%hash, TYPE ] # TYPE 0 : $hash{ valBn=>n-1 } # TYPE 1 : $hash{ valBn=>n } # # OUT: # $regexp = '(?:valA1|valA2|...|valB1|...)' # $lists = [ [ valA1 valA2 ... ] # only the 1st list (or # [ valB1 valB2 ... ] ... ] # 2nd for int. characters) # $hash sub Date_InitLists { print "DEBUG: Date_InitLists\n" if ($Curr{"Debug"} =~ /trace/); my($data,$regexp,$opts,$lists,$hash)=@_; my(@data)=@$data; my(@lists)=@$lists; my($i,@ele,$ele,@list,$j,$tmp)=(); # Parse the options my($lc,$sort,$back,$escape)=(0,0,0,0); $lc=1 if ($opts =~ /lc/i); $sort=1 if ($opts =~ /sort/i); $back=1 if ($opts =~ /back/i); $escape=1 if ($opts =~ /escape/i); # Set each of the lists if (@lists) { confess "ERROR: Date_InitLists: lists must be 1 per data\n" if ($#lists != $#data); for ($i=0; $i<=$#data; $i++) { @ele=@{ $data[$i] }; if ($Cnf{"IntCharSet"} && $#ele>0) { @{ $lists[$i] } = @{ $ele[1] }; } else { @{ $lists[$i] } = @{ $ele[0] }; } } } # Create the hash my($hashtype,$hashsave,%hash)=(); if (@$hash) { ($hash,$hashtype)=@$hash; $hashsave=1; } else { $hashtype=0; $hashsave=0; } for ($i=0; $i<=$#data; $i++) { @ele=@{ $data[$i] }; foreach $ele (@ele) { @list = @{ $ele }; for ($j=0; $j<=$#list; $j++) { $tmp=$list[$j]; next if (! $tmp); $tmp=lc($tmp) if ($lc); $hash{$tmp}= $j+$hashtype; } } } %$hash = %hash if ($hashsave); # Create the regular expression if ($regexp) { @list=keys(%hash); @list=sort sortByLength(@list) if ($sort); if ($escape) { foreach $ele (@list) { $ele="\Q$ele\E"; } } if ($back) { $$regexp="(" . join("|",@list) . ")"; } else { $$regexp="(?:" . join("|",@list) . ")"; } } } # This is used in Date_Init to fill in regular expressions and lists based # on international data. This takes a list of strings and returns a regular # expression (to find any one of them). # # IN: # $data = [ string1 string2 ... ] # $opts = lc : lowercase the values in the regexp # sort : sort (by length) the values in the regexp # back : create a regexp with a back reference # escape : escape all strings in the regexp # # OUT: # $regexp = '(string1|string2|...)' sub Date_InitStrings { print "DEBUG: Date_InitStrings\n" if ($Curr{"Debug"} =~ /trace/); my($data,$regexp,$opts)=@_; my(@list)=@{ $data }; # Parse the options my($lc,$sort,$back,$escape)=(0,0,0,0); $lc=1 if ($opts =~ /lc/i); $sort=1 if ($opts =~ /sort/i); $back=1 if ($opts =~ /back/i); $escape=1 if ($opts =~ /escape/i); # Create the regular expression my($ele)=(); @list=sort sortByLength(@list) if ($sort); if ($escape) { foreach $ele (@list) { $ele="\Q$ele\E"; } } if ($back) { $$regexp="(" . join("|",@list) . ")"; } else { $$regexp="(?:" . join("|",@list) . ")"; } $$regexp=lc($$regexp) if ($lc); } # items is passed in (either as a space separated string, or a reference to # a list) and a regular expression which matches any one of the items is # prepared. The regular expression will be of one of the forms: # "(a|b)" @list not empty, back option included # "(?:a|b)" @list not empty # "()" @list empty, back option included # "" @list empty # $options is a string which contains any of the following strings: # back : the regular expression has a backreference # opt : the regular expression is optional and a "?" is appended in # the first two forms # optws : the regular expression is optional and may be replaced by # whitespace # optWs : the regular expression is optional, but if not present, must # be replaced by whitespace # sort : the items in the list are sorted by length (longest first) # lc : the string is lowercased # under : any underscores are converted to spaces # pre : it may be preceded by whitespace # Pre : it must be preceded by whitespace # PRE : it must be preceded by whitespace or the start # post : it may be followed by whitespace # Post : it must be followed by whitespace # POST : it must be followed by whitespace or the end # Spaces due to pre/post options will not be included in the back reference. # # If $array is included, then the elements will also be returned as a list. # $array is a string which may contain any of the following: # keys : treat the list as a hash and only the keys go into the regexp # key0 : treat the list as the values of a hash with keys 0 .. N-1 # key1 : treat the list as the values of a hash with keys 1 .. N # val0 : treat the list as the keys of a hash with values 0 .. N-1 # val1 : treat the list as the keys of a hash with values 1 .. N # &Date_InitLists([$lang{"month_name"},$lang{"month_abb"}], # [\$Month,"lc,sort,back"], # [\@Month,\@Mon], # [\%Month,1]); # This is used in Date_Init to prepare regular expressions. A list of # items is passed in (either as a space separated string, or a reference to # a list) and a regular expression which matches any one of the items is # prepared. The regular expression will be of one of the forms: # "(a|b)" @list not empty, back option included # "(?:a|b)" @list not empty # "()" @list empty, back option included # "" @list empty # $options is a string which contains any of the following strings: # back : the regular expression has a backreference # opt : the regular expression is optional and a "?" is appended in # the first two forms # optws : the regular expression is optional and may be replaced by # whitespace # optWs : the regular expression is optional, but if not present, must # be replaced by whitespace # sort : the items in the list are sorted by length (longest first) # lc : the string is lowercased # under : any underscores are converted to spaces # pre : it may be preceded by whitespace # Pre : it must be preceded by whitespace # PRE : it must be preceded by whitespace or the start # post : it may be followed by whitespace # Post : it must be followed by whitespace # POST : it must be followed by whitespace or the end # Spaces due to pre/post options will not be included in the back reference. # # If $array is included, then the elements will also be returned as a list. # $array is a string which may contain any of the following: # keys : treat the list as a hash and only the keys go into the regexp # key0 : treat the list as the values of a hash with keys 0 .. N-1 # key1 : treat the list as the values of a hash with keys 1 .. N # val0 : treat the list as the keys of a hash with values 0 .. N-1 # val1 : treat the list as the keys of a hash with values 1 .. N sub Date_Regexp { print "DEBUG: Date_Regexp\n" if ($Curr{"Debug"} =~ /trace/); my($list,$options,$array)=@_; my(@list,$ret,%hash,$i)=(); local($_)=(); $options="" if (! defined $options); $array="" if (! defined $array); my($sort,$lc,$under)=(0,0,0); $sort =1 if ($options =~ /sort/i); $lc =1 if ($options =~ /lc/i); $under=1 if ($options =~ /under/i); my($back,$opt,$pre,$post,$ws)=("?:","","","",""); $back ="" if ($options =~ /back/i); $opt ="?" if ($options =~ /opt/i); $pre ='\s*' if ($options =~ /pre/); $pre ='\s+' if ($options =~ /Pre/); $pre ='(?:\s+|^)' if ($options =~ /PRE/); $post ='\s*' if ($options =~ /post/); $post ='\s+' if ($options =~ /Post/); $post ='(?:$|\s+)' if ($options =~ /POST/); $ws ='\s*' if ($options =~ /optws/); $ws ='\s+' if ($options =~ /optws/); my($hash,$keys,$key0,$key1,$val0,$val1)=(0,0,0,0,0,0); $keys =1 if ($array =~ /keys/i); $key0 =1 if ($array =~ /key0/i); $key1 =1 if ($array =~ /key1/i); $val0 =1 if ($array =~ /val0/i); $val1 =1 if ($array =~ /val1/i); $hash =1 if ($keys or $key0 or $key1 or $val0 or $val1); my($ref)=ref $list; if (! $ref) { $list =~ s/\s*$//; $list =~ s/^\s*//; $list =~ s/\s+/&&&/g; } elsif ($ref eq "ARRAY") { $list = join("&&&",@$list); } else { confess "ERROR: Date_Regexp.\n"; } if (! $list) { if ($back eq "") { return "()"; } else { return ""; } } $list=lc($list) if ($lc); $list=~ s/_/ /g if ($under); @list=split(/&&&/,$list); if ($keys) { %hash=@list; @list=keys %hash; } elsif ($key0 or $key1 or $val0 or $val1) { $i=0; $i=1 if ($key1 or $val1); if ($key0 or $key1) { %hash= map { $_,$i++ } @list; } else { %hash= map { $i++,$_ } @list; } } @list=sort sortByLength(@list) if ($sort); $ret="($back" . join("|",@list) . ")"; $ret="(?:$pre$ret$post)" if ($pre or $post); $ret.=$opt; $ret="(?:$ret|$ws)" if ($ws); if ($array and $hash) { return ($ret,%hash); } elsif ($array) { return ($ret,@list); } else { return $ret; } } # This will produce a delta with the correct number of signs. At most two # signs will be in it normally (one before the year, and one in front of # the day), but if appropriate, signs will be in front of all elements. # Also, as many of the signs will be equivalent as possible. sub Delta_Normalize { print "DEBUG: Delta_Normalize\n" if ($Curr{"Debug"} =~ /trace/); my($delta,$mode)=@_; return "" if (! $delta); return "+0:+0:+0:+0:+0:+0:+0" if ($delta =~ /^([+-]?0+:){6}[+-]?0+$/ and $Cnf{"DeltaSigns"}); return "+0:0:0:0:0:0:0" if ($delta =~ /^([+-]?0+:){6}[+-]?0+$/); my($tmp,$sign1,$sign2,$len)=(); # Calculate the length of the day in minutes $len=24*60; $len=$Curr{"WDlen"} if ($mode==2 || $mode==3); # We have to get the sign of every component explicitely so that a "-0" # or "+0" doesn't get lost by treating it numerically (i.e. "-0:0:2" must # be a negative delta). my($y,$mon,$w,$d,$h,$m,$s)=&Delta_Split($delta); # We need to make sure that the signs of all parts of a delta are the # same. The easiest way to do this is to convert all of the large # components to the smallest ones, then convert the smaller components # back to the larger ones. # Do the year/month part $mon += $y*12; # convert y to m $sign1="+"; if ($mon<0) { $mon *= -1; $sign1="-"; } $y = $mon/12; # convert m to y $mon -= $y*12; $y=0 if ($y eq "-0"); # get around silly -0 problem $mon=0 if ($mon eq "-0"); # Do the wk/day/hour/min/sec part { # Unfortunately, $s is overflowing for dates more than ~70 years # apart. no integer; if ($mode==3 || $mode==2) { $s += $d*$len*60 + $h*3600 + $m*60; # convert d/h/m to s } else { $s += ($d+7*$w)*$len*60 + $h*3600 + $m*60; # convert w/d/h/m to s } $sign2="+"; if ($s<0) { $s*=-1; $sign2="-"; } $m = int($s/60); # convert s to m $s -= $m*60; $d = int($m/$len); # convert m to d $m -= $d*$len; # The rest should be fine. } $h = $m/60; # convert m to h $m -= $h*60; if ($mode == 3 || $mode == 2) { $w = $w*1; # get around +0 problem } else { $w = $d/7; # convert d to w $d -= $w*7; } $w=0 if ($w eq "-0"); # get around silly -0 problem $d=0 if ($d eq "-0"); $h=0 if ($h eq "-0"); $m=0 if ($m eq "-0"); $s=0 if ($s eq "-0"); # Only include two signs if necessary $sign1=$sign2 if ($y==0 and $mon==0); $sign2=$sign1 if ($w==0 and $d==0 and $h==0 and $m==0 and $s==0); $sign2="" if ($sign1 eq $sign2 and ! $Cnf{"DeltaSigns"}); if ($Cnf{"DeltaSigns"}) { return "$sign1$y:$sign1$mon:$sign2$w:$sign2$d:$sign2$h:$sign2$m:$sign2$s"; } else { return "$sign1$y:$mon:$sign2$w:$d:$h:$m:$s"; } } # This checks a delta to make sure it is valid. If it is, it splits # it and returns the elements with a sign on each. The 2nd argument # specifies the default sign. Blank elements are set to 0. If the # third element is non-nil, exactly 7 elements must be included. sub Delta_Split { print "DEBUG: Delta_Split\n" if ($Curr{"Debug"} =~ /trace/); my($delta,$sign,$exact)=@_; my(@delta)=split(/:/,$delta); return () if ($exact and $#delta != 6); my($i)=(); $sign="+" if (! defined $sign); for ($i=0; $i<=$#delta; $i++) { $delta[$i]="0" if (! $delta[$i]); return () if ($delta[$i] !~ /^[+-]?\d+$/); $sign = ($delta[$i] =~ s/^([+-])// ? $1 : $sign); $delta[$i] = $sign.$delta[$i]; } @delta; } # Reads up to 3 arguments. $h may contain the time in any international # format. Any empty elements are set to 0. sub Date_ParseTime { print "DEBUG: Date_ParseTime\n" if ($Curr{"Debug"} =~ /trace/); my($h,$m,$s)=@_; my($t)=&CheckTime("one"); if (defined $h and $h =~ /$t/) { $h=$1; $m=$2; $s=$3 if (defined $3); } $h="00" if (! defined $h); $m="00" if (! defined $m); $s="00" if (! defined $s); ($h,$m,$s); } # Forms a date with the 6 elements passed in (all of which must be defined). # No check as to validity is made. sub Date_Join { print "DEBUG: Date_Join\n" if ($Curr{"Debug"} =~ /trace/); foreach (0 .. $#_) { croak "undefined arg $_ to Date_Join()" if not defined $_[$_]; } my($y,$m,$d,$h,$mn,$s)=@_; my($ym,$md,$dh,$hmn,$mns)=(); if ($Cnf{"Internal"} == 0) { $ym=$md=$dh=""; $hmn=$mns=":"; } elsif ($Cnf{"Internal"} == 1) { $ym=$md=$dh=$hmn=$mns=""; } elsif ($Cnf{"Internal"} == 2) { $ym=$md="-"; $dh=" "; $hmn=$mns=":"; } else { confess "ERROR: Invalid internal format in Date_Join.\n"; } $m="0$m" if (length($m)==1); $d="0$d" if (length($d)==1); $h="0$h" if (length($h)==1); $mn="0$mn" if (length($mn)==1); $s="0$s" if (length($s)==1); "$y$ym$m$md$d$dh$h$hmn$mn$mns$s"; } # This checks a time. If it is valid, it splits it and returns 3 elements. # If "one" or "two" is passed in, a regexp with 1/2 or 2 digit hours is # returned. sub CheckTime { print "DEBUG: CheckTime\n" if ($Curr{"Debug"} =~ /trace/); my($time)=@_; my($h)='(?:0?[0-9]|1[0-9]|2[0-3])'; my($h2)='(?:0[0-9]|1[0-9]|2[0-3])'; my($m)='[0-5][0-9]'; my($s)=$m; my($hm)="(?:". $Lang{$Cnf{"Language"}}{"SepHM"} ."|:)"; my($ms)="(?:". $Lang{$Cnf{"Language"}}{"SepMS"} ."|:)"; my($ss)=$Lang{$Cnf{"Language"}}{"SepSS"}; my($t)="^($h)$hm($m)(?:$ms($s)(?:$ss\\d+)?)?\$"; if ($time eq "one") { return $t; } elsif ($time eq "two") { $t="^($h2)$hm($m)(?:$ms($s)(?:$ss\\d+)?)?\$"; return $t; } if ($time =~ /$t/i) { ($h,$m,$s)=($1,$2,$3); $h="0$h" if (length($h)<2); $m="0$m" if (length($m)<2); $s="00" if (! defined $s); return ($h,$m,$s); } else { return (); } } # This checks a recurrence. If it is valid, it splits it and returns the # elements. Otherwise, it returns an empty list. # ($recur0,$recur1,$flags,$dateb,$date0,$date1)=&Recur_Split($recur); sub Recur_Split { print "DEBUG: Recur_Split\n" if ($Curr{"Debug"} =~ /trace/); my($recur)=@_; my(@ret,@tmp); my($R) = '(\*?(?:[-,0-9]+[:\*]){6}[-,0-9]+)'; my($F) = '(?:\*([^*]*))'; my($DB,$D0,$D1); $DB=$D0=$D1=$F; if ($recur =~ /^$R$F?$DB?$D0?$D1?$/) { @ret=($1,$2,$3,$4,$5); @tmp=split(/\*/,shift(@ret)); return () if ($#tmp>1); return (@tmp,"",@ret) if ($#tmp==0); return (@tmp,@ret); } return (); } # This checks a date. If it is valid, it splits it and returns the elements. # # The optional second argument says 'I really expect this to be a # valid Date::Manip object, please throw an exception if it is not'. # Otherwise, if the date passed in is undef or '', a regular # expression for the date is returned; if the string is nonempty but # still not valid, () is returned. # sub Date_Split { print "DEBUG: Date_Split\n" if ($Curr{"Debug"} =~ /trace/); my($date, $definitely_valid)=@_; $definitely_valid = 0 if not defined $definitely_valid; my($ym,$md,$dh,$hmn,$mns)=(); my($y)='(\d{4})'; my($m)='(0[1-9]|1[0-2])'; my($d)='(0[1-9]|[1-2][0-9]|3[0-1])'; my($h)='([0-1][0-9]|2[0-3])'; my($mn)='([0-5][0-9])'; my($s)=$mn; if ($Cnf{"Internal"} == 0) { $ym=$md=$dh=""; $hmn=$mns=":"; } elsif ($Cnf{"Internal"} == 1) { $ym=$md=$dh=$hmn=$mns=""; } elsif ($Cnf{"Internal"} == 2) { $ym=$md="-"; $dh=" "; $hmn=$mns=":"; } else { confess "ERROR: Invalid internal format in Date_Split.\n"; } my($t)="^$y$ym$m$md$d$dh$h$hmn$mn$mns$s\$"; if (not defined $date or $date eq '') { if ($definitely_valid) { die "bad date '$date'"; } else { return $t; } } if ($date =~ /$t/) { ($y,$m,$d,$h,$mn,$s)=($1,$2,$3,$4,$5,$6); my(@d_in_m)=(0,31,28,31,30,31,30,31,31,30,31,30,31); $d_in_m[2]=29 if (&Date_LeapYear($y)); if ($d>$d_in_m[$m]) { my $msg = "invalid date $date: day $d of month $m, but only $d_in_m[$m] days in that month"; if ($definitely_valid) { die $msg; } else { warn $msg; return (); } } return ($y,$m,$d,$h,$mn,$s); } if ($definitely_valid) { die "invalid date $date: doesn't match regexp $t"; } return (); } # This returns the date easter occurs on for a given year as ($month,$day). # This is from the Calendar FAQ. sub Date_Easter { my($y)=@_; $y=&Date_FixYear($y) if (length($y)==2); my($c) = $y/100; my($g) = $y % 19; my($k) = ($c-17)/25; my($i) = ($c - $c/4 - ($c-$k)/3 + 19*$g + 15) % 30; $i = $i - ($i/28)*(1 - ($i/28)*(29/($i+1))*((21-$g)/11)); my($j) = ($y + $y/4 + $i + 2 - $c + $c/4) % 7; my($l) = $i-$j; my($m) = 3 + ($l+40)/44; my($d) = $l + 28 - 31*($m/4); return ($m,$d); } # This takes a list of years, months, WeekOfMonth's, and DayOfWeek's, and # returns a list of dates. Optionally, a list of dates can be passed in as # the 1st argument (with the 2nd argument the null list) and the year/month # of these will be used. sub Date_Recur_WoM { my($y,$m,$w,$d)=@_; my(@y)=@$y; my(@m)=@$m; my(@w)=@$w; my(@d)=@$d; my($date0,$date1,@tmp,@date,$d0,$d1,@tmp2)=(); if (@m) { foreach $m (@m) { return () if (! &IsInt($m,1,12)); } @tmp=@tmp2=(); foreach $y (@y) { foreach $m (@m) { push(@tmp,$y); push(@tmp2,$m); } } @y=@tmp; @m=@tmp2; } else { foreach $d0 (@y) { @tmp=&Date_Split($d0); return () if (! @tmp); push(@tmp2,$tmp[0]); push(@m,$tmp[1]); } @y=@tmp2; } return () if (! @w); foreach $w (@w) { return () if ($w==0 || ! &IsInt($w,-5,5)); } if (@d) { foreach $d (@d) { return () if ($d==0 || ! &IsInt($d,-7,7)); $d += 8 if ($d < 0); } } @date=(); foreach $y (@y) { $m=shift(@m); # Find 1st day of this month and next month $date0=&Date_Join($y,$m,1,0,0,0); $date1=&DateCalc_DateDelta($date0,"+0:1:0:0:0:0:0"); foreach $d (@d) { # Find 1st occurence of DOW (in both months) $d0=&Date_GetNext($date0,$d,1); $d1=&Date_GetNext($date1,$d,1); @tmp=(); while (&Date_Cmp($d0,$d1)<0) { push(@tmp,$d0); $d0=&DateCalc_DateDelta($d0,"+0:0:1:0:0:0:0"); } @tmp2=(); foreach $w (@w) { if ($w>0) { next if ($w > $#tmp+1); push(@tmp2,$tmp[$w-1]); } else { next if (-$w > $#tmp+1); push(@tmp2,$tmp[$#tmp+1+$w]); } } @tmp2=sort { Date_Cmp($a,$b) } @tmp2; push(@date,@tmp2); } } @date; } # This returns a sorted list of dates formed by adding/subtracting # $delta to $dateb in the range $date0<=$d<$dateb. The first date in # the list is actually the first date<$date0 and the last date in the # list is the first date>=$date1 (because sometimes the set part will # move the date back into the range). sub Date_Recur { my($date0,$date1,$dateb,$delta)=@_; my(@ret,$d)=(); while (&Date_Cmp($dateb,$date0)<0) { $dateb=&DateCalc_DateDelta($dateb,$delta); } while (&Date_Cmp($dateb,$date1)>=0) { $dateb=&DateCalc_DateDelta($dateb,"-$delta"); } # Add the dates $date0..$dateb $d=$dateb; while (&Date_Cmp($d,$date0)>=0) { unshift(@ret,$d); $d=&DateCalc_DateDelta($d,"-$delta"); } # Add the first date earler than the range unshift(@ret,$d); # Add the dates $dateb..$date1 $d=&DateCalc_DateDelta($dateb,$delta); while (&Date_Cmp($d,$date1)<0) { push(@ret,$d); $d=&DateCalc_DateDelta($d,$delta); } # Add the first date later than the range push(@ret,$d); @ret; } # This sets the values in each date of a recurrence. # # $h,$m,$s can each be values or lists "1-2,4". If any are equal to "-1", # they are not set (and none of the larger elements are set). sub Date_RecurSetTime { my($date0,$date1,$dates,$h,$m,$s)=@_; my(@dates)=@$dates; my(@h,@m,@s,$date,@tmp)=(); $m="-1" if ($s eq "-1"); $h="-1" if ($m eq "-1"); if ($h ne "-1") { @h=&ReturnList($h); return () if ! (@h); @h=sort { $a<=>$b } (@h); @tmp=(); foreach $date (@dates) { foreach $h (@h) { push(@tmp,&Date_SetDateField($date,"h",$h,1)); } } @dates=@tmp; } if ($m ne "-1") { @m=&ReturnList($m); return () if ! (@m); @m=sort { $a<=>$b } (@m); @tmp=(); foreach $date (@dates) { foreach $m (@m) { push(@tmp,&Date_SetDateField($date,"mn",$m,1)); } } @dates=@tmp; } if ($s ne "-1") { @s=&ReturnList($s); return () if ! (@s); @s=sort { $a<=>$b } (@s); @tmp=(); foreach $date (@dates) { foreach $s (@s) { push(@tmp,&Date_SetDateField($date,"s",$s,1)); } } @dates=@tmp; } @tmp=(); foreach $date (@dates) { push(@tmp,$date) if (&Date_Cmp($date,$date0)>=0 && &Date_Cmp($date,$date1)<0 && &Date_Split($date)); } @tmp; } sub DateCalc_DateDate { print "DEBUG: DateCalc_DateDate\n" if ($Curr{"Debug"} =~ /trace/); my($D1,$D2,$mode)=@_; my(@d_in_m)=(0,31,28,31,30,31,30,31,31,30,31,30,31); $mode=0 if (! defined $mode); # Exact mode if ($mode==0) { my($y1,$m1,$d1,$h1,$mn1,$s1)=&Date_Split($D1, 1); my($y2,$m2,$d2,$h2,$mn2,$s2)=&Date_Split($D2, 1); my($i,@delta,$d,$delta,$y)=(); # form the delta for hour/min/sec $delta[4]=$h2-$h1; $delta[5]=$mn2-$mn1; $delta[6]=$s2-$s1; # form the delta for yr/mon/day $delta[0]=$delta[1]=0; $d=0; if ($y2>$y1) { $d=&Date_DaysInYear($y1) - &Date_DayOfYear($m1,$d1,$y1); $d+=&Date_DayOfYear($m2,$d2,$y2); for ($y=$y1+1; $y<$y2; $y++) { $d+= &Date_DaysInYear($y); } } elsif ($y2<$y1) { $d=&Date_DaysInYear($y2) - &Date_DayOfYear($m2,$d2,$y2); $d+=&Date_DayOfYear($m1,$d1,$y1); for ($y=$y2+1; $y<$y1; $y++) { $d+= &Date_DaysInYear($y); } $d *= -1; } else { $d=&Date_DayOfYear($m2,$d2,$y2) - &Date_DayOfYear($m1,$d1,$y1); } $delta[2]=0; $delta[3]=$d; for ($i=0; $i<7; $i++) { $delta[$i]="+".$delta[$i] if ($delta[$i]>=0); } $delta=join(":",@delta); $delta=&Delta_Normalize($delta,0); return $delta; } my($date1,$date2)=($D1,$D2); my($tmp,$sign,$err,@tmp)=(); # make sure both are work days if ($mode==2 || $mode==3) { $date1=&Date_NextWorkDay($date1,0,1); $date2=&Date_NextWorkDay($date2,0,1); } # make sure date1 comes before date2 if (&Date_Cmp($date1,$date2)>0) { $sign="-"; $tmp=$date1; $date1=$date2; $date2=$tmp; } else { $sign="+"; } if (&Date_Cmp($date1,$date2)==0) { return "+0:+0:+0:+0:+0:+0:+0" if ($Cnf{"DeltaSigns"}); return "+0:0:0:0:0:0:0"; } my($y1,$m1,$d1,$h1,$mn1,$s1)=&Date_Split($date1, 1); my($y2,$m2,$d2,$h2,$mn2,$s2)=&Date_Split($date2, 1); my($dy,$dm,$dw,$dd,$dh,$dmn,$ds,$ddd)=(0,0,0,0,0,0,0,0); if ($mode != 3) { # Do years $dy=$y2-$y1; $dm=0; if ($dy>0) { $tmp=&DateCalc_DateDelta($date1,"+$dy:0:0:0:0:0:0",\$err,0); if (&Date_Cmp($tmp,$date2)>0) { $dy--; $tmp=$date1; $tmp=&DateCalc_DateDelta($date1,"+$dy:0:0:0:0:0:0",\$err,0) if ($dy>0); $dm=12; } $date1=$tmp; } # Do months $dm+=$m2-$m1; if ($dm>0) { $tmp=&DateCalc_DateDelta($date1,"+0:$dm:0:0:0:0:0",\$err,0); if (&Date_Cmp($tmp,$date2)>0) { $dm--; $tmp=$date1; $tmp=&DateCalc_DateDelta($date1,"+0:$dm:0:0:0:0:0",\$err,0) if ($dm>0); } $date1=$tmp; } # At this point, check to see that we're on a business day again so that # Aug 3 (Monday) -> Sep 3 (Sunday) -> Sep 4 (Monday) = 1 month if ($mode==2) { if (! &Date_IsWorkDay($date1,0)) { $date1=&Date_NextWorkDay($date1,0,1); } } } # Do days if ($mode==2 || $mode==3) { $dd=0; while (1) { $tmp=&Date_NextWorkDay($date1,1,1); if (&Date_Cmp($tmp,$date2)<=0) { $dd++; $date1=$tmp; } else { last; } } } else { ($y1,$m1,$d1)=( &Date_Split($date1, 1) )[0..2]; $dd=0; # If we're jumping across months, set $d1 to the first of the next month # (or possibly the 0th of next month which is equivalent to the last day # of this month) if ($m1!=$m2) { $d_in_m[2]=29 if (&Date_LeapYear($y1)); $dd=$d_in_m[$m1]-$d1+1; $d1=1; $tmp=&DateCalc_DateDelta($date1,"+0:0:0:$dd:0:0:0",\$err,0); if (&Date_Cmp($tmp,$date2)>0) { $dd--; $d1--; $tmp=&DateCalc_DateDelta($date1,"+0:0:0:$dd:0:0:0",\$err,0); } $date1=$tmp; } $ddd=0; if ($d1<$d2) { $ddd=$d2-$d1; $tmp=&DateCalc_DateDelta($date1,"+0:0:0:$ddd:0:0:0",\$err,0); if (&Date_Cmp($tmp,$date2)>0) { $ddd--; $tmp=&DateCalc_DateDelta($date1,"+0:0:0:$ddd:0:0:0",\$err,0); } $date1=$tmp; } $dd+=$ddd; } # in business mode, make sure h1 comes before h2 (if not find delta between # now and end of day and move to start of next business day) $d1=( &Date_Split($date1, 1) )[2]; $dh=$dmn=$ds=0; if ($mode==2 || $mode==3 and $d1 != $d2) { $tmp=&Date_SetTime($date1,$Cnf{"WorkDayEnd"}); $tmp=&DateCalc_DateDelta($tmp,"+0:0:0:0:0:1:0") if ($Cnf{"WorkDay24Hr"}); $tmp=&DateCalc_DateDate($date1,$tmp,0); ($tmp,$tmp,$tmp,$tmp,$dh,$dmn,$ds)=&Delta_Split($tmp); $date1=&Date_NextWorkDay($date1,1,0); $date1=&Date_SetTime($date1,$Cnf{"WorkDayBeg"}); $d1=( &Date_Split($date1, 1) )[2]; confess "ERROR: DateCalc DateDate Business.\n" if ($d1 != $d2); } # Hours, minutes, seconds $tmp=&DateCalc_DateDate($date1,$date2,0); @tmp=&Delta_Split($tmp); $dh += $tmp[4]; $dmn += $tmp[5]; $ds += $tmp[6]; $tmp="$sign$dy:$dm:0:$dd:$dh:$dmn:$ds"; &Delta_Normalize($tmp,$mode); } sub DateCalc_DeltaDelta { print "DEBUG: DateCalc_DeltaDelta\n" if ($Curr{"Debug"} =~ /trace/); my($D1,$D2,$mode)=@_; my(@delta1,@delta2,$i,$delta,@delta)=(); $mode=0 if (! defined $mode); @delta1=&Delta_Split($D1); @delta2=&Delta_Split($D2); for ($i=0; $i<7; $i++) { $delta[$i]=$delta1[$i]+$delta2[$i]; $delta[$i]="+".$delta[$i] if ($delta[$i]>=0); } $delta=join(":",@delta); $delta=&Delta_Normalize($delta,$mode); return $delta; } sub DateCalc_DateDelta { print "DEBUG: DateCalc_DateDelta\n" if ($Curr{"Debug"} =~ /trace/); my($D1,$D2,$errref,$mode)=@_; my($date)=(); my(@d_in_m)=(0,31,28,31,30,31,30,31,31,30,31,30,31); my($h1,$m1,$h2,$m2,$len,$hh,$mm)=(); $mode=0 if (! defined $mode); if ($mode==2 || $mode==3) { $h1=$Curr{"WDBh"}; $m1=$Curr{"WDBm"}; $h2=$Curr{"WDEh"}; $m2=$Curr{"WDEm"}; $hh=$h2-$h1; $mm=$m2-$m1; if ($mm<0) { $hh--; $mm+=60; } } # Date, delta my($y,$m,$d,$h,$mn,$s)=&Date_Split($D1, 1); my($dy,$dm,$dw,$dd,$dh,$dmn,$ds)=&Delta_Split($D2); # do the month/year part $y+=$dy; while (length($y)<4) { $y = "0$y"; } &ModuloAddition(-12,$dm,\$m,\$y); # -12 means 1-12 instead of 0-11 $d_in_m[2]=29 if (&Date_LeapYear($y)); # if we have gone past the last day of a month, move the date back to # the last day of the month if ($d>$d_in_m[$m]) { $d=$d_in_m[$m]; } # do the week part if ($mode==0 || $mode==1) { $dd += $dw*7; } else { $date=&DateCalc_DateDelta(&Date_Join($y,$m,$d,$h,$mn,$s), "+0:0:$dw:0:0:0:0",0); ($y,$m,$d,$h,$mn,$s)=&Date_Split($date, 1); } # in business mode, set the day to a work day at this point so the h/mn/s # stuff will work out if ($mode==2 || $mode==3) { $d=$d_in_m[$m] if ($d>$d_in_m[$m]); $date=&Date_NextWorkDay(&Date_Join($y,$m,$d,$h,$mn,$s),0,1); ($y,$m,$d,$h,$mn,$s)=&Date_Split($date, 1); } # seconds, minutes, hours &ModuloAddition(60,$ds,\$s,\$mn); if ($mode==2 || $mode==3) { while (1) { &ModuloAddition(60,$dmn,\$mn,\$h); $h+= $dh; if ($h>$h2 or $h==$h2 && $mn>$m2) { $dh=$h-$h2; $dmn=$mn-$m2; $h=$h1; $mn=$m1; $dd++; } elsif ($h<$h1 or $h==$h1 && $mn<$m1) { $dh=$h-$h1; $dmn=$m1-$mn; $h=$h2; $mn=$m2; $dd--; } elsif ($h==$h2 && $mn==$m2) { $dd++; $dh=-$hh; $dmn=-$mm; } else { last; } } } else { &ModuloAddition(60,$dmn,\$mn,\$h); &ModuloAddition(24,$dh,\$h,\$d); } # If we have just gone past the last day of the month, we need to make # up for this: if ($d>$d_in_m[$m]) { $dd+= $d-$d_in_m[$m]; $d=$d_in_m[$m]; } # days if ($mode==2 || $mode==3) { if ($dd>=0) { $date=&Date_NextWorkDay(&Date_Join($y,$m,$d,$h,$mn,$s),$dd,1); } else { $date=&Date_PrevWorkDay(&Date_Join($y,$m,$d,$h,$mn,$s),-$dd,1); } ($y,$m,$d,$h,$mn,$s)=&Date_Split($date, 1); } else { $d_in_m[2]=29 if (&Date_LeapYear($y)); $d=$d_in_m[$m] if ($d>$d_in_m[$m]); $d += $dd; while ($d<1) { $m--; if ($m==0) { $m=12; $y--; if (&Date_LeapYear($y)) { $d_in_m[2]=29; } else { $d_in_m[2]=28; } } $d += $d_in_m[$m]; } while ($d>$d_in_m[$m]) { $d -= $d_in_m[$m]; $m++; if ($m==13) { $m=1; $y++; if (&Date_LeapYear($y)) { $d_in_m[2]=29; } else { $d_in_m[2]=28; } } } } if ($y<0 or $y>9999) { $$errref=3; return; } &Date_Join($y,$m,$d,$h,$mn,$s); } sub Date_UpdateHolidays { print "DEBUG: Date_UpdateHolidays\n" if ($Curr{"Debug"} =~ /trace/); my($year)=@_; $Holiday{"year"}=$year; $Holiday{"dates"}{$year}={}; my($date,$delta,$err)=(); my($key,@tmp,$tmp); foreach $key (keys %{ $Holiday{"desc"} }) { @tmp=&Recur_Split($key); if (@tmp) { $tmp=&ParseDateString("${year}010100:00:00"); ($date)=&ParseRecur($key,$tmp,$tmp,($year+1)."-01-01"); next if (! $date); } elsif ($key =~ /^(.*)([+-].*)$/) { # Date +/- Delta ($date,$delta)=($1,$2); $tmp=&ParseDateString("$date $year"); if ($tmp) { $date=$tmp; } else { $date=&ParseDateString($date); next if ($date !~ /^$year/); } $date=&DateCalc($date,$delta,\$err,0); } else { # Date $date=$key; $tmp=&ParseDateString("$date $year"); if ($tmp) { $date=$tmp; } else { $date=&ParseDateString($date); next if ($date !~ /^$year/); } } $Holiday{"dates"}{$year}{$date}=$Holiday{"desc"}{$key}; } } # This sets a Date::Manip config variable. sub Date_SetConfigVariable { print "DEBUG: Date_SetConfigVariable\n" if ($Curr{"Debug"} =~ /trace/); my($var,$val)=@_; # These are most appropriate for command line options instead of in files. $Cnf{"PathSep"}=$val, return if ($var =~ /^PathSep$/i); $Cnf{"PersonalCnf"}=$val, return if ($var =~ /^PersonalCnf$/i); $Cnf{"PersonalCnfPath"}=$val, return if ($var =~ /^PersonalCnfPath$/i); &EraseHolidays(), return if ($var =~ /^EraseHolidays$/i); $Cnf{"IgnoreGlobalCnf"}=1, return if ($var =~ /^IgnoreGlobalCnf$/i); $Cnf{"GlobalCnf"}=$val, return if ($var =~ /^GlobalCnf$/i); $Curr{"InitLang"}=1, $Cnf{"Language"}=$val, return if ($var =~ /^Language$/i); $Cnf{"DateFormat"}=$val, return if ($var =~ /^DateFormat$/i); $Cnf{"TZ"}=$val, return if ($var =~ /^TZ$/i); $Cnf{"ConvTZ"}=$val, return if ($var =~ /^ConvTZ$/i); $Cnf{"Internal"}=$val, return if ($var =~ /^Internal$/i); $Cnf{"FirstDay"}=$val, return if ($var =~ /^FirstDay$/i); $Cnf{"WorkWeekBeg"}=$val, return if ($var =~ /^WorkWeekBeg$/i); $Cnf{"WorkWeekEnd"}=$val, return if ($var =~ /^WorkWeekEnd$/i); $Cnf{"WorkDayBeg"}=$val, $Curr{"ResetWorkDay"}=1, return if ($var =~ /^WorkDayBeg$/i); $Cnf{"WorkDayEnd"}=$val, $Curr{"ResetWorkDay"}=1, return if ($var =~ /^WorkDayEnd$/i); $Cnf{"WorkDay24Hr"}=$val, $Curr{"ResetWorkDay"}=1, return if ($var =~ /^WorkDay24Hr$/i); $Cnf{"DeltaSigns"}=$val, return if ($var =~ /^DeltaSigns$/i); $Cnf{"Jan1Week1"}=$val, return if ($var =~ /^Jan1Week1$/i); $Cnf{"YYtoYYYY"}=$val, return if ($var =~ /^YYtoYYYY$/i); $Cnf{"UpdateCurrTZ"}=$val, return if ($var =~ /^UpdateCurrTZ$/i); $Cnf{"IntCharSet"}=$val, return if ($var =~ /^IntCharSet$/i); $Curr{"DebugVal"}=$val, return if ($var =~ /^Debug$/i); $Cnf{"TomorrowFirst"}=$val, return if ($var =~ /^TomorrowFirst$/i); $Cnf{"ForceDate"}=$val, return if ($var =~ /^ForceDate$/i); $Cnf{"TodayIsMidnight"}=$val, return if ($var =~ /^TodayIsMidnight$/i); confess "ERROR: Unknown configuration variable $var in Date::Manip.\n"; } sub EraseHolidays { print "DEBUG: EraseHolidays\n" if ($Curr{"Debug"} =~ /trace/); $Cnf{"EraseHolidays"}=0; delete $Holiday{"list"}; $Holiday{"list"}={}; delete $Holiday{"desc"}; $Holiday{"desc"}={}; $Holiday{"dates"}={}; } # This returns a pointer to a list of times and events in the format # [ date [ events ], date, [ events ], ... ] # where each list of events are events that are in effect at the date # immediately preceding the list. # # This takes either one date or two dates as arguments. sub Events_Calc { print "DEBUG: Events_Calc\n" if ($Curr{"Debug"} =~ /trace/); my($date0,$date1)=@_; my($tmp); $date0=&ParseDateString($date0); return undef if (! $date0); if ($date1) { $date1=&ParseDateString($date1); if (&Date_Cmp($date0,$date1)>0) { $tmp=$date1; $date1=$date0; $date0=$tmp; } } else { $date1=&DateCalc_DateDelta($date0,"+0:0:0:0:0:0:1"); } # # [ d0,d1,del,name ] => [ d0, d1+del ) # [ d0,0,del,name ] => [ d0, d0+del ) # my(%ret,$d0,$d1,$del,$name,$c0,$c1); my(@tmp)=@{ $Events{"dates"} }; DATE: while (@tmp) { ($d0,$d1,$del,$name)=splice(@tmp,0,4); $d0=&ParseDateString($d0); $d1=&ParseDateString($d1) if ($d1); $del=&ParseDateDelta($del) if ($del); if ($d1) { if ($del) { $d1=&DateCalc_DateDelta($d1,$del); } } else { $d1=&DateCalc_DateDelta($d0,$del); } if (&Date_Cmp($d0,$d1)>0) { $tmp=$d1; $d1=$d0; $d0=$tmp; } # [ date0,date1 ) # [ d0,d1 ) OR [ d0,d1 ) next DATE if (&Date_Cmp($d1,$date0)<=0 || &Date_Cmp($d0,$date1)>=0); # [ date0,date1 ) # [ d0,d1 ) # [ d0, d1 ) if (&Date_Cmp($d0,$date0)<=0) { push @{ $ret{$date0} },$name; push @{ $ret{$d1} },"!$name" if (&Date_Cmp($d1,$date1)<0); next DATE; } # [ date0,date1 ) # [ d0,d1 ) if (&Date_Cmp($d1,$date1)>=0) { push @{ $ret{$d0} },$name; next DATE; } # [ date0,date1 ) # [ d0,d1 ) push @{ $ret{$d0} },$name; push @{ $ret{$d1} },"!$name"; } # # [ recur,delta0,delta1,name ] => [ {date-delta0},{date+delta1} ) # my($rec,$del0,$del1,@d); @tmp=@{ $Events{"recur"} }; RECUR: while (@tmp) { ($rec,$del0,$del1,$name)=splice(@tmp,0,4); @d=(); } # Sort them AND take into account the "!$name" entries. my(%tmp,$date,@tmp2,@ret); @d=sort { &Date_Cmp($a,$b) } keys %ret; foreach $date (@d) { @tmp=@{ $ret{$date} }; @tmp2=(); foreach $tmp (@tmp) { push(@tmp2,$tmp), next if ($tmp =~ /^!/); $tmp{$tmp}=1; } foreach $tmp (@tmp2) { $tmp =~ s/^!//; delete $tmp{$tmp}; } push(@ret,$date,[ keys %tmp ]); } %tmp = @ret; @ret = (); foreach my $d (sort { Date_Cmp($a,$b) } keys %tmp) { my $e = $tmp{$d}; push @ret,($d,[ sort @$e ]); } return \@ret; } # This parses the raw events list sub Events_ParseRaw { print "DEBUG: Events_ParseRaw\n" if ($Curr{"Debug"} =~ /trace/); # Only need to be parsed once my($force)=@_; $Events{"parsed"}=0 if ($force); return if ($Events{"parsed"}); $Events{"parsed"}=1; my(@events)=@{ $Events{"raw"} }; my($event,$name,@event,$date0,$date1,$tmp,$delta,$recur0,$recur1,@recur,$r, $recur); EVENT: while (@events) { ($event,$name)=splice(@events,0,2); @event=split(/\s*;\s*/,$event); if ($#event == 0) { if ($date0=&ParseDateString($event[0])) { # # date = event # $tmp=&ParseDateString("$event[0] 00:00:00"); if ($tmp && $tmp eq $date0) { $delta="+0:0:0:1:0:0:0"; } else { $delta="+0:0:0:0:1:0:0"; } push @{ $Events{"dates"} },($date0,0,$delta,$name); } elsif ($recur=&ParseRecur($event[0])) { # # recur = event # ($recur0,$recur1)=&Recur_Split($recur); if ($recur0) { if ($recur1) { $r="$recur0:$recur1"; } else { $r=$recur0; } } else { $r=$recur1; } (@recur)=split(/:/,$r); if (pop(@recur)==0 && pop(@recur)==0 && pop(@recur)==0) { $delta="+0:0:0:1:0:0:0"; } else { $delta="+0:0:0:0:1:0:0"; } push @{ $Events{"recur"} },($recur,0,$delta,$name); } else { # ??? = event warn "WARNING: illegal event ignored [ @event ]\n"; next EVENT; } } elsif ($#event == 1) { if ($date0=&ParseDateString($event[0])) { if ($date1=&ParseDateString($event[1])) { # # date ; date = event # $tmp=&ParseDateString("$event[1] 00:00:00"); if ($tmp && $tmp eq $date1) { $date1=&DateCalc_DateDelta($date1,"+0:0:0:1:0:0:0"); } push @{ $Events{"dates"} },($date0,$date1,0,$name); } elsif ($delta=&ParseDateDelta($event[1])) { # # date ; delta = event # push @{ $Events{"dates"} },($date0,0,$delta,$name); } else { # date ; ??? = event warn "WARNING: illegal event ignored [ @event ]\n"; next EVENT; } } elsif ($recur=&ParseRecur($event[0])) { if ($delta=&ParseDateDelta($event[1])) { # # recur ; delta = event # push @{ $Events{"recur"} },($recur,0,$delta,$name); } else { # recur ; ??? = event warn "WARNING: illegal event ignored [ @event ]\n"; next EVENT; } } else { # ??? ; ??? = event warn "WARNING: illegal event ignored [ @event ]\n"; next EVENT; } } else { # date ; delta0 ; delta1 = event # recur ; delta0 ; delta1 = event # ??? ; ??? ; ??? ... = event warn "WARNING: illegal event ignored [ @event ]\n"; next EVENT; } } } # This reads an init file. sub Date_InitFile { print "DEBUG: Date_InitFile\n" if ($Curr{"Debug"} =~ /trace/); my($file)=@_; my($in)=new IO::File; local($_)=(); my($section)="vars"; my($var,$val,$recur,$name)=(); $in->open($file) || return; while(defined ($_=<$in>)) { chomp; s/^\s+//; s/\s+$//; next if (! $_ or /^\#/); if (/^\*holiday/i) { $section="holiday"; &EraseHolidays() if ($section =~ /holiday/i && $Cnf{"EraseHolidays"}); next; } elsif (/^\*events/i) { $section="events"; next; } if ($section =~ /var/i) { confess "ERROR: invalid Date::Manip config file line.\n $_\n" if (! /(.*\S)\s*=\s*(.*)$/); ($var,$val)=($1,$2); &Date_SetConfigVariable($var,$val); } elsif ($section =~ /holiday/i) { confess "ERROR: invalid Date::Manip config file line.\n $_\n" if (! /(.*\S)\s*=\s*(.*)$/); ($recur,$name)=($1,$2); $name="" if (! defined $name); $Holiday{"desc"}{$recur}=$name; } elsif ($section =~ /events/i) { confess "ERROR: invalid Date::Manip config file line.\n $_\n" if (! /(.*\S)\s*=\s*(.*)$/); ($val,$var)=($1,$2); push @{ $Events{"raw"} },($val,$var); } else { # A section not currently used by Date::Manip (but may be # used by some extension to it). next; } } close($in); } # $flag=&Date_TimeCheck(\$h,\$mn,\$s,\$ampm); # Returns 1 if any of the fields are bad. All fields are optional, and # all possible checks are done on the data. If a field is not passed in, # it is set to default values. If data is missing, appropriate defaults # are supplied. sub Date_TimeCheck { print "DEBUG: Date_TimeCheck\n" if ($Curr{"Debug"} =~ /trace/); my($h,$mn,$s,$ampm)=@_; my($tmp1,$tmp2,$tmp3)=(); $$h="" if (! defined $$h); $$mn="" if (! defined $$mn); $$s="" if (! defined $$s); $$ampm="" if (! defined $$ampm); $$ampm=uc($$ampm) if ($$ampm); # Check hour $tmp1=$Lang{$Cnf{"Language"}}{"AmPm"}; $tmp2=""; if ($$ampm =~ /^$tmp1$/i) { $tmp3=$Lang{$Cnf{"Language"}}{"AM"}; $tmp2="AM" if ($$ampm =~ /^$tmp3$/i); $tmp3=$Lang{$Cnf{"Language"}}{"PM"}; $tmp2="PM" if ($$ampm =~ /^$tmp3$/i); } elsif ($$ampm) { return 1; } if ($tmp2 eq "AM" || $tmp2 eq "PM") { $$h="0$$h" if (length($$h)==1); return 1 if ($$h<1 || $$h>12); $$h="00" if ($tmp2 eq "AM" and $$h==12); $$h += 12 if ($tmp2 eq "PM" and $$h!=12); } else { $$h="00" if ($$h eq ""); $$h="0$$h" if (length($$h)==1); return 1 if (! &IsInt($$h,0,23)); $tmp2="AM" if ($$h<12); $tmp2="PM" if ($$h>=12); } $$ampm=$Lang{$Cnf{"Language"}}{"AMstr"}; $$ampm=$Lang{$Cnf{"Language"}}{"PMstr"} if ($tmp2 eq "PM"); # Check minutes $$mn="00" if ($$mn eq ""); $$mn="0$$mn" if (length($$mn)==1); return 1 if (! &IsInt($$mn,0,59)); # Check seconds $$s="00" if ($$s eq ""); $$s="0$$s" if (length($$s)==1); return 1 if (! &IsInt($$s,0,59)); return 0; } # $flag=&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk); # Returns 1 if any of the fields are bad. All fields are optional, and # all possible checks are done on the data. If a field is not passed in, # it is set to default values. If data is missing, appropriate defaults # are supplied. # # If the flag UpdateHolidays is set, the year is set to # CurrHolidayYear. sub Date_DateCheck { print "DEBUG: Date_DateCheck\n" if ($Curr{"Debug"} =~ /trace/); my($y,$m,$d,$h,$mn,$s,$ampm,$wk)=@_; my($tmp1,$tmp2,$tmp3)=(); my(@d_in_m)=(0,31,28,31,30,31,30,31,31,30,31,30,31); my($curr_y)=$Curr{"Y"}; my($curr_m)=$Curr{"M"}; my($curr_d)=$Curr{"D"}; $$m=1, $$d=1 if (defined $$y and ! defined $$m and ! defined $$d); $$y="" if (! defined $$y); $$m="" if (! defined $$m); $$d="" if (! defined $$d); $$wk="" if (! defined $$wk); $$d=$curr_d if ($$y eq "" and $$m eq "" and $$d eq ""); # Check year. $$y=$curr_y if ($$y eq ""); $$y=&Date_FixYear($$y) if (length($$y)<4); return 1 if (! &IsInt($$y,0,9999)); $d_in_m[2]=29 if (&Date_LeapYear($$y)); # Check month $$m=$curr_m if ($$m eq ""); $$m=$Lang{$Cnf{"Language"}}{"MonthH"}{lc($$m)} if (exists $Lang{$Cnf{"Language"}}{"MonthH"}{lc($$m)}); $$m="0$$m" if (length($$m)==1); return 1 if (! &IsInt($$m,1,12)); # Check day $$d="01" if ($$d eq ""); $$d="0$$d" if (length($$d)==1); return 1 if (! &IsInt($$d,1,$d_in_m[$$m])); if ($$wk) { $tmp1=&Date_DayOfWeek($$m,$$d,$$y); $tmp2=$Lang{$Cnf{"Language"}}{"WeekH"}{lc($$wk)} if (exists $Lang{$Cnf{"Language"}}{"WeekH"}{lc($$wk)}); return 1 if ($tmp1 != $tmp2); } return &Date_TimeCheck($h,$mn,$s,$ampm); } # Takes a year in 2 digit form and returns it in 4 digit form sub Date_FixYear { print "DEBUG: Date_FixYear\n" if ($Curr{"Debug"} =~ /trace/); my($y)=@_; my($curr_y)=$Curr{"Y"}; $y=$curr_y if (! defined $y or ! $y); return $y if (length($y)==4); confess "ERROR: Invalid year ($y)\n" if (length($y)!=2); my($y1,$y2)=(); if (lc($Cnf{"YYtoYYYY"}) eq "c") { $y1=substring($y,0,2); $y="$y1$y"; } elsif ($Cnf{"YYtoYYYY"} =~ /^c(\d{2})$/i) { $y1=$1; $y="$y1$y"; } elsif ($Cnf{"YYtoYYYY"} =~ /^c(\d{2})(\d{2})$/i) { $y1="$1$2"; $y ="$1$y"; $y += 100 if ($y<$y1); } else { $y1=$curr_y-$Cnf{"YYtoYYYY"}; $y2=$y1+99; $y="19$y"; while ($y<$y1) { $y+=100; } while ($y>$y2) { $y-=100; } } $y; } # &Date_NthWeekOfYear($y,$n); # Returns a list of (YYYY,MM,DD) for the 1st day of the Nth week of the # year. # &Date_NthWeekOfYear($y,$n,$dow,$flag); # Returns a list of (YYYY,MM,DD) for the Nth DoW of the year. If flag # is nil, the first DoW of the year may actually be in the previous # year (since the 1st week may include days from the previous year). # If flag is non-nil, the 1st DoW of the year refers to the 1st one # actually in the year sub Date_NthWeekOfYear { print "DEBUG: Date_NthWeekOfYear\n" if ($Curr{"Debug"} =~ /trace/); my($y,$n,$dow,$flag)=@_; my($m,$d,$err,$tmp,$date,%dow)=(); $y=$Curr{"Y"} if (! defined $y or ! $y); $n=1 if (! defined $n or $n eq ""); return () if ($n<0 || $n>53); if (defined $dow) { $dow=lc($dow); %dow=%{ $Lang{$Cnf{"Language"}}{"WeekH"} }; $dow=$dow{$dow} if (exists $dow{$dow}); return () if ($dow<1 || $dow>7); $flag="" if (! defined $flag); } else { $dow=""; $flag=""; } $y=&Date_FixYear($y) if (length($y)<4); if ($Cnf{"Jan1Week1"}) { $date=&Date_Join($y,1,1,0,0,0); } else { $date=&Date_Join($y,1,4,0,0,0); } $date=&Date_GetPrev($date,$Cnf{"FirstDay"},1); $date=&Date_GetNext($date,$dow,1) if ($dow ne ""); if ($flag) { ($tmp)=&Date_Split($date, 1); $n++ if ($tmp != $y); } if ($n>1) { $date=&DateCalc_DateDelta($date,"+0:0:". ($n-1) . ":0:0:0:0",\$err,0); } elsif ($n==0) { $date=&DateCalc_DateDelta($date,"-0:0:1:0:0:0:0",\$err,0); } ($y,$m,$d)=&Date_Split($date, 1); ($y,$m,$d); } ######################################################################## # LANGUAGE INITIALIZATION ######################################################################## # 8-bit international characters can be gotten by "\xXX". I don't know # how to get 16-bit characters. I've got to read up on perllocale. sub Char_8Bit { my($hash)=@_; # grave ` # A` 00c0 a` 00e0 # E` 00c8 e` 00e8 # I` 00cc i` 00ec # O` 00d2 o` 00f2 # U` 00d9 u` 00f9 # W` 1e80 w` 1e81 # Y` 1ef2 y` 1ef3 $$hash{"A`"} = "\xc0"; # LATIN CAPITAL LETTER A WITH GRAVE $$hash{"E`"} = "\xc8"; # LATIN CAPITAL LETTER E WITH GRAVE $$hash{"I`"} = "\xcc"; # LATIN CAPITAL LETTER I WITH GRAVE $$hash{"O`"} = "\xd2"; # LATIN CAPITAL LETTER O WITH GRAVE $$hash{"U`"} = "\xd9"; # LATIN CAPITAL LETTER U WITH GRAVE $$hash{"a`"} = "\xe0"; # LATIN SMALL LETTER A WITH GRAVE $$hash{"e`"} = "\xe8"; # LATIN SMALL LETTER E WITH GRAVE $$hash{"i`"} = "\xec"; # LATIN SMALL LETTER I WITH GRAVE $$hash{"o`"} = "\xf2"; # LATIN SMALL LETTER O WITH GRAVE $$hash{"u`"} = "\xf9"; # LATIN SMALL LETTER U WITH GRAVE # acute ' # A' 00c1 a' 00e1 # C' 0106 c' 0107 # E' 00c9 e' 00e9 # I' 00cd i' 00ed # L' 0139 l' 013a # N' 0143 n' 0144 # O' 00d3 o' 00f3 # R' 0154 r' 0155 # S' 015a s' 015b # U' 00da u' 00fa # W' 1e82 w' 1e83 # Y' 00dd y' 00fd # Z' 0179 z' 017a $$hash{"A'"} = "\xc1"; # LATIN CAPITAL LETTER A WITH ACUTE $$hash{"E'"} = "\xc9"; # LATIN CAPITAL LETTER E WITH ACUTE $$hash{"I'"} = "\xcd"; # LATIN CAPITAL LETTER I WITH ACUTE $$hash{"O'"} = "\xd3"; # LATIN CAPITAL LETTER O WITH ACUTE $$hash{"U'"} = "\xda"; # LATIN CAPITAL LETTER U WITH ACUTE $$hash{"Y'"} = "\xdd"; # LATIN CAPITAL LETTER Y WITH ACUTE $$hash{"a'"} = "\xe1"; # LATIN SMALL LETTER A WITH ACUTE $$hash{"e'"} = "\xe9"; # LATIN SMALL LETTER E WITH ACUTE $$hash{"i'"} = "\xed"; # LATIN SMALL LETTER I WITH ACUTE $$hash{"o'"} = "\xf3"; # LATIN SMALL LETTER O WITH ACUTE $$hash{"u'"} = "\xfa"; # LATIN SMALL LETTER U WITH ACUTE $$hash{"y'"} = "\xfd"; # LATIN SMALL LETTER Y WITH ACUTE # double acute " " # O" 0150 o" 0151 # U" 0170 u" 0171 # circumflex ^ # A^ 00c2 a^ 00e2 # C^ 0108 c^ 0109 # E^ 00ca e^ 00ea # G^ 011c g^ 011d # H^ 0124 h^ 0125 # I^ 00ce i^ 00ee # J^ 0134 j^ 0135 # O^ 00d4 o^ 00f4 # S^ 015c s^ 015d # U^ 00db u^ 00fb # W^ 0174 w^ 0175 # Y^ 0176 y^ 0177 $$hash{"A^"} = "\xc2"; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX $$hash{"E^"} = "\xca"; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX $$hash{"I^"} = "\xce"; # LATIN CAPITAL LETTER I WITH CIRCUMFLEX $$hash{"O^"} = "\xd4"; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX $$hash{"U^"} = "\xdb"; # LATIN CAPITAL LETTER U WITH CIRCUMFLEX $$hash{"a^"} = "\xe2"; # LATIN SMALL LETTER A WITH CIRCUMFLEX $$hash{"e^"} = "\xea"; # LATIN SMALL LETTER E WITH CIRCUMFLEX $$hash{"i^"} = "\xee"; # LATIN SMALL LETTER I WITH CIRCUMFLEX $$hash{"o^"} = "\xf4"; # LATIN SMALL LETTER O WITH CIRCUMFLEX $$hash{"u^"} = "\xfb"; # LATIN SMALL LETTER U WITH CIRCUMFLEX # tilde ~ # A~ 00c3 a~ 00e3 # I~ 0128 i~ 0129 # N~ 00d1 n~ 00f1 # O~ 00d5 o~ 00f5 # U~ 0168 u~ 0169 $$hash{"A~"} = "\xc3"; # LATIN CAPITAL LETTER A WITH TILDE $$hash{"N~"} = "\xd1"; # LATIN CAPITAL LETTER N WITH TILDE $$hash{"O~"} = "\xd5"; # LATIN CAPITAL LETTER O WITH TILDE $$hash{"a~"} = "\xe3"; # LATIN SMALL LETTER A WITH TILDE $$hash{"n~"} = "\xf1"; # LATIN SMALL LETTER N WITH TILDE $$hash{"o~"} = "\xf5"; # LATIN SMALL LETTER O WITH TILDE # macron - # A- 0100 a- 0101 # E- 0112 e- 0113 # I- 012a i- 012b # O- 014c o- 014d # U- 016a u- 016b # breve ( [half circle up] # A( 0102 a( 0103 # G( 011e g( 011f # U( 016c u( 016d # dot . # C. 010a c. 010b # E. 0116 e. 0117 # G. 0120 g. 0121 # I. 0130 # Z. 017b z. 017c # diaeresis : [side by side dots] # A: 00c4 a: 00e4 # E: 00cb e: 00eb # I: 00cf i: 00ef # O: 00d6 o: 00f6 # U: 00dc u: 00fc # W: 1e84 w: 1e85 # Y: 0178 y: 00ff $$hash{"A:"} = "\xc4"; # LATIN CAPITAL LETTER A WITH DIAERESIS $$hash{"E:"} = "\xcb"; # LATIN CAPITAL LETTER E WITH DIAERESIS $$hash{"I:"} = "\xcf"; # LATIN CAPITAL LETTER I WITH DIAERESIS $$hash{"O:"} = "\xd6"; # LATIN CAPITAL LETTER O WITH DIAERESIS $$hash{"U:"} = "\xdc"; # LATIN CAPITAL LETTER U WITH DIAERESIS $$hash{"a:"} = "\xe4"; # LATIN SMALL LETTER A WITH DIAERESIS $$hash{"e:"} = "\xeb"; # LATIN SMALL LETTER E WITH DIAERESIS $$hash{"i:"} = "\xef"; # LATIN SMALL LETTER I WITH DIAERESIS $$hash{"o:"} = "\xf6"; # LATIN SMALL LETTER O WITH DIAERESIS $$hash{"u:"} = "\xfc"; # LATIN SMALL LETTER U WITH DIAERESIS $$hash{"y:"} = "\xff"; # LATIN SMALL LETTER Y WITH DIAERESIS # ring o # U0 016e u0 016f # cedilla , [squiggle down and left below the letter] # ,C 00c7 ,c 00e7 # ,G 0122 ,g 0123 # ,K 0136 ,k 0137 # ,L 013b ,l 013c # ,N 0145 ,n 0146 # ,R 0156 ,r 0157 # ,S 015e ,s 015f # ,T 0162 ,t 0163 $$hash{",C"} = "\xc7"; # LATIN CAPITAL LETTER C WITH CEDILLA $$hash{",c"} = "\xe7"; # LATIN SMALL LETTER C WITH CEDILLA # ogonek ; [squiggle down and right below the letter] # A; 0104 a; 0105 # E; 0118 e; 0119 # I; 012e i; 012f # U; 0172 u; 0173 # caron < [little v on top] # A< 01cd a< 01ce # C< 010c c< 010d # D< 010e d< 010f # E< 011a e< 011b # L< 013d l< 013e # N< 0147 n< 0148 # R< 0158 r< 0159 # S< 0160 s< 0161 # T< 0164 t< 0165 # Z< 017d z< 017e # Other characters # First character is below, 2nd character is above $$hash{"||"} = "\xa6"; # BROKEN BAR $$hash{" :"} = "\xa8"; # DIAERESIS $$hash{"-a"} = "\xaa"; # FEMININE ORDINAL INDICATOR #$$hash{" -"}= "\xaf"; # MACRON (narrow bar) $$hash{" -"} = "\xad"; # HYPHEN (wide bar) $$hash{" o"} = "\xb0"; # DEGREE SIGN $$hash{"-+"} = "\xb1"; # PLUS\342\200\220MINUS SIGN $$hash{" 1"} = "\xb9"; # SUPERSCRIPT ONE $$hash{" 2"} = "\xb2"; # SUPERSCRIPT TWO $$hash{" 3"} = "\xb3"; # SUPERSCRIPT THREE $$hash{" '"} = "\xb4"; # ACUTE ACCENT $$hash{"-o"} = "\xba"; # MASCULINE ORDINAL INDICATOR $$hash{" ."} = "\xb7"; # MIDDLE DOT $$hash{", "} = "\xb8"; # CEDILLA $$hash{"Ao"} = "\xc5"; # LATIN CAPITAL LETTER A WITH RING ABOVE $$hash{"ao"} = "\xe5"; # LATIN SMALL LETTER A WITH RING ABOVE $$hash{"ox"} = "\xf0"; # LATIN SMALL LETTER ETH # upside down characters $$hash{"ud!"} = "\xa1"; # INVERTED EXCLAMATION MARK $$hash{"ud?"} = "\xbf"; # INVERTED QUESTION MARK # overlay characters $$hash{"X o"} = "\xa4"; # CURRENCY SIGN $$hash{"Y ="} = "\xa5"; # YEN SIGN $$hash{"S o"} = "\xa7"; # SECTION SIGN $$hash{"O c"} = "\xa9"; # COPYRIGHT SIGN Copyright $$hash{"O R"} = "\xae"; # REGISTERED SIGN $$hash{"D -"} = "\xd0"; # LATIN CAPITAL LETTER ETH $$hash{"O /"} = "\xd8"; # LATIN CAPITAL LETTER O WITH STROKE $$hash{"o /"} = "\xf8"; # LATIN SMALL LETTER O WITH STROKE # special names $$hash{"1/4"} = "\xbc"; # VULGAR FRACTION ONE QUARTER $$hash{"1/2"} = "\xbd"; # VULGAR FRACTION ONE HALF $$hash{"3/4"} = "\xbe"; # VULGAR FRACTION THREE QUARTERS $$hash{"<<"} = "\xab"; # LEFT POINTING DOUBLE ANGLE QUOTATION MARK $$hash{">>"} = "\xbb"; # RIGHT POINTING DOUBLE ANGLE QUOTATION MARK $$hash{"cent"}= "\xa2"; # CENT SIGN $$hash{"lb"} = "\xa3"; # POUND SIGN $$hash{"mu"} = "\xb5"; # MICRO SIGN $$hash{"beta"}= "\xdf"; # LATIN SMALL LETTER SHARP S $$hash{"para"}= "\xb6"; # PILCROW SIGN $$hash{"-|"} = "\xac"; # NOT SIGN $$hash{"AE"} = "\xc6"; # LATIN CAPITAL LETTER AE $$hash{"ae"} = "\xe6"; # LATIN SMALL LETTER AE $$hash{"x"} = "\xd7"; # MULTIPLICATION SIGN $$hash{"P"} = "\xde"; # LATIN CAPITAL LETTER THORN $$hash{"/"} = "\xf7"; # DIVISION SIGN $$hash{"p"} = "\xfe"; # LATIN SMALL LETTER THORN } # $hashref = &Date_Init_LANGUAGE; # This returns a hash containing all of the initialization for a # specific language. The hash elements are: # # @ month_name full month names January February ... # @ month_abb month abbreviations Jan Feb ... # @ day_name day names Monday Tuesday ... # @ day_abb day abbreviations Mon Tue ... # @ day_char day character abbrevs M T ... # @ am AM notations # @ pm PM notations # # @ num_suff number with suffix 1st 2nd ... # @ num_word numbers spelled out first second ... # # $ now words which mean now now ... # $ today words which mean today today ... # $ last words which mean last last final ... # $ each words which mean each each every ... # $ of of (as in a member of) in of ... # ex. 4th day OF June # $ at at 4:00 at # $ on on Sunday on # $ future in the future in # $ past in the past ago # $ next next item next # $ prev previous item last previous # $ later 2 hours later # # % offset a hash of special dates { tomorrow->0:0:0:1:0:0:0 } # % times a hash of times { noon->12:00:00 ... } # # $ years words for year y yr year ... # $ months words for month # $ weeks words for week # $ days words for day # $ hours words for hour # $ minutes words for minute # $ seconds words for second # % replace # The replace element is quite important, but a bit tricky. In # English (and probably other languages), one of the abbreviations # for the word month that would be nice is "m". The problem is that # "m" matches the "m" in "minute" which causes the string to be # improperly matched in some cases. Hence, the list of abbreviations # for month is given as: # "mon month months" # In order to allow you to enter "m", replacements can be done. # $replace is a list of pairs of words which are matched and replaced # AS ENTIRE WORDS. Having $replace equal to "m"->"month" means that # the entire word "m" will be replaced with "month". This allows the # desired abbreviation to be used. Make sure that replace contains # an even number of words (i.e. all must be pairs). Any time a # desired abbreviation matches the start of any other, it has to go # here. # # $ exact exact mode exactly # $ approx approximate mode approximately # $ business business mode business # # r sephm hour/minute separator (?::) # r sepms minute/second separator (?::) # r sepss second/fraction separator (?:[.:]) # # Elements marked with an asterix (@) are returned as a set of lists. # Each list contains the strings for each element. The first set is used # when the 7-bit ASCII (US) character set is wanted. The 2nd set is used # when an international character set is available. Both of the 1st two # sets should be complete (but the 2nd list can be left empty to force the # first set to be used always). The 3rd set and later can be partial sets # if desired. # # Elements marked with a dollar ($) are returned as a simple list of words. # # Elements marked with a percent (%) are returned as a hash list. # # Elements marked with (r) are regular expression elements which must not # create a back reference. # # ***NOTE*** Every hash element (unless otherwise noted) MUST be defined in # every language. sub Date_Init_English { print "DEBUG: Date_Init_English\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; $$d{"month_name"}= [["January","February","March","April","May","June", "July","August","September","October","November","December"]]; $$d{"month_abb"}= [["Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"], [], ["","","","","","","","","Sept"]]; $$d{"day_name"}= [["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]]; $$d{"day_abb"}= [["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], ["", "Tues","", "Thur","", "", ""]]; $$d{"day_char"}= [["M","T","W","Th","F","Sa","S"]]; $$d{"num_suff"}= [["1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th", "11th","12th","13th","14th","15th","16th","17th","18th","19th","20th", "21st","22nd","23rd","24th","25th","26th","27th","28th","29th","30th", "31st"]]; $$d{"num_word"}= [["first","second","third","fourth","fifth","sixth","seventh","eighth", "ninth","tenth","eleventh","twelfth","thirteenth","fourteenth", "fifteenth","sixteenth","seventeenth","eighteenth","nineteenth", "twentieth","twenty-first","twenty-second","twenty-third", "twenty-fourth","twenty-fifth","twenty-sixth","twenty-seventh", "twenty-eighth","twenty-ninth","thirtieth","thirty-first"]]; $$d{"now"} =["now"]; $$d{"today"} =["today"]; $$d{"last"} =["last","final"]; $$d{"each"} =["each","every"]; $$d{"of"} =["in","of"]; $$d{"at"} =["at"]; $$d{"on"} =["on"]; $$d{"future"} =["in"]; $$d{"past"} =["ago"]; $$d{"next"} =["next"]; $$d{"prev"} =["previous","last"]; $$d{"later"} =["later"]; $$d{"exact"} =["exactly"]; $$d{"approx"} =["approximately"]; $$d{"business"}=["business"]; $$d{"offset"} =["yesterday","-0:0:0:1:0:0:0","tomorrow","+0:0:0:1:0:0:0"]; $$d{"times"} =["noon","12:00:00","midnight","00:00:00"]; $$d{"years"} =["y","yr","year","yrs","years"]; $$d{"months"} =["mon","month","months"]; $$d{"weeks"} =["w","wk","wks","week","weeks"]; $$d{"days"} =["d","day","days"]; $$d{"hours"} =["h","hr","hrs","hour","hours"]; $$d{"minutes"} =["mn","min","minute","minutes"]; $$d{"seconds"} =["s","sec","second","seconds"]; $$d{"replace"} =["m","month"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } sub Date_Init_Italian { print "DEBUG: Date_Init_Italian\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($i)=$h{"i`"}; $$d{"month_name"}= [[qw(Gennaio Febbraio Marzo Aprile Maggio Giugno Luglio Agosto Settembre Ottobre Novembre Dicembre)]]; $$d{"month_abb"}= [[qw(Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic)]]; $$d{"day_name"}= [[qw(Lunedi Martedi Mercoledi Giovedi Venerdi Sabato Domenica)], [qw(Luned${i} Marted${i} Mercoled${i} Gioved${i} Venerd${i})]]; $$d{"day_abb"}= [[qw(Lun Mar Mer Gio Ven Sab Dom)]]; $$d{"day_char"}= [[qw(L Ma Me G V S D)]]; $$d{"num_suff"}= [[qw(1mo 2do 3zo 4to 5to 6to 7mo 8vo 9no 10mo 11mo 12mo 13mo 14mo 15mo 16mo 17mo 18mo 19mo 20mo 21mo 22mo 23mo 24mo 25mo 26mo 27mo 28mo 29mo 3mo 31mo)]]; $$d{"num_word"}= [[qw(primo secondo terzo quarto quinto sesto settimo ottavo nono decimo undicesimo dodicesimo tredicesimo quattordicesimo quindicesimo sedicesimo diciassettesimo diciottesimo diciannovesimo ventesimo ventunesimo ventiduesimo ventitreesimo ventiquattresimo venticinquesimo ventiseiesimo ventisettesimo ventottesimo ventinovesimo trentesimo trentunesimo)]]; $$d{"now"} =[qw(adesso)]; $$d{"today"} =[qw(oggi)]; $$d{"last"} =[qw(ultimo)]; $$d{"each"} =[qw(ogni)]; $$d{"of"} =[qw(della del)]; $$d{"at"} =[qw(alle)]; $$d{"on"} =[qw(di)]; $$d{"future"} =[qw(fra)]; $$d{"past"} =[qw(fa)]; $$d{"next"} =[qw(prossimo)]; $$d{"prev"} =[qw(ultimo)]; $$d{"later"} =[qw(dopo)]; $$d{"exact"} =[qw(esattamente)]; $$d{"approx"} =[qw(circa)]; $$d{"business"}=[qw(lavorativi lavorativo)]; $$d{"offset"} =[qw(ieri -0:0:0:1:0:0:0 domani +0:0:0:1:0:0:0)]; $$d{"times"} =[qw(mezzogiorno 12:00:00 mezzanotte 00:00:00)]; $$d{"years"} =[qw(anni anno a)]; $$d{"months"} =[qw(mesi mese mes)]; $$d{"weeks"} =[qw(settimane settimana sett)]; $$d{"days"} =[qw(giorni giorno g)]; $$d{"hours"} =[qw(ore ora h)]; $$d{"minutes"} =[qw(minuti minuto min)]; $$d{"seconds"} =[qw(secondi secondo sec)]; $$d{"replace"} =[qw(s sec m mes)]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = [qw(AM)]; $$d{"pm"} = [qw(PM)]; } sub Date_Init_French { print "DEBUG: Date_Init_French\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($e)=$h{"e'"}; my($u)=$h{"u^"}; my($a)=$h{"a'"}; $$d{"month_name"}= [["janvier","fevrier","mars","avril","mai","juin", "juillet","aout","septembre","octobre","novembre","decembre"], ["janvier","f${e}vrier","mars","avril","mai","juin", "juillet","ao${u}t","septembre","octobre","novembre","d${e}cembre"]]; $$d{"month_abb"}= [["jan","fev","mar","avr","mai","juin", "juil","aout","sept","oct","nov","dec"], ["jan","f${e}v","mar","avr","mai","juin", "juil","ao${u}t","sept","oct","nov","d${e}c"]]; $$d{"day_name"}= [["lundi","mardi","mercredi","jeudi","vendredi","samedi","dimanche"]]; $$d{"day_abb"}= [["lun","mar","mer","jeu","ven","sam","dim"]]; $$d{"day_char"}= [["l","ma","me","j","v","s","d"]]; $$d{"num_suff"}= [["1er","2e","3e","4e","5e","6e","7e","8e","9e","10e", "11e","12e","13e","14e","15e","16e","17e","18e","19e","20e", "21e","22e","23e","24e","25e","26e","27e","28e","29e","30e", "31e"]]; $$d{"num_word"}= [["premier","deux","trois","quatre","cinq","six","sept","huit","neuf", "dix","onze","douze","treize","quatorze","quinze","seize","dix-sept", "dix-huit","dix-neuf","vingt","vingt et un","vingt-deux","vingt-trois", "vingt-quatre","vingt-cinq","vingt-six","vingt-sept","vingt-huit", "vingt-neuf","trente","trente et un"], ["1re"]]; $$d{"now"} =["maintenant"]; $$d{"today"} =["aujourd'hui"]; $$d{"last"} =["dernier"]; $$d{"each"} =["chaque","tous les","toutes les"]; $$d{"of"} =["en","de"]; $$d{"at"} =["a","${a}0"]; $$d{"on"} =["sur"]; $$d{"future"} =["en"]; $$d{"past"} =["il y a"]; $$d{"next"} =["suivant"]; $$d{"prev"} =["precedent","pr${e}c${e}dent"]; $$d{"later"} =["plus tard"]; $$d{"exact"} =["exactement"]; $$d{"approx"} =["approximativement"]; $$d{"business"}=["professionel"]; $$d{"offset"} =["hier","-0:0:0:1:0:0:0","demain","+0:0:0:1:0:0:0"]; $$d{"times"} =["midi","12:00:00","minuit","00:00:00"]; $$d{"years"} =["an","annee","ans","annees","ann${e}e","ann${e}es"]; $$d{"months"} =["mois"]; $$d{"weeks"} =["sem","semaine"]; $$d{"days"} =["j","jour","jours"]; $$d{"hours"} =["h","heure","heures"]; $$d{"minutes"} =["mn","min","minute","minutes"]; $$d{"seconds"} =["s","sec","seconde","secondes"]; $$d{"replace"} =["m","mois"]; $$d{"sephm"} ='[h:]'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:,]'; $$d{"am"} = ["du matin"]; $$d{"pm"} = ["du soir"]; } sub Date_Init_Romanian { print "DEBUG: Date_Init_Romanian\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($p)=$h{"p"}; my($i)=$h{"i^"}; my($a)=$h{"a~"}; my($o)=$h{"-o"}; $$d{"month_name"}= [["ianuarie","februarie","martie","aprilie","mai","iunie", "iulie","august","septembrie","octombrie","noiembrie","decembrie"]]; $$d{"month_abb"}= [["ian","febr","mart","apr","mai","iun", "iul","aug","sept","oct","nov","dec"], ["","feb"]]; $$d{"day_name"}= [["luni","marti","miercuri","joi","vineri","simbata","duminica"], ["luni","mar${p}i","miercuri","joi","vineri","s${i}mb${a}t${a}", "duminic${a}"]]; $$d{"day_abb"}= [["lun","mar","mie","joi","vin","sim","dum"], ["lun","mar","mie","joi","vin","s${i}m","dum"]]; $$d{"day_char"}= [["L","Ma","Mi","J","V","S","D"]]; $$d{"num_suff"}= [["prima","a doua","a 3-a","a 4-a","a 5-a","a 6-a","a 7-a","a 8-a", "a 9-a","a 10-a","a 11-a","a 12-a","a 13-a","a 14-a","a 15-a", "a 16-a","a 17-a","a 18-a","a 19-a","a 20-a","a 21-a","a 22-a", "a 23-a","a 24-a","a 25-a","a 26-a","a 27-a","a 28-a","a 29-a", "a 30-a","a 31-a"]]; $$d{"num_word"}= [["prima","a doua","a treia","a patra","a cincea","a sasea","a saptea", "a opta","a noua","a zecea","a unsprezecea","a doisprezecea", "a treisprezecea","a patrusprezecea","a cincisprezecea","a saiprezecea", "a saptesprezecea","a optsprezecea","a nouasprezecea","a douazecea", "a douazecisiuna","a douazecisidoua","a douazecisitreia", "a douazecisipatra","a douazecisicincea","a douazecisisasea", "a douazecisisaptea","a douazecisiopta","a douazecisinoua","a treizecea", "a treizecisiuna"], ["prima","a doua","a treia","a patra","a cincea","a ${o}asea", "a ${o}aptea","a opta","a noua","a zecea","a unsprezecea", "a doisprezecea","a treisprezecea","a patrusprezecea","a cincisprezecea", "a ${o}aiprezecea","a ${o}aptesprezecea","a optsprezecea", "a nou${a}sprezecea","a dou${a}zecea","a dou${a}zeci${o}iuna", "a dou${a}zeci${o}idoua","a dou${a}zeci${o}itreia", "a dou${a}zeci${o}ipatra","a dou${a}zeci${o}icincea", "a dou${a}zeci${o}i${o}asea","a dou${a}zeci${o}i${o}aptea", "a dou${a}zeci${o}iopta","a dou${a}zeci${o}inoua","a treizecea", "a treizeci${o}iuna"], ["intii", "doi", "trei", "patru", "cinci", "sase", "sapte", "opt","noua","zece","unsprezece","doisprezece", "treisprezece","patrusprezece","cincisprezece","saiprezece", "saptesprezece","optsprezece","nouasprezece","douazeci", "douazecisiunu","douazecisidoi","douazecisitrei", "douazecisipatru","douazecisicinci","douazecisisase","douazecisisapte", "douazecisiopt","douazecisinoua","treizeci","treizecisiunu"], ["${i}nt${i}i", "doi", "trei", "patru", "cinci", "${o}ase", "${o}apte", "opt","nou${a}","zece","unsprezece","doisprezece", "treisprezece","patrusprezece","cincisprezece","${o}aiprezece", "${o}aptesprezece","optsprezece","nou${a}sprezece","dou${a}zeci", "dou${a}zeci${o}iunu","dou${a}zeci${o}idoi","dou${a}zeci${o}itrei", "dou${a}zecisipatru","dou${a}zeci${o}icinci","dou${a}zeci${o}i${o}ase", "dou${a}zeci${o}i${o}apte","dou${a}zeci${o}iopt", "dou${a}zeci${o}inou${a}","treizeci","treizeci${o}iunu"]]; $$d{"now"} =["acum"]; $$d{"today"} =["azi","astazi","ast${a}zi"]; $$d{"last"} =["ultima"]; $$d{"each"} =["fiecare"]; $$d{"of"} =["din","in","n"]; $$d{"at"} =["la"]; $$d{"on"} =["on"]; $$d{"future"} =["in","${i}n"]; $$d{"past"} =["in urma", "${i}n urm${a}"]; $$d{"next"} =["urmatoarea","urm${a}toarea"]; $$d{"prev"} =["precedenta","ultima"]; $$d{"later"} =["mai tirziu", "mai t${i}rziu"]; $$d{"exact"} =["exact"]; $$d{"approx"} =["aproximativ"]; $$d{"business"}=["de lucru","lucratoare","lucr${a}toare"]; $$d{"offset"} =["ieri","-0:0:0:1:0:0:0", "alaltaieri", "-0:0:0:2:0:0:0", "alalt${a}ieri","-0:0:0:2:0:0:0", "miine","+0:0:0:1:0:0:0", "m${i}ine","+0:0:0:1:0:0:0", "poimiine","+0:0:0:2:0:0:0", "poim${i}ine","+0:0:0:2:0:0:0"]; $$d{"times"} =["amiaza","12:00:00", "amiaz${a}","12:00:00", "miezul noptii","00:00:00", "miezul nop${p}ii","00:00:00"]; $$d{"years"} =["ani","an","a"]; $$d{"months"} =["luni","luna","lun${a}","l"]; $$d{"weeks"} =["saptamini","s${a}pt${a}m${i}ni","saptamina", "s${a}pt${a}m${i}na","sapt","s${a}pt"]; $$d{"days"} =["zile","zi","z"]; $$d{"hours"} =["ore", "ora", "or${a}", "h"]; $$d{"minutes"} =["minute","min","m"]; $$d{"seconds"} =["secunde","sec",]; $$d{"replace"} =["s","secunde"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:,]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } sub Date_Init_Swedish { print "DEBUG: Date_Init_Swedish\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($ao)=$h{"ao"}; my($o) =$h{"o:"}; my($a) =$h{"a:"}; $$d{"month_name"}= [["Januari","Februari","Mars","April","Maj","Juni", "Juli","Augusti","September","Oktober","November","December"]]; $$d{"month_abb"}= [["Jan","Feb","Mar","Apr","Maj","Jun", "Jul","Aug","Sep","Okt","Nov","Dec"]]; $$d{"day_name"}= [["Mandag","Tisdag","Onsdag","Torsdag","Fredag","Lordag","Sondag"], ["M${ao}ndag","Tisdag","Onsdag","Torsdag","Fredag","L${o}rdag", "S${o}ndag"]]; $$d{"day_abb"}= [["Man","Tis","Ons","Tor","Fre","Lor","Son"], ["M${ao}n","Tis","Ons","Tor","Fre","L${o}r","S${o}n"]]; $$d{"day_char"}= [["M","Ti","O","To","F","L","S"]]; $$d{"num_suff"}= [["1:a","2:a","3:e","4:e","5:e","6:e","7:e","8:e","9:e","10:e", "11:e","12:e","13:e","14:e","15:e","16:e","17:e","18:e","19:e","20:e", "21:a","22:a","23:e","24:e","25:e","26:e","27:e","28:e","29:e","30:e", "31:a"]]; $$d{"num_word"}= [["forsta","andra","tredje","fjarde","femte","sjatte","sjunde", "attonde","nionde","tionde","elfte","tolfte","trettonde","fjortonde", "femtonde","sextonde","sjuttonde","artonde","nittonde","tjugonde", "tjugoforsta","tjugoandra","tjugotredje","tjugofjarde","tjugofemte", "tjugosjatte","tjugosjunde","tjugoattonde","tjugonionde", "trettionde","trettioforsta"], ["f${o}rsta","andra","tredje","fj${a}rde","femte","sj${a}tte","sjunde", "${ao}ttonde","nionde","tionde","elfte","tolfte","trettonde","fjortonde", "femtonde","sextonde","sjuttonde","artonde","nittonde","tjugonde", "tjugof${o}rsta","tjugoandra","tjugotredje","tjugofj${a}rde","tjugofemte", "tjugosj${a}tte","tjugosjunde","tjugo${ao}ttonde","tjugonionde", "trettionde","trettiof${o}rsta"]]; $$d{"now"} =["nu"]; $$d{"today"} =["idag"]; $$d{"last"} =["forra","f${o}rra","senaste"]; $$d{"each"} =["varje"]; $$d{"of"} =["om"]; $$d{"at"} =["kl","kl.","klockan"]; $$d{"on"} =["pa","p${ao}"]; $$d{"future"} =["om"]; $$d{"past"} =["sedan"]; $$d{"next"} =["nasta","n${a}sta"]; $$d{"prev"} =["forra","f${o}rra"]; $$d{"later"} =["senare"]; $$d{"exact"} =["exakt"]; $$d{"approx"} =["ungefar","ungef${a}r"]; $$d{"business"}=["arbetsdag","arbetsdagar"]; $$d{"offset"} =["ig${ao}r","-0:0:0:1:0:0:0","igar","-0:0:0:1:0:0:0", "imorgon","+0:0:0:1:0:0:0"]; $$d{"times"} =["mitt pa dagen","12:00:00","mitt p${ao} dagen","12:00:00", "midnatt","00:00:00"]; $$d{"years"} =["ar","${ao}r"]; $$d{"months"} =["man","manad","manader","m${ao}n","m${ao}nad","m${ao}nader"]; $$d{"weeks"} =["v","vecka","veckor"]; $$d{"days"} =["d","dag","dagar"]; $$d{"hours"} =["t","tim","timme","timmar"]; $$d{"minutes"} =["min","minut","minuter"]; $$d{"seconds"} =["s","sek","sekund","sekunder"]; $$d{"replace"} =["m","minut"]; $$d{"sephm"} ='[.:]'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["FM"]; $$d{"pm"} = ["EM"]; } sub Date_Init_German { print "DEBUG: Date_Init_German\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($a)=$h{"a:"}; my($u)=$h{"u:"}; my($o)=$h{"o:"}; my($b)=$h{"beta"}; $$d{"month_name"}= [["Januar","Februar","Maerz","April","Mai","Juni", "Juli","August","September","Oktober","November","Dezember"], ["J${a}nner","Februar","M${a}rz","April","Mai","Juni", "Juli","August","September","Oktober","November","Dezember"]]; $$d{"month_abb"}= [["Jan","Feb","Mar","Apr","Mai","Jun", "Jul","Aug","Sep","Okt","Nov","Dez"], ["J${a}n","Feb","M${a}r","Apr","Mai","Jun", "Jul","Aug","Sep","Okt","Nov","Dez"]]; $$d{"day_name"}= [["Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag", "Sonntag"]]; $$d{"day_abb"}= [["Mo","Di","Mi","Do","Fr","Sa","So"]]; $$d{"day_char"}= [["M","Di","Mi","Do","F","Sa","So"]]; $$d{"num_suff"}= [["1.","2.","3.","4.","5.","6.","7.","8.","9.","10.", "11.","12.","13.","14.","15.","16.","17.","18.","19.","20.", "21.","22.","23.","24.","25.","26.","27.","28.","29.","30.", "31."]]; $$d{"num_word"}= [ ["erste","zweite","dritte","vierte","funfte","sechste","siebente", "achte","neunte","zehnte","elfte","zwolfte","dreizehnte","vierzehnte", "funfzehnte","sechzehnte","siebzehnte","achtzehnte","neunzehnte", "zwanzigste","einundzwanzigste","zweiundzwanzigste","dreiundzwanzigste", "vierundzwanzigste","funfundzwanzigste","sechundzwanzigste", "siebundzwanzigste","achtundzwanzigste","neunundzwanzigste", "dreibigste","einunddreibigste"], ["erste","zweite","dritte","vierte","f${u}nfte","sechste","siebente", "achte","neunte","zehnte","elfte","zw${o}lfte","dreizehnte", "vierzehnte","f${u}nfzehnte","sechzehnte","siebzehnte","achtzehnte", "neunzehnte","zwanzigste","einundzwanzigste","zweiundzwanzigste", "dreiundzwanzigste","vierundzwanzigste","f${u}nfundzwanzigste", "sechundzwanzigste","siebundzwanzigste","achtundzwanzigste", "neunundzwanzigste","drei${b}igste","einunddrei${b}igste"], ["erster"]]; $$d{"now"} =["jetzt"]; $$d{"today"} =["heute"]; $$d{"last"} =["letzte","letzten"]; $$d{"each"} =["jeden"]; $$d{"of"} =["der","im","des"]; $$d{"at"} =["um"]; $$d{"on"} =["am"]; $$d{"future"} =["in"]; $$d{"past"} =["vor"]; $$d{"next"} =["nachste","n${a}chste","nachsten","n${a}chsten"]; $$d{"prev"} =["vorherigen","vorherige","letzte","letzten"]; $$d{"later"} =["spater","sp${a}ter"]; $$d{"exact"} =["genau"]; $$d{"approx"} =["ungefahr","ungef${a}hr"]; $$d{"business"}=["Arbeitstag"]; $$d{"offset"} =["gestern","-0:0:0:1:0:0:0","morgen","+0:0:0:1:0:0:0"]; $$d{"times"} =["mittag","12:00:00","mitternacht","00:00:00"]; $$d{"years"} =["j","Jahr","Jahre"]; $$d{"months"} =["Monat","Monate"]; $$d{"weeks"} =["w","Woche","Wochen"]; $$d{"days"} =["t","Tag","Tage"]; $$d{"hours"} =["h","std","Stunde","Stunden"]; $$d{"minutes"} =["min","Minute","Minuten"]; $$d{"seconds"} =["s","sek","Sekunde","Sekunden"]; $$d{"replace"} =["m","Monat"]; $$d{"sephm"} =':'; $$d{"sepms"} ='[: ]'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["FM"]; $$d{"pm"} = ["EM"]; } sub Date_Init_Dutch { print "DEBUG: Date_Init_Dutch\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); $$d{"month_name"}= [["januari","februari","maart","april","mei","juni","juli","augustus", "september","october","november","december"], ["","","","","","","","","","oktober"]]; $$d{"month_abb"}= [["jan","feb","maa","apr","mei","jun","jul", "aug","sep","oct","nov","dec"], ["","","mrt","","","","","","","okt"]]; $$d{"day_name"}= [["maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag", "zondag"]]; $$d{"day_abb"}= [["ma","di","wo","do","vr","zat","zon"], ["","","","","","za","zo"]]; $$d{"day_char"}= [["M","D","W","D","V","Za","Zo"]]; $$d{"num_suff"}= [["1ste","2de","3de","4de","5de","6de","7de","8ste","9de","10de", "11de","12de","13de","14de","15de","16de","17de","18de","19de","20ste", "21ste","22ste","23ste","24ste","25ste","26ste","27ste","28ste","29ste", "30ste","31ste"]]; $$d{"num_word"}= [["eerste","tweede","derde","vierde","vijfde","zesde","zevende","achtste", "negende","tiende","elfde","twaalfde", map {"${_}tiende";} qw (der veer vijf zes zeven acht negen), "twintigste", map {"${_}entwintigste";} qw (een twee drie vier vijf zes zeven acht negen), "dertigste","eenendertigste"], ["","","","","","","","","","","","","","","","","","","","", map {"${_}-en-twintigste";} qw (een twee drie vier vijf zes zeven acht negen), "dertigste","een-en-dertigste"], ["een","twee","drie","vier","vijf","zes","zeven","acht","negen","tien", "elf","twaalf", map {"${_}tien"} qw (der veer vijf zes zeven acht negen), "twintig", map {"${_}entwintig"} qw (een twee drie vier vijf zes zeven acht negen), "dertig","eenendertig"], ["","","","","","","","","","","","","","","","","","","","", map {"${_}-en-twintig"} qw (een twee drie vier vijf zes zeven acht negen), "dertig","een-en-dertig"]]; $$d{"now"} =["nu","nou"]; $$d{"today"} =["vandaag"]; $$d{"last"} =["laatste"]; $$d{"each"} =["elke","elk"]; $$d{"of"} =["in","van"]; $$d{"at"} =["om"]; $$d{"on"} =["op"]; $$d{"future"} =["over"]; $$d{"past"} =["geleden","vroeger","eerder"]; $$d{"next"} =["volgende","volgend"]; $$d{"prev"} =["voorgaande","voorgaand"]; $$d{"later"} =["later"]; $$d{"exact"} =["exact","precies","nauwkeurig"]; $$d{"approx"} =["ongeveer","ong",'ong\.',"circa","ca",'ca\.']; $$d{"business"}=["werk","zakelijke","zakelijk"]; $$d{"offset"} =["morgen","+0:0:0:1:0:0:0","overmorgen","+0:0:0:2:0:0:0", "gisteren","-0:0:0:1:0:0:0","eergisteren","-0::00:2:0:0:0"]; $$d{"times"} =["noen","12:00:00","middernacht","00:00:00"]; $$d{"years"} =["jaar","jaren","ja","j"]; $$d{"months"} =["maand","maanden","mnd"]; $$d{"weeks"} =["week","weken","w"]; $$d{"days"} =["dag","dagen","d"]; $$d{"hours"} =["uur","uren","u","h"]; $$d{"minutes"} =["minuut","minuten","min"]; $$d{"seconds"} =["seconde","seconden","sec","s"]; $$d{"replace"} =["m","minuten"]; $$d{"sephm"} ='[:.uh]'; $$d{"sepms"} ='[:.m]'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["am","a.m.","vm","v.m.","voormiddag","'s_ochtends", "ochtend","'s_nachts","nacht"]; $$d{"pm"} = ["pm","p.m.","nm","n.m.","namiddag","'s_middags","middag", "'s_avonds","avond"]; } sub Date_Init_Polish { print "DEBUG: Date_Init_Polish\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; $$d{"month_name"}= [["stycznia","luty","marca","kwietnia","maja","czerwca", "lipca","sierpnia","wrzesnia","pazdziernika","listopada","grudnia"], ["stycznia","luty","marca","kwietnia","maja","czerwca","lipca", "sierpnia","wrze\x9cnia","pa\x9fdziernika","listopada","grudnia"]]; $$d{"month_abb"}= [["sty.","lut.","mar.","kwi.","maj","cze.", "lip.","sie.","wrz.","paz.","lis.","gru."], ["sty.","lut.","mar.","kwi.","maj","cze.", "lip.","sie.","wrz.","pa\x9f.","lis.","gru."]]; $$d{"day_name"}= [["poniedzialek","wtorek","sroda","czwartek","piatek","sobota", "niedziela"], ["poniedzia\x81\xb3ek","wtorek","\x9croda","czwartek","pi\x81\xb9tek", "sobota","niedziela"]]; $$d{"day_abb"}= [["po.","wt.","sr.","cz.","pi.","so.","ni."], ["po.","wt.","\x9cr.","cz.","pi.","so.","ni."]]; $$d{"day_char"}= [["p","w","e","c","p","s","n"], ["p","w","\x9c.","c","p","s","n"]]; $$d{"num_suff"}= [["1.","2.","3.","4.","5.","6.","7.","8.","9.","10.", "11.","12.","13.","14.","15.","16.","17.","18.","19.","20.", "21.","22.","23.","24.","25.","26.","27.","28.","29.","30.", "31."]]; $$d{"num_word"}= [["pierwszego","drugiego","trzeczego","czwartego","piatego","szostego", "siodmego","osmego","dziewiatego","dziesiatego", "jedenastego","dwunastego","trzynastego","czternastego","pietnastego", "szestnastego","siedemnastego","osiemnastego","dziewietnastego", "dwudziestego", "dwudziestego pierwszego","dwudziestego drugiego", "dwudziestego trzeczego","dwudziestego czwartego", "dwudziestego piatego","dwudziestego szostego", "dwudziestego siodmego","dwudziestego osmego", "dwudziestego dziewiatego","trzydziestego","trzydziestego pierwszego"], ["pierwszego","drugiego","trzeczego","czwartego","pi\x81\xb9tego", "sz\x81\xf3stego","si\x81\xf3dmego","\x81\xf3smego","dziewi\x81\xb9tego", "dziesi\x81\xb9tego","jedenastego","dwunastego","trzynastego", "czternastego","pi\x81\xeatnastego","szestnastego","siedemnastego", "osiemnastego","dziewietnastego","dwudziestego", "dwudziestego pierwszego","dwudziestego drugiego", "dwudziestego trzeczego","dwudziestego czwartego", "dwudziestego pi\x81\xb9tego","dwudziestego sz\x81\xf3stego", "dwudziestego si\x81\xf3dmego","dwudziestego \x81\xf3smego", "dwudziestego dziewi\x81\xb9tego","trzydziestego", "trzydziestego pierwszego"]]; $$d{"now"} =["teraz"]; $$d{"today"} =["dzisaj"]; $$d{"last"} =["ostatni","ostatna"]; $$d{"each"} =["kazdy","ka\x81\xbfdy", "kazdym","ka\x81\xbfdym"]; $$d{"of"} =["w","z"]; $$d{"at"} =["o","u"]; $$d{"on"} =["na"]; $$d{"future"} =["za"]; $$d{"past"} =["temu"]; $$d{"next"} =["nastepny","nast\x81\xeapny","nastepnym","nast\x81\xeapnym", "przyszly","przysz\x81\xb3y","przyszlym", "przysz\x81\xb3ym"]; $$d{"prev"} =["zeszly","zesz\x81\xb3y","zeszlym","zesz\x81\xb3ym"]; $$d{"later"} =["later"]; $$d{"exact"} =["doklandnie","dok\x81\xb3andnie"]; $$d{"approx"} =["w przyblizeniu","w przybli\x81\xbfeniu","mniej wiecej", "mniej wi\x81\xeacej","okolo","oko\x81\xb3o"]; $$d{"business"}=["sluzbowy","s\x81\xb3u\x81\xbfbowy","sluzbowym", "s\x81\xb3u\x81\xbfbowym"]; $$d{"times"} =["po\x81\xb3udnie","12:00:00", "p\x81\xf3\x81\xb3noc","00:00:00", "poludnie","12:00:00","polnoc","00:00:00"]; $$d{"offset"} =["wczoraj","-0:0:1:0:0:0","jutro","+0:0:1:0:0:0"]; $$d{"years"} =["rok","lat","lata","latach"]; $$d{"months"} =["m.","miesiac","miesi\x81\xb9c","miesiecy", "miesi\x81\xeacy","miesiacu","miesi\x81\xb9cu"]; $$d{"weeks"} =["ty.","tydzien","tydzie\x81\xf1","tygodniu"]; $$d{"days"} =["d.","dzien","dzie\x81\xf1","dni"]; $$d{"hours"} =["g.","godzina","godziny","godzinie"]; $$d{"minutes"} =["mn.","min.","minut","minuty"]; $$d{"seconds"} =["s.","sekund","sekundy"]; $$d{"replace"} =["m.","miesiac"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } sub Date_Init_Spanish { print "DEBUG: Date_Init_Spanish\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); $$d{"month_name"}= [["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto", "Septiembre","Octubre","Noviembre","Diciembre"]]; $$d{"month_abb"}= [["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct", "Nov","Dic"]]; $$d{"day_name"}= [["Lunes","Martes","Miercoles","Jueves","Viernes","Sabado","Domingo"]]; $$d{"day_abb"}= [["Lun","Mar","Mie","Jue","Vie","Sab","Dom"]]; $$d{"day_char"}= [["L","Ma","Mi","J","V","S","D"]]; $$d{"num_suff"}= [["1o","2o","3o","4o","5o","6o","7o","8o","9o","10o", "11o","12o","13o","14o","15o","16o","17o","18o","19o","20o", "21o","22o","23o","24o","25o","26o","27o","28o","29o","30o","31o"], ["1a","2a","3a","4a","5a","6a","7a","8a","9a","10a", "11a","12a","13a","14a","15a","16a","17a","18a","19a","20a", "21a","22a","23a","24a","25a","26a","27a","28a","29a","30a","31a"]]; $$d{"num_word"}= [["Primero","Segundo","Tercero","Cuarto","Quinto","Sexto","Septimo", "Octavo","Noveno","Decimo","Decimo Primero","Decimo Segundo", "Decimo Tercero","Decimo Cuarto","Decimo Quinto","Decimo Sexto", "Decimo Septimo","Decimo Octavo","Decimo Noveno","Vigesimo", "Vigesimo Primero","Vigesimo Segundo","Vigesimo Tercero", "Vigesimo Cuarto","Vigesimo Quinto","Vigesimo Sexto", "Vigesimo Septimo","Vigesimo Octavo","Vigesimo Noveno","Trigesimo", "Trigesimo Primero"], ["Primera","Segunda","Tercera","Cuarta","Quinta","Sexta","Septima", "Octava","Novena","Decima","Decimo Primera","Decimo Segunda", "Decimo Tercera","Decimo Cuarta","Decimo Quinta","Decimo Sexta", "Decimo Septima","Decimo Octava","Decimo Novena","Vigesima", "Vigesimo Primera","Vigesimo Segunda","Vigesimo Tercera", "Vigesimo Cuarta","Vigesimo Quinta","Vigesimo Sexta", "Vigesimo Septima","Vigesimo Octava","Vigesimo Novena","Trigesima", "Trigesimo Primera"]]; $$d{"now"} =["Ahora"]; $$d{"today"} =["Hoy"]; $$d{"last"} =["ultimo"]; $$d{"each"} =["cada"]; $$d{"of"} =["en","de"]; $$d{"at"} =["a"]; $$d{"on"} =["el"]; $$d{"future"} =["en"]; $$d{"past"} =["hace"]; $$d{"next"} =["siguiente"]; $$d{"prev"} =["anterior"]; $$d{"later"} =["later"]; $$d{"exact"} =["exactamente"]; $$d{"approx"} =["aproximadamente"]; $$d{"business"}=["laborales"]; $$d{"offset"} =["ayer","-0:0:0:1:0:0:0","manana","+0:0:0:1:0:0:0"]; $$d{"times"} =["mediodia","12:00:00","medianoche","00:00:00"]; $$d{"years"} =["a","ano","ano","anos","anos"]; $$d{"months"} =["m","mes","mes","meses"]; $$d{"weeks"} =["sem","semana","semana","semanas"]; $$d{"days"} =["d","dia","dias"]; $$d{"hours"} =["hr","hrs","hora","horas"]; $$d{"minutes"} =["min","min","minuto","minutos"]; $$d{"seconds"} =["s","seg","segundo","segundos"]; $$d{"replace"} =["m","mes"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } sub Date_Init_Portuguese { print "DEBUG: Date_Init_Portuguese\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($o) = $h{"-o"}; my($c) = $h{",c"}; my($a) = $h{"a'"}; my($e) = $h{"e'"}; my($u) = $h{"u'"}; my($o2)= $h{"o'"}; my($a2)= $h{"a`"}; my($a3)= $h{"a~"}; my($e2)= $h{"e^"}; $$d{"month_name"}= [["Janeiro","Fevereiro","Marco","Abril","Maio","Junho", "Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"], ["Janeiro","Fevereiro","Mar${c}o","Abril","Maio","Junho", "Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"]]; $$d{"month_abb"}= [["Jan","Fev","Mar","Abr","Mai","Jun", "Jul","Ago","Set","Out","Nov","Dez"]]; $$d{"day_name"}= [["Segunda","Terca","Quarta","Quinta","Sexta","Sabado","Domingo"], ["Segunda","Ter${c}a","Quarta","Quinta","Sexta","S${a}bado","Domingo"]]; $$d{"day_abb"}= [["Seg","Ter","Qua","Qui","Sex","Sab","Dom"], ["Seg","Ter","Qua","Qui","Sex","S${a}b","Dom"]]; $$d{"day_char"}= [["Sg","T","Qa","Qi","Sx","Sb","D"]]; $$d{"num_suff"}= [["1${o}","2${o}","3${o}","4${o}","5${o}","6${o}","7${o}","8${o}", "9${o}","10${o}","11${o}","12${o}","13${o}","14${o}","15${o}", "16${o}","17${o}","18${o}","19${o}","20${o}","21${o}","22${o}", "23${o}","24${o}","25${o}","26${o}","27${o}","28${o}","29${o}", "30${o}","31${o}"]]; $$d{"num_word"}= [["primeiro","segundo","terceiro","quarto","quinto","sexto","setimo", "oitavo","nono","decimo","decimo primeiro","decimo segundo", "decimo terceiro","decimo quarto","decimo quinto","decimo sexto", "decimo setimo","decimo oitavo","decimo nono","vigesimo", "vigesimo primeiro","vigesimo segundo","vigesimo terceiro", "vigesimo quarto","vigesimo quinto","vigesimo sexto","vigesimo setimo", "vigesimo oitavo","vigesimo nono","trigesimo","trigesimo primeiro"], ["primeiro","segundo","terceiro","quarto","quinto","sexto","s${e}timo", "oitavo","nono","d${e}cimo","d${e}cimo primeiro","d${e}cimo segundo", "d${e}cimo terceiro","d${e}cimo quarto","d${e}cimo quinto", "d${e}cimo sexto","d${e}cimo s${e}timo","d${e}cimo oitavo", "d${e}cimo nono","vig${e}simo","vig${e}simo primeiro", "vig${e}simo segundo","vig${e}simo terceiro","vig${e}simo quarto", "vig${e}simo quinto","vig${e}simo sexto","vig${e}simo s${e}timo", "vig${e}simo oitavo","vig${e}simo nono","trig${e}simo", "trig${e}simo primeiro"]]; $$d{"now"} =["agora"]; $$d{"today"} =["hoje"]; $$d{"last"} =["${u}ltimo","ultimo"]; $$d{"each"} =["cada"]; $$d{"of"} =["da","do"]; $$d{"at"} =["as","${a2}s"]; $$d{"on"} =["na","no"]; $$d{"future"} =["em"]; $$d{"past"} =["a","${a2}"]; $$d{"next"} =["proxima","proximo","pr${o2}xima","pr${o2}ximo"]; $$d{"prev"} =["ultima","ultimo","${u}ltima","${u}ltimo"]; $$d{"later"} =["passadas","passados"]; $$d{"exact"} =["exactamente"]; $$d{"approx"} =["aproximadamente"]; $$d{"business"}=["util","uteis"]; $$d{"offset"} =["ontem","-0:0:0:1:0:0:0", "amanha","+0:0:0:1:0:0:0","amanh${a3}","+0:0:0:1:0:0:0"]; $$d{"times"} =["meio-dia","12:00:00","meia-noite","00:00:00"]; $$d{"years"} =["anos","ano","ans","an","a"]; $$d{"months"} =["meses","m${e2}s","mes","m"]; $$d{"weeks"} =["semanas","semana","sem","sems","s"]; $$d{"days"} =["dias","dia","d"]; $$d{"hours"} =["horas","hora","hr","hrs"]; $$d{"minutes"} =["minutos","minuto","min","mn"]; $$d{"seconds"} =["segundos","segundo","seg","sg"]; $$d{"replace"} =["m","mes","s","sems"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[,]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } sub Date_Init_Russian { print "DEBUG: Date_Init_Russian\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; my(%h)=(); &Char_8Bit(\%h); my($a) =$h{"a:"}; $$d{"month_name"}= [ ["\xd1\xce\xd7\xc1\xd2\xd1","\xc6\xc5\xd7\xd2\xc1\xcc\xd1", "\xcd\xc1\xd2\xd4\xc1","\xc1\xd0\xd2\xc5\xcc\xd1","\xcd\xc1\xd1", "\xc9\xc0\xce\xd1", "\xc9\xc0\xcc\xd1","\xc1\xd7\xc7\xd5\xd3\xd4\xc1", "\xd3\xc5\xce\xd4\xd1\xc2\xd2\xd1","\xcf\xcb\xd4\xd1\xc2\xd2\xd1", "\xce\xcf\xd1\xc2\xd2\xd1","\xc4\xc5\xcb\xc1\xc2\xd2\xd1"], ["\xd1\xce\xd7\xc1\xd2\xd8","\xc6\xc5\xd7\xd2\xc1\xcc\xd8", "\xcd\xc1\xd2\xd4","\xc1\xd0\xd2\xc5\xcc\xd8","\xcd\xc1\xca", "\xc9\xc0\xce\xd8", "\xc9\xc0\xcc\xd8","\xc1\xd7\xc7\xd5\xd3\xd4", "\xd3\xc5\xce\xd4\xd1\xc2\xd2\xd8","\xcf\xcb\xd4\xd1\xc2\xd2\xd8", "\xce\xcf\xd1\xc2\xd2\xd8","\xc4\xc5\xcb\xc1\xc2\xd2\xd8"] ]; $$d{"month_abb"}= [["\xd1\xce\xd7","\xc6\xc5\xd7","\xcd\xd2\xd4","\xc1\xd0\xd2", "\xcd\xc1\xca","\xc9\xc0\xce", "\xc9\xc0\xcc","\xc1\xd7\xc7","\xd3\xce\xd4","\xcf\xcb\xd4", "\xce\xcf\xd1\xc2","\xc4\xc5\xcb"], ["","\xc6\xd7\xd2","","","\xcd\xc1\xd1","", "","","\xd3\xc5\xce","\xcf\xcb\xd4","\xce\xcf\xd1",""]]; $$d{"day_name"}= [["\xd0\xcf\xce\xc5\xc4\xc5\xcc\xd8\xce\xc9\xcb", "\xd7\xd4\xcf\xd2\xce\xc9\xcb","\xd3\xd2\xc5\xc4\xc1", "\xde\xc5\xd4\xd7\xc5\xd2\xc7","\xd0\xd1\xd4\xce\xc9\xc3\xc1", "\xd3\xd5\xc2\xc2\xcf\xd4\xc1", "\xd7\xcf\xd3\xcb\xd2\xc5\xd3\xc5\xce\xd8\xc5"]]; $$d{"day_abb"}= [["\xd0\xce\xc4","\xd7\xd4\xd2","\xd3\xd2\xc4","\xde\xd4\xd7", "\xd0\xd4\xce","\xd3\xd5\xc2","\xd7\xd3\xcb"], ["\xd0\xcf\xce","\xd7\xd4\xcf","\xd3\xd2e","\xde\xc5\xd4", "\xd0\xd1\xd4","\xd3\xd5\xc2","\xd7\xcf\xd3\xcb"]]; $$d{"day_char"}= [["\xd0\xce","\xd7\xd4","\xd3\xd2","\xde\xd4","\xd0\xd4","\xd3\xc2", "\xd7\xd3"]]; $$d{"num_suff"}= [["1 ","2 ","3 ","4 ","5 ","6 ","7 ","8 ","9 ","10 ", "11 ","12 ","13 ","14 ","15 ","16 ","17 ","18 ","19 ","20 ", "21 ","22 ","23 ","24 ","25 ","26 ","27 ","28 ","29 ","30 ", "31 "]]; $$d{"num_word"}= [["\xd0\xc5\xd2\xd7\xd9\xca","\xd7\xd4\xcf\xd2\xcf\xca", "\xd4\xd2\xc5\xd4\xc9\xca","\xde\xc5\xd4\xd7\xc5\xd2\xd4\xd9\xca", "\xd0\xd1\xd4\xd9\xca","\xdb\xc5\xd3\xd4\xcf\xca", "\xd3\xc5\xc4\xd8\xcd\xcf\xca","\xd7\xcf\xd3\xd8\xcd\xcf\xca", "\xc4\xc5\xd7\xd1\xd4\xd9\xca","\xc4\xc5\xd3\xd1\xd4\xd9\xca", "\xcf\xc4\xc9\xce\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xc4\xd7\xc5\xce\xc1\xc4\xde\xc1\xd4\xd9\xca", "\xd4\xd2\xc5\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xde\xc5\xd4\xd9\xd2\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xd0\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xdb\xc5\xd3\xd4\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xd7\xcf\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xc4\xc5\xd7\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xd9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xd4\xcf\xd2\xcf\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd4\xd2\xc5\xd4\xc9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xde\xc5\xd4\xd7\xc5\xd2\xd4\xd9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xd1\xd4\xd9\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xdb\xc5\xd3\xd4\xcf\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd3\xc5\xc4\xd8\xcd\xcf\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xcf\xd3\xd8\xcd\xcf\xca", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xc4\xc5\xd7\xd1\xd4\xd9\xca", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xd9\xca", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xd9\xca"], ["\xd0\xc5\xd2\xd7\xcf\xc5","\xd7\xd4\xcf\xd2\xcf\xc5", "\xd4\xd2\xc5\xd4\xd8\xc5","\xde\xc5\xd4\xd7\xc5\xd2\xd4\xcf\xc5", "\xd0\xd1\xd4\xcf\xc5","\xdb\xc5\xd3\xd4\xcf\xc5", "\xd3\xc5\xc4\xd8\xcd\xcf\xc5","\xd7\xcf\xd3\xd8\xcd\xcf\xc5", "\xc4\xc5\xd7\xd1\xd4\xcf\xc5","\xc4\xc5\xd3\xd1\xd4\xcf\xc5", "\xcf\xc4\xc9\xce\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xc4\xd7\xc5\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xd4\xd2\xc5\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xde\xc5\xd4\xd9\xd2\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xd0\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xdb\xc5\xd3\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xd7\xcf\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xc4\xc5\xd7\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xd4\xcf\xd2\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd4\xd2\xc5\xd4\xd8\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xde\xc5\xd4\xd7\xc5\xd2\xd4\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xd1\xd4\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xdb\xc5\xd3\xd4\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd3\xc5\xc4\xd8\xcd\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xcf\xd3\xd8\xcd\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xc4\xc5\xd7\xd1\xd4\xcf\xc5", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xcf\xc5", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xcf\xc5"], ["\xd0\xc5\xd2\xd7\xcf\xc7\xcf","\xd7\xd4\xcf\xd2\xcf\xc7\xcf", "\xd4\xd2\xc5\xd4\xd8\xc5\xc7\xcf", "\xde\xc5\xd4\xd7\xc5\xd2\xd4\xcf\xc7\xcf","\xd0\xd1\xd4\xcf\xc7\xcf", "\xdb\xc5\xd3\xd4\xcf\xc7\xcf","\xd3\xc5\xc4\xd8\xcd\xcf\xc7\xcf", "\xd7\xcf\xd3\xd8\xcd\xcf\xc7\xcf", "\xc4\xc5\xd7\xd1\xd4\xcf\xc7\xcf","\xc4\xc5\xd3\xd1\xd4\xcf\xc7\xcf", "\xcf\xc4\xc9\xce\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xc4\xd7\xc5\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xd4\xd2\xc5\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xde\xc5\xd4\xd9\xd2\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xd0\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xdb\xc5\xd3\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xd7\xcf\xd3\xc5\xcd\xd8\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xc4\xc5\xd7\xd1\xd4\xce\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xd4\xcf\xd2\xcf\xc5", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd4\xd2\xc5\xd4\xd8\xc5\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xde\xc5\xd4\xd7\xc5\xd2\xd4\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd0\xd1\xd4\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xdb\xc5\xd3\xd4\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd3\xc5\xc4\xd8\xcd\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xd7\xcf\xd3\xd8\xcd\xcf\xc7\xcf", "\xc4\xd7\xc1\xc4\xc3\xc1\xd4\xd8 \xc4\xc5\xd7\xd1\xd4\xcf\xc7\xcf", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xcf\xc7\xcf", "\xd4\xd2\xc9\xc4\xc3\xc1\xd4\xd8 \xd0\xc5\xd2\xd7\xcf\xc7\xcf"]]; $$d{"now"} =["\xd3\xc5\xca\xde\xc1\xd3"]; $$d{"today"} =["\xd3\xc5\xc7\xcf\xc4\xce\xd1"]; $$d{"last"} =["\xd0\xcf\xd3\xcc\xc5\xc4\xce\xc9\xca"]; $$d{"each"} =["\xcb\xc1\xd6\xc4\xd9\xca"]; $$d{"of"} =[" "]; $$d{"at"} =["\xd7"]; $$d{"on"} =["\xd7"]; $$d{"future"} =["\xd7\xd0\xc5\xd2\xc5\xc4 \xce\xc1"]; $$d{"past"} =["\xce\xc1\xda\xc1\xc4 \xce\xc1 "]; $$d{"next"} =["\xd3\xcc\xc5\xc4\xd5\xc0\xdd\xc9\xca"]; $$d{"prev"} =["\xd0\xd2\xc5\xc4\xd9\xc4\xd5\xdd\xc9\xca"]; $$d{"later"} =["\xd0\xcf\xda\xd6\xc5"]; $$d{"exact"} =["\xd4\xcf\xde\xce\xcf"]; $$d{"approx"} =["\xd0\xd2\xc9\xcd\xc5\xd2\xce\xcf"]; $$d{"business"}=["\xd2\xc1\xc2\xcf\xde\xc9\xc8"]; $$d{"offset"} =["\xd0\xcf\xda\xc1\xd7\xde\xc5\xd2\xc1","-0:0:0:2:0:0:0", "\xd7\xde\xc5\xd2\xc1","-0:0:0:1:0:0:0", "\xda\xc1\xd7\xd4\xd2\xc1","+0:0:0:1:0:0:0", "\xd0\xcf\xd3\xcc\xc5\xda\xc1\xd7\xd4\xd2\xc1", "+0:0:0:2:0:0:0"]; $$d{"times"} =["\xd0\xcf\xcc\xc4\xc5\xce\xd8","12:00:00", "\xd0\xcf\xcc\xce\xcf\xde\xd8","00:00:00"]; $$d{"years"} =["\xc7","\xc7\xc4","\xc7\xcf\xc4","\xcc\xc5\xd4", "\xcc\xc5\xd4","\xc7\xcf\xc4\xc1"]; $$d{"months"} =["\xcd\xc5\xd3","\xcd\xc5\xd3\xd1\xc3", "\xcd\xc5\xd3\xd1\xc3\xc5\xd7"]; $$d{"weeks"} =["\xce\xc5\xc4\xc5\xcc\xd1","\xce\xc5\xc4\xc5\xcc\xd8", "\xce\xc5\xc4\xc5\xcc\xc9","\xce\xc5\xc4\xc5\xcc\xc0"]; $$d{"days"} =["\xc4","\xc4\xc5\xce\xd8","\xc4\xce\xc5\xca", "\xc4\xce\xd1"]; $$d{"hours"} =["\xde","\xde.","\xde\xd3","\xde\xd3\xd7","\xde\xc1\xd3", "\xde\xc1\xd3\xcf\xd7","\xde\xc1\xd3\xc1"]; $$d{"minutes"} =["\xcd\xce","\xcd\xc9\xce","\xcd\xc9\xce\xd5\xd4\xc1", "\xcd\xc9\xce\xd5\xd4"]; $$d{"seconds"} =["\xd3","\xd3\xc5\xcb","\xd3\xc5\xcb\xd5\xce\xc4\xc1", "\xd3\xc5\xcb\xd5\xce\xc4"]; $$d{"replace"} =[]; $$d{"sephm"} ="[:\xde]"; $$d{"sepms"} ="[:\xcd]"; $$d{"sepss"} ="[:.\xd3]"; $$d{"am"} = ["\xc4\xd0","${a}\xf0","${a}.\xf0.","\xce\xcf\xde\xc9", "\xd5\xd4\xd2\xc1", "\xc4\xcf \xd0\xcf\xcc\xd5\xc4\xce\xd1"]; $$d{"pm"} = ["\xd0\xd0","\xf0\xf0","\xf0.\xf0.","\xc4\xce\xd1", "\xd7\xc5\xde\xc5\xd2\xc1", "\xd0\xcf\xd3\xcc\xc5 \xd0\xcf\xcc\xd5\xc4\xce\xd1", "\xd0\xcf \xd0\xcf\xcc\xd5\xc4\xce\xc0"]; } sub Date_Init_Turkish { print "DEBUG: Date_Init_Turkish\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; $$d{"month_name"}= [ ["ocak","subat","mart","nisan","mayis","haziran", "temmuz","agustos","eylul","ekim","kasim","aralik"], ["ocak","\xfeubat","mart","nisan","may\xfds","haziran", "temmuz","a\xf0ustos","eyl\xfcl","ekim","kas\xfdm","aral\xfdk"] ]; $$d{"month_abb"}= [ ["oca","sub","mar","nis","may","haz", "tem","agu","eyl","eki","kas","ara"], ["oca","\xfeub","mar","nis","may","haz", "tem","a\xf0u","eyl","eki","kas","ara"] ]; $$d{"day_name"}= [ ["pazartesi","sali","carsamba","persembe","cuma","cumartesi","pazar"], ["pazartesi","sal\xfd","\xe7ar\xfeamba","per\xfeembe","cuma", "cumartesi","pazar"], ]; $$d{"day_abb"}= [ ["pzt","sal","car","per","cum","cts","paz"], ["pzt","sal","\xe7ar","per","cum","cts","paz"], ]; $$d{"day_char"}= [["Pt","S","Cr","Pr","C","Ct","P"], ["Pt","S","\xc7","Pr","C","Ct","P"]]; $$d{"num_suff"}= [[ "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10.", "11.", "12.", "13.", "14.", "15.", "16.", "17.", "18.", "19.", "20.", "21.", "22.", "23.", "24.", "25.", "26.", "27.", "28.", "29.", "30.", "31."]]; $$d{"num_word"}= [ ["birinci","ikinci","ucuncu","dorduncu", "besinci","altinci","yedinci","sekizinci", "dokuzuncu","onuncu","onbirinci","onikinci", "onucuncu","ondordoncu", "onbesinci","onaltinci","onyedinci","onsekizinci", "ondokuzuncu","yirminci","yirmibirinci","yirmikinci", "yirmiucuncu","yirmidorduncu", "yirmibesinci","yirmialtinci","yirmiyedinci","yirmisekizinci", "yirmidokuzuncu","otuzuncu","otuzbirinci"], ["birinci","ikinci","\xfc\xe7\xfcnc\xfc","d\xf6rd\xfcnc\xfc", "be\xfeinci","alt\xfdnc\xfd","yedinci","sekizinci", "dokuzuncu","onuncu","onbirinci","onikinci", "on\xfc\xe7\xfcnc\xfc","ond\xf6rd\xfcnc\xfc", "onbe\xfeinci","onalt\xfdnc\xfd","onyedinci","onsekizinci", "ondokuzuncu","yirminci","yirmibirinci","yirmikinci", "yirmi\xfc\xe7\xfcnc\xfc","yirmid\xf6rd\xfcnc\xfc", "yirmibe\xfeinci","yirmialt\xfdnc\xfd","yirmiyedinci","yirmisekizinci", "yirmidokuzuncu","otuzuncu","otuzbirinci"] ]; $$d{"now"} =["\xfeimdi", "simdi"]; $$d{"today"} =["bugun", "bug\xfcn"]; $$d{"last"} =["son", "sonuncu"]; $$d{"each"} =["her"]; $$d{"of"} =["of"]; $$d{"at"} =["saat"]; $$d{"on"} =["on"]; $$d{"future"} =["gelecek"]; $$d{"past"} =["ge\xe7mi\xfe", "gecmis","gecen", "ge\xe7en"]; $$d{"next"} =["gelecek","sonraki"]; $$d{"prev"} =["onceki","\xf6nceki"]; $$d{"later"} =["sonra"]; $$d{"exact"} =["tam"]; $$d{"approx"} =["yakla\xfe\xfdk", "yaklasik"]; $$d{"business"}=["i\xfe","\xe7al\xfd\xfema","is", "calisma"]; $$d{"offset"} =["d\xfcn","-0:0:0:1:0:0:0", "dun", "-0:0:0:1:0:0:0", "yar\xfdn","+0:0:0:1:0:0:0", "yarin","+0:0:0:1:0:0:0"]; $$d{"times"} =["\xf6\xf0len","12:00:00", "oglen","12:00:00", "yarim","12:300:00", "yar\xfdm","12:30:00", "gece yar\xfds\xfd","00:00:00", "gece yarisi","00:00:00"]; $$d{"years"} =["yil","y"]; $$d{"months"} =["ay","a"]; $$d{"weeks"} =["hafta", "h"]; $$d{"days"} =["gun","g"]; $$d{"hours"} =["saat"]; $$d{"minutes"} =["dakika","dak","d"]; $$d{"seconds"} =["saniye","sn",]; $$d{"replace"} =["s","saat"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:,]'; $$d{"am"} = ["\xf6gleden \xf6nce","ogleden once"]; $$d{"pm"} = ["\xf6\xf0leden sonra","ogleden sonra"]; } sub Date_Init_Danish { print "DEBUG: Date_Init_Danish\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; $$d{"month_name"}= [["Januar","Februar","Marts","April","Maj","Juni", "Juli","August","September","Oktober","November","December"]]; $$d{"month_abb"}= [["Jan","Feb","Mar","Apr","Maj","Jun", "Jul","Aug","Sep","Okt","Nov","Dec"]]; $$d{"day_name"}= [["Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lordag","Sondag"], ["Mandag","Tirsdag","Onsdag","Torsdag","Fredag","L\xf8rdag","S\xf8ndag"]]; $$d{"day_abb"}= [["Man","Tis","Ons","Tor","Fre","Lor","Son"], ["Man","Tis","Ons","Tor","Fre","L\xf8r","S\xf8n"]]; $$d{"day_char"}= [["M","Ti","O","To","F","L","S"]]; $$d{"num_suff"}= [["1:e","2:e","3:e","4:e","5:e","6:e","7:e","8:e","9:e","10:e", "11:e","12:e","13:e","14:e","15:e","16:e","17:e","18:e","19:e","20:e", "21:e","22:e","23:e","24:e","25:e","26:e","27:e","28:e","29:e","30:e", "31:e"]]; $$d{"num_word"}= [["forste","anden","tredie","fjerde","femte","sjette","syvende", "ottende","niende","tiende","elfte","tolvte","trettende","fjortende", "femtende","sekstende","syttende","attende","nittende","tyvende", "enogtyvende","toogtyvende","treogtyvende","fireogtyvende","femogtyvende", "seksogtyvende","syvogtyvende","otteogtyvende","niogtyvende", "tredivte","enogtredivte"], ["f\xf8rste","anden","tredie","fjerde","femte","sjette","syvende", "ottende","niende","tiende","elfte","tolvte","trettende","fjortende", "femtende","sekstende","syttende","attende","nittende","tyvende", "enogtyvende","toogtyvende","treogtyvende","fireogtyvende","femogtyvende", "seksogtyvende","syvogtyvende","otteogtyvende","niogtyvende", "tredivte","enogtredivte"]]; $$d{"now"} =["nu"]; $$d{"today"} =["idag"]; $$d{"last"} =["forrige","sidste","nyeste"]; $$d{"each"} =["hver"]; $$d{"of"} =["om"]; $$d{"at"} =["kl","kl.","klokken"]; $$d{"on"} =["pa","p\xe5"]; $$d{"future"} =["om"]; $$d{"past"} =["siden"]; $$d{"next"} =["nasta","n\xe6ste"]; $$d{"prev"} =["forrige"]; $$d{"later"} =["senere"]; $$d{"exact"} =["pracist","pr\xe6cist"]; $$d{"approx"} =["circa"]; $$d{"business"}=["arbejdsdag","arbejdsdage"]; $$d{"offset"} =["ig\xe5r","-0:0:0:1:0:0:0","igar","-0:0:0:1:0:0:0", "imorgen","+0:0:0:1:0:0:0"]; $$d{"times"} =["midt pa dagen","12:00:00","midt p\xe5 dagen","12:00:00", "midnat","00:00:00"]; $$d{"years"} =["ar","\xe5r"]; $$d{"months"} =["man","maned","maneder","m\xe5n","m\xe5ned","m\xe5neder"]; $$d{"weeks"} =["u","uge","uger"]; $$d{"days"} =["d","dag","dage"]; $$d{"hours"} =["t","tim","time","timer"]; $$d{"minutes"} =["min","minut","minutter"]; $$d{"seconds"} =["s","sek","sekund","sekunder"]; $$d{"replace"} =["m","minut"]; $$d{"sephm"} ='[.:]'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["FM"]; $$d{"pm"} = ["EM"]; } sub Date_Init_Catalan { print "DEBUG: Date_Init_Catalan\n" if ($Curr{"Debug"} =~ /trace/); my($d)=@_; $$d{"month_name"}= [["Gener","Febrer","Marc","Abril","Maig","Juny", "Juliol","Agost","Setembre","Octubre","Novembre","Desembre"], ["Gener","Febrer","Març","Abril","Maig","Juny", "Juliol","Agost","Setembre","Octubre","Novembre","Desembre"], ["Gener","Febrer","Marc,","Abril","Maig","Juny", "Juliol","Agost","Setembre","Octubre","Novembre","Desembre"]]; $$d{"month_abb"}= [["Gen","Feb","Mar","Abr","Mai","Jun", "Jul","Ago","Set","Oct","Nov","Des"], [], ["","","","","","", "","","","","","Dec"] #common mistake ]; $$d{"day_name"}= [["Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte","Diumenge"]]; $$d{"day_abb"}= [["Dll","Dmt","Dmc","Dij","Div","Dis","Diu"], ["","Dim","","","","",""], ["","","Dic","","","",""] ]; $$d{"day_char"}= [["Dl","Dm","Dc","Dj","Dv","Ds","Du"] , ["L","M","X","J","V","S","U"]]; $$d{"num_suff"}= [["1er","2n","3r","4t","5e","6e","7e","8e","9e","10e", "11e","12e","13e","14e","15e","16e","17e","18e","19e","20e", "21e","22e","23e","24e","25e","26e","27e","28e","29e","30e", "31e"], ["1er","2n","3r","4t","5è","6è","7è","8è","9è","10è", "11è","12è","13è","14è","15è","16è","17è","18è","19è","20è", "21è","22è","23è","24è","25è","26è","27è","28è","29è","30è", "31è"]]; $$d{"num_word"}= [["primer","segon","tercer","quart","cinque","sise","sete","vuite", "nove","dese","onze","dotze","tretze","catorze", "quinze","setze","dissete","divuite","dinove", "vinte","vint-i-une","vint-i-dose","vint-i-trese", "vint-i-quatre","vint-i-cinque","vint-i-sise","vint-i-sete", "vint-i-vuite","vint-i-nove","trente","trenta-une"], ["primer","segon","tercer","quart","cinquè","sisè","setè","vuitè", "novè","desè","onzè","dotzè","tretzè","catorzè", "quinzè","setzè","dissetè","divuitè","dinovè", "vintè","vint-i-unè","vint-i-dosè","vint-i-tresè", "vint-i-quatrè","vint-i-cinquè","vint-i-sisè","vint-i-setè", "vint-i-vuitè","vint-i-novè","trentè","trenta-unè"]]; $$d{"now"} =["avui","ara"]; $$d{"last"} =["darrer","últim","darrera","última"]; $$d{"each"} =["cada","cadascun","cadascuna"]; $$d{"of"} =["de","d'"]; $$d{"at"} =["a les","a","al"]; $$d{"on"} =["el"]; $$d{"future"} =["d'aquí a"]; $$d{"past"} =["fa"]; $$d{"next"} =["proper"]; $$d{"prev"} =["passat","proppassat","anterior"]; $$d{"later"} =["més tard"]; $$d{"exact"} =["exactament"]; $$d{"approx"} =["approximadament"]; $$d{"business"}=["empresa"]; $$d{"offset"} =["ahir","-0:0:0:1:0:0:0","demà","+0:0:0:1:0:0:0","abans d'ahir","-0:0:0:2:0:0:0","demà passat","+0:0:0:2:0:0:0",]; $$d{"times"} =["migdia","12:00:00","mitjanit","00:00:00"]; $$d{"years"} =["a","an","any","anys"]; $$d{"months"} =["mes","me","ms"]; $$d{"weeks"} =["se","set","setm","setmana","setmanes"]; $$d{"days"} =["d","dia","dies"]; $$d{"hours"} =["h","ho","hores","hora"]; $$d{"minutes"} =["mn","min","minut","minuts"]; $$d{"seconds"} =["s","seg","segon","segons"]; $$d{"replace"} =["m","mes","s","setmana"]; $$d{"sephm"} =':'; $$d{"sepms"} =':'; $$d{"sepss"} ='[.:]'; $$d{"am"} = ["AM","A.M."]; $$d{"pm"} = ["PM","P.M."]; } ######################################################################## # FROM MY PERSONAL LIBRARIES ######################################################################## no integer; # &ModuloAddition($N,$add,\$val,\$rem); # This calculates $val=$val+$add and forces $val to be in a certain range. # This is useful for adding numbers for which only a certain range is # allowed (for example, minutes can be between 0 and 59 or months can be # between 1 and 12). The absolute value of $N determines the range and # the sign of $N determines whether the range is 0 to N-1 (if N>0) or # 1 to N (N<0). The remainder (as modulo N) is added to $rem. # Example: # To add 2 hours together (with the excess returned in days) use: # &ModuloAddition(60,$s1,\$s,\$day); sub ModuloAddition { my($N,$add,$val,$rem)=@_; return if ($N==0); $$val+=$add; if ($N<0) { # 1 to N $N = -$N; if ($$val>$N) { $$rem+= int(($$val-1)/$N); $$val = ($$val-1)%$N +1; } elsif ($$val<1) { $$rem-= int(-$$val/$N)+1; $$val = $N-(-$$val % $N); } } else { # 0 to N-1 if ($$val>($N-1)) { $$rem+= int($$val/$N); $$val = $$val%$N; } elsif ($$val<0) { $$rem-= int(-($$val+1)/$N)+1; $$val = ($N-1)-(-($$val+1)%$N); } } } # $Flag=&IsInt($String [,$low, $high]); # Returns 1 if $String is a valid integer, 0 otherwise. If $low is # entered, $String must be >= $low. If $high is entered, $String must # be <= $high. It is valid to check only one of the bounds. sub IsInt { my($N,$low,$high)=@_; return 0 if (! defined $N or $N !~ /^\s*[-+]?\d+\s*$/ or defined $low && $N<$low or defined $high && $N>$high); return 1; } # $Pos=&SinLindex(\@List,$Str [,$offset [,$CaseInsensitive]]); # Searches for an exact string in a list. # # This is similar to RinLindex except that it searches for elements # which are exactly equal to $Str (possibly case insensitive). sub SinLindex { my($listref,$Str,$offset,$Insensitive)=@_; my($i,$len,$tmp)=(); $len=$#$listref; return -2 if ($len<0 or ! $Str); return -1 if (&Index_First(\$offset,$len)); $Str=uc($Str) if ($Insensitive); for ($i=$offset; $i<=$len; $i++) { $tmp=$$listref[$i]; $tmp=uc($tmp) if ($Insensitive); return $i if ($tmp eq $Str); } return -1; } sub Index_First { my($offsetref,$max)=@_; $$offsetref=0 if (! $$offsetref); if ($$offsetref < 0) { $$offsetref += $max + 1; $$offsetref=0 if ($$offsetref < 0); } return -1 if ($$offsetref > $max); return 0; } # $File=&CleanFile($file); # This cleans up a path to remove the following things: # double slash /a//b -> /a/b # trailing dot /a/. -> /a # leading dot ./a -> a # trailing slash a/ -> a sub CleanFile { my($file)=@_; $file =~ s/\s*$//; $file =~ s/^\s*//; $file =~ s|//+|/|g; # multiple slash $file =~ s|/\.$|/|; # trailing /. (leaves trailing slash) $file =~ s|^\./|| # leading ./ if ($file ne "./"); $file =~ s|/$|| # trailing slash if ($file ne "/"); return $file; } # $File=&ExpandTilde($file); # This checks to see if a "~" appears as the first character in a path. # If it does, the "~" expansion is interpreted (if possible) and the full # path is returned. If a "~" expansion is used but cannot be # interpreted, an empty string is returned. # # This is Windows/Mac friendly. # This is efficient. sub ExpandTilde { my($file)=shift; my($user,$home)=(); # ~aaa/bbb= ~ aaa /bbb if ($file =~ s|^~([^/]*)||) { $user=$1; # Single user operating systems (Mac, MSWindows) don't have the getpwnam # and getpwuid routines defined. Try to catch various different ways # of knowing we are on one of these systems: return "" if ($OS eq "Windows" or $OS eq "Mac" or $OS eq "Netware" or $OS eq "MPE"); $user="" if (! defined $user); if ($user) { $home= (getpwnam($user))[7]; } else { $home= (getpwuid($<))[7]; } $home = VMS::Filespec::unixpath($home) if ($OS eq "VMS"); return "" if (! $home); $file="$home/$file"; } $file; } # $File=&FullFilePath($file); # Returns the full or relative path to $file (expanding "~" if necessary). # Returns an empty string if a "~" expansion cannot be interpreted. The # path does not need to exist. CleanFile is called. sub FullFilePath { my($file)=shift; my($rootpat) = '^/'; #default pattern to match absolute path $rootpat = '^(\\|/|([A-Za-z]:[\\/]))' if ($OS eq 'Windows'); $file=&ExpandTilde($file); return "" if (! $file); return &CleanFile($file); } # $Flag=&CheckFilePath($file [,$mode]); # Checks to see if $file exists, to see what type it is, and whether # the script can access it. If it exists and has the correct mode, 1 # is returned. # # $mode is a string which may contain any of the valid file test operator # characters except t, M, A, C. The appropriate test is run for each # character. For example, if $mode is "re" the -r and -e tests are both # run. # # An empty string is returned if the file doesn't exist. A 0 is returned # if the file exists but any test fails. # # All characters in $mode which do not correspond to valid tests are # ignored. sub CheckFilePath { my($file,$mode)=@_; my($test)=(); $file=&FullFilePath($file); $mode = "" if (! defined $mode); # Run tests return 0 if (! defined $file or ! $file); return 0 if (( ! -e $file) or ($mode =~ /r/ && ! -r $file) or ($mode =~ /w/ && ! -w $file) or ($mode =~ /x/ && ! -x $file) or ($mode =~ /R/ && ! -R $file) or ($mode =~ /W/ && ! -W $file) or ($mode =~ /X/ && ! -X $file) or ($mode =~ /o/ && ! -o $file) or ($mode =~ /O/ && ! -O $file) or ($mode =~ /z/ && ! -z $file) or ($mode =~ /s/ && ! -s $file) or ($mode =~ /f/ && ! -f $file) or ($mode =~ /d/ && ! -d $file) or ($mode =~ /l/ && ! -l $file) or ($mode =~ /s/ && ! -s $file) or ($mode =~ /p/ && ! -p $file) or ($mode =~ /b/ && ! -b $file) or ($mode =~ /c/ && ! -c $file) or ($mode =~ /u/ && ! -u $file) or ($mode =~ /g/ && ! -g $file) or ($mode =~ /k/ && ! -k $file) or ($mode =~ /T/ && ! -T $file) or ($mode =~ /B/ && ! -B $file)); return 1; } #&& # $Path=&FixPath($path [,$full] [,$mode] [,$error]); # Makes sure that every directory in $path (a colon separated list of # directories) appears as a full path or relative path. All "~" # expansions are removed. All trailing slashes are removed also. If # $full is non-nil, relative paths are expanded to full paths as well. # # If $mode is given, it may be either "e", "r", or "w". In this case, # additional checking is done to each directory. If $mode is "e", it # need ony exist to pass the check. If $mode is "r", it must have have # read and execute permission. If $mode is "w", it must have read, # write, and execute permission. # # The value of $error determines what happens if the directory does not # pass the test. If it is non-nil, if any directory does not pass the # test, the subroutine returns the empty string. Otherwise, it is simply # removed from $path. # # The corrected path is returned. sub FixPath { my($path,$full,$mode,$err)=@_; local($_)=""; my(@dir)=split(/$Cnf{"PathSep"}/,$path); $full=0 if (! defined $full); $mode="" if (! defined $mode); $err=0 if (! defined $err); $path=""; if ($mode eq "e") { $mode="de"; } elsif ($mode eq "r") { $mode="derx"; } elsif ($mode eq "w") { $mode="derwx"; } foreach (@dir) { # Expand path if ($full) { $_=&FullFilePath($_); } else { $_=&ExpandTilde($_); } if (! $_) { return "" if ($err); next; } # Check mode if (! $mode or &CheckFilePath($_,$mode)) { $path .= $Cnf{"PathSep"} . $_; } else { return "" if ($err); } } $path =~ s/^$Cnf{"PathSep"}//; return $path; } #&& # $File=&SearchPath($file,$path [,$mode] [,@suffixes]); # Searches through directories in $path for a file named $file. The # full path is returned if one is found, or an empty string otherwise. # The file may exist with one of the @suffixes. The mode is checked # similar to &CheckFilePath. # # The first full path that matches the name and mode is returned. If none # is found, an empty string is returned. sub SearchPath { my($file,$path,$mode,@suff)=@_; my($f,$s,$d,@dir,$fs)=(); $path=&FixPath($path,1,"r"); @dir=split(/$Cnf{"PathSep"}/,$path); foreach $d (@dir) { $f="$d/$file"; $f=~ s|//|/|g; return $f if (&CheckFilePath($f,$mode)); foreach $s (@suff) { $fs="$f.$s"; return $fs if (&CheckFilePath($fs,$mode)); } } return ""; } # @list=&ReturnList($str); # This takes a string which should be a comma separated list of integers # or ranges (5-7). It returns a sorted list of all integers referred to # by the string, or () if there is an invalid element. # # Negative integers are also handled. "-2--1" is equivalent to "-2,-1". sub ReturnList { my($str)=@_; my(@ret,@str,$from,$to,$tmp)=(); @str=split(/,/,$str); foreach $str (@str) { if ($str =~ /^[-+]?\d+$/) { push(@ret,$str); } elsif ($str =~ /^([-+]?\d+)-([-+]?\d+)$/) { ($from,$to)=($1,$2); if ($from>$to) { $tmp=$from; $from=$to; $to=$tmp; } push(@ret,$from..$to); } else { return (); } } @ret; } 1;