| 1 |
package Date::Manip; |
|---|
| 2 |
# Copyright (c) 1995-2005 Sullivan Beck. All rights reserved. |
|---|
| 3 |
# This program is free software; you can redistribute it and/or modify it |
|---|
| 4 |
# under the same terms as Perl itself. |
|---|
| 5 |
|
|---|
| 6 |
########################################################################### |
|---|
| 7 |
########################################################################### |
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
use vars qw($OS %Lang %Holiday %Events %Curr %Cnf %Zone $VERSION @ISA @EXPORT); |
|---|
| 11 |
|
|---|
| 12 |
# Determine the type of OS... |
|---|
| 13 |
$OS="Unix"; |
|---|
| 14 |
$OS="Windows" if ((defined $^O and |
|---|
| 15 |
$^O =~ /MSWin32/i || |
|---|
| 16 |
$^O =~ /Windows_95/i || |
|---|
| 17 |
$^O =~ /Windows_NT/i) || |
|---|
| 18 |
(defined $ENV{OS} and |
|---|
| 19 |
$ENV{OS} =~ /MSWin32/i || |
|---|
| 20 |
$ENV{OS} =~ /Windows_95/i || |
|---|
| 21 |
$ENV{OS} =~ /Windows_NT/i)); |
|---|
| 22 |
$OS="Unix" if (defined $^O and |
|---|
| 23 |
$^O =~ /cygwin/i); |
|---|
| 24 |
$OS="Netware" if (defined $^O and |
|---|
| 25 |
$^O =~ /NetWare/i); |
|---|
| 26 |
$OS="Mac" if ((defined $^O and |
|---|
| 27 |
$^O =~ /MacOS/i) || |
|---|
| 28 |
(defined $ENV{OS} and |
|---|
| 29 |
$ENV{OS} =~ /MacOS/i)); |
|---|
| 30 |
$OS="MPE" if (defined $^O and |
|---|
| 31 |
$^O =~ /MPE/i); |
|---|
| 32 |
$OS="OS2" if (defined $^O and |
|---|
| 33 |
$^O =~ /os2/i); |
|---|
| 34 |
$OS="VMS" if (defined $^O and |
|---|
| 35 |
$^O =~ /VMS/i); |
|---|
| 36 |
|
|---|
| 37 |
# Determine if we're doing taint checking |
|---|
| 38 |
$Date::Manip::NoTaint = eval { local $^W=0; eval("#" . substr($^X, 0, 0)); 1 }; |
|---|
| 39 |
|
|---|
| 40 |
########################################################################### |
|---|
| 41 |
# CUSTOMIZATION |
|---|
| 42 |
########################################################################### |
|---|
| 43 |
# |
|---|
| 44 |
# See the section of the POD documentation section CUSTOMIZING DATE::MANIP |
|---|
| 45 |
# below for a complete description of each of these variables. |
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
# Location of a the global config file. Tilde (~) expansions are allowed. |
|---|
| 49 |
# This should be set in Date_Init arguments. |
|---|
| 50 |
$Cnf{"GlobalCnf"}="/etc/DateManip.cnf"; |
|---|
| 51 |
$Cnf{"IgnoreGlobalCnf"}=""; |
|---|
| 52 |
|
|---|
| 53 |
# Name of a personal config file and the path to search for it. Tilde (~) |
|---|
| 54 |
# expansions are allowed. This should be set in Date_Init arguments or in |
|---|
| 55 |
# the global config file. |
|---|
| 56 |
|
|---|
| 57 |
@Date::Manip::DatePath=(); |
|---|
| 58 |
if ($OS eq "Windows") { |
|---|
| 59 |
$Cnf{"PathSep"} = ";"; |
|---|
| 60 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 61 |
$Cnf{"PersonalCnfPath"} = "."; |
|---|
| 62 |
|
|---|
| 63 |
} elsif ($OS eq "Netware") { |
|---|
| 64 |
$Cnf{"PathSep"} = ";"; |
|---|
| 65 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 66 |
$Cnf{"PersonalCnfPath"} = "."; |
|---|
| 67 |
|
|---|
| 68 |
} elsif ($OS eq "MPE") { |
|---|
| 69 |
$Cnf{"PathSep"} = ":"; |
|---|
| 70 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 71 |
$Cnf{"PersonalCnfPath"} = "."; |
|---|
| 72 |
|
|---|
| 73 |
} elsif ($OS eq "OS2") { |
|---|
| 74 |
$Cnf{"PathSep"} = ":"; |
|---|
| 75 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 76 |
$Cnf{"PersonalCnfPath"} = "."; |
|---|
| 77 |
|
|---|
| 78 |
} elsif ($OS eq "Mac") { |
|---|
| 79 |
$Cnf{"PathSep"} = ":"; |
|---|
| 80 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 81 |
$Cnf{"PersonalCnfPath"} = "."; |
|---|
| 82 |
|
|---|
| 83 |
} elsif ($OS eq "VMS") { |
|---|
| 84 |
# VMS doesn't like files starting with "." |
|---|
| 85 |
$Cnf{"PathSep"} = ","; |
|---|
| 86 |
$Cnf{"PersonalCnf"} = "Manip.cnf"; |
|---|
| 87 |
$Cnf{"PersonalCnfPath"} = "/sys\$login"; |
|---|
| 88 |
|
|---|
| 89 |
} else { |
|---|
| 90 |
# Unix |
|---|
| 91 |
$Cnf{"PathSep"} = ":"; |
|---|
| 92 |
$Cnf{"PersonalCnf"} = ".DateManip.cnf"; |
|---|
| 93 |
$Cnf{"PersonalCnfPath"} = ".:~"; |
|---|
| 94 |
@Date::Manip::DatePath=qw(/bin /usr/bin /usr/local/bin); |
|---|
| 95 |
} |
|---|
| 96 |
|
|---|
| 97 |
### Date::Manip variables set in the global or personal config file |
|---|
| 98 |
|
|---|
| 99 |
# Which language to use when parsing dates. |
|---|
| 100 |
$Cnf{"Language"}="English"; |
|---|
| 101 |
|
|---|
| 102 |
# 12/10 = Dec 10 (US) or Oct 12 (anything else) |
|---|
| 103 |
$Cnf{"DateFormat"}="US"; |
|---|
| 104 |
|
|---|
| 105 |
# Local timezone |
|---|
| 106 |
$Cnf{"TZ"}=""; |
|---|
| 107 |
|
|---|
| 108 |
# Timezone to work in (""=local, "IGNORE", or a timezone) |
|---|
| 109 |
$Cnf{"ConvTZ"}=""; |
|---|
| 110 |
|
|---|
| 111 |
# Date::Manip internal format (0=YYYYMMDDHH:MN:SS, 1=YYYYHHMMDDHHMNSS) |
|---|
| 112 |
$Cnf{"Internal"}=0; |
|---|
| 113 |
|
|---|
| 114 |
# First day of the week (1=monday, 7=sunday). ISO 8601 says monday. |
|---|
| 115 |
$Cnf{"FirstDay"}=1; |
|---|
| 116 |
|
|---|
| 117 |
# First and last day of the work week (1=monday, 7=sunday) |
|---|
| 118 |
$Cnf{"WorkWeekBeg"}=1; |
|---|
| 119 |
$Cnf{"WorkWeekEnd"}=5; |
|---|
| 120 |
|
|---|
| 121 |
# If non-nil, a work day is treated as 24 hours long (WorkDayBeg/WorkDayEnd |
|---|
| 122 |
# ignored) |
|---|
| 123 |
$Cnf{"WorkDay24Hr"}=0; |
|---|
| 124 |
|
|---|
| 125 |
# Start and end time of the work day (any time format allowed, seconds |
|---|
| 126 |
# ignored) |
|---|
| 127 |
$Cnf{"WorkDayBeg"}="08:00"; |
|---|
| 128 |
$Cnf{"WorkDayEnd"}="17:00"; |
|---|
| 129 |
|
|---|
| 130 |
# If "today" is a holiday, we look either to "tomorrow" or "yesterday" for |
|---|
| 131 |
# the nearest business day. By default, we'll always look "tomorrow" |
|---|
| 132 |
# first. |
|---|
| 133 |
$Cnf{"TomorrowFirst"}=1; |
|---|
| 134 |
|
|---|
| 135 |
# Erase the old holidays |
|---|
| 136 |
$Cnf{"EraseHolidays"}=""; |
|---|
| 137 |
|
|---|
| 138 |
# Set this to non-zero to be produce completely backwards compatible deltas |
|---|
| 139 |
$Cnf{"DeltaSigns"}=0; |
|---|
| 140 |
|
|---|
| 141 |
# If this is 0, use the ISO 8601 standard that Jan 4 is in week 1. If 1, |
|---|
| 142 |
# make week 1 contain Jan 1. |
|---|
| 143 |
$Cnf{"Jan1Week1"}=0; |
|---|
| 144 |
|
|---|
| 145 |
# 2 digit years fall into the 100 year period given by [ CURR-N, |
|---|
| 146 |
# CURR+(99-N) ] where N is 0-99. Default behavior is 89, but other useful |
|---|
| 147 |
# numbers might be 0 (forced to be this year or later) and 99 (forced to be |
|---|
| 148 |
# this year or earlier). It can also be set to "c" (current century) or |
|---|
| 149 |
# "cNN" (i.e. c18 forces the year to bet 1800-1899). Also accepts the |
|---|
| 150 |
# form cNNNN to give the 100 year period NNNN to NNNN+99. |
|---|
| 151 |
$Cnf{"YYtoYYYY"}=89; |
|---|
| 152 |
|
|---|
| 153 |
# Set this to 1 if you want a long-running script to always update the |
|---|
| 154 |
# timezone. This will slow Date::Manip down. Read the POD documentation. |
|---|
| 155 |
$Cnf{"UpdateCurrTZ"}=0; |
|---|
| 156 |
|
|---|
| 157 |
# Use an international character set. |
|---|
| 158 |
$Cnf{"IntCharSet"}=0; |
|---|
| 159 |
|
|---|
| 160 |
# Use this to force the current date to be set to this: |
|---|
| 161 |
$Cnf{"ForceDate"}=""; |
|---|
| 162 |
|
|---|
| 163 |
# Use this to make "today" mean "today at midnight". |
|---|
| 164 |
$Cnf{"TodayIsMidnight"}=0; |
|---|
| 165 |
|
|---|
| 166 |
########################################################################### |
|---|
| 167 |
|
|---|
| 168 |
require 5.000; |
|---|
| 169 |
require Exporter; |
|---|
| 170 |
@ISA = qw(Exporter); |
|---|
| 171 |
@EXPORT = qw( |
|---|
| 172 |
DateManipVersion |
|---|
| 173 |
Date_Init |
|---|
| 174 |
ParseDateString |
|---|
| 175 |
ParseDate |
|---|
| 176 |
ParseRecur |
|---|
| 177 |
Date_Cmp |
|---|
| 178 |
DateCalc |
|---|
| 179 |
ParseDateDelta |
|---|
| 180 |
UnixDate |
|---|
| 181 |
Delta_Format |
|---|
| 182 |
Date_GetPrev |
|---|
| 183 |
Date_GetNext |
|---|
| 184 |
Date_SetTime |
|---|
| 185 |
Date_SetDateField |
|---|
| 186 |
Date_IsHoliday |
|---|
| 187 |
Events_List |
|---|
| 188 |
|
|---|
| 189 |
Date_DaysInMonth |
|---|
| 190 |
Date_DayOfWeek |
|---|
| 191 |
Date_SecsSince1970 |
|---|
| 192 |
Date_SecsSince1970GMT |
|---|
| 193 |
Date_DaysSince1BC |
|---|
| 194 |
Date_DayOfYear |
|---|
| 195 |
Date_DaysInYear |
|---|
| 196 |
Date_WeekOfYear |
|---|
| 197 |
Date_LeapYear |
|---|
| 198 |
Date_DaySuffix |
|---|
| 199 |
Date_ConvTZ |
|---|
| 200 |
Date_TimeZone |
|---|
| 201 |
Date_IsWorkDay |
|---|
| 202 |
Date_NextWorkDay |
|---|
| 203 |
Date_PrevWorkDay |
|---|
| 204 |
Date_NearestWorkDay |
|---|
| 205 |
Date_NthDayOfYear |
|---|
| 206 |
); |
|---|
| 207 |
use strict; |
|---|
| 208 |
use integer; |
|---|
| 209 |
use Carp; |
|---|
| 210 |
|
|---|
| 211 |
use IO::File; |
|---|
| 212 |
|
|---|
| 213 |
$VERSION="5.44"; |
|---|
| 214 |
|
|---|
| 215 |
######################################################################## |
|---|
| 216 |
######################################################################## |
|---|
| 217 |
|
|---|
| 218 |
$Curr{"InitLang"} = 1; # Whether a language is being init'ed |
|---|
| 219 |
$Curr{"InitDone"} = 0; # Whether Init_Date has been called |
|---|
| 220 |
$Curr{"InitFilesRead"} = 0; |
|---|
| 221 |
$Curr{"ResetWorkDay"} = 1; |
|---|
| 222 |
$Curr{"Debug"} = ""; |
|---|
| 223 |
$Curr{"DebugVal"} = ""; |
|---|
| 224 |
|
|---|
| 225 |
$Holiday{"year"} = 0; |
|---|
| 226 |
$Holiday{"dates"} = {}; |
|---|
| 227 |
$Holiday{"desc"} = {}; |
|---|
| 228 |
|
|---|
| 229 |
$Events{"raw"} = []; |
|---|
| 230 |
$Events{"parsed"} = 0; |
|---|
| 231 |
$Events{"dates"} = []; |
|---|
| 232 |
$Events{"recur"} = []; |
|---|
| 233 |
|
|---|
| 234 |
######################################################################## |
|---|
| 235 |
######################################################################## |
|---|
| 236 |
# THESE ARE THE MAIN ROUTINES |
|---|
| 237 |
######################################################################## |
|---|
| 238 |
######################################################################## |
|---|
| 239 |
|
|---|
| 240 |
# Get rid of a problem with old versions of perl |
|---|
| 241 |
no strict "vars"; |
|---|
| 242 |
# This sorts from longest to shortest element |
|---|
| 243 |
sub sortByLength { |
|---|
| 244 |
return (length $b <=> length $a); |
|---|
| 245 |
} |
|---|
| 246 |
use strict "vars"; |
|---|
| 247 |
|
|---|
| 248 |
sub DateManipVersion { |
|---|
| 249 |
print "DEBUG: DateManipVersion\n" if ($Curr{"Debug"} =~ /trace/); |
|---|
| 250 |
return "bkuhn-" . $VERSION; |
|---|
| 251 |
} |
|---|
| 252 |
|
|---|
| 253 |
sub Date_Init { |
|---|
| 254 |
print "DEBUG: Date_Init\n" if ($Curr{"Debug"} =~ /trace/); |
|---|
| 255 |
$Curr{"Debug"}=""; |
|---|
| 256 |
|
|---|
| 257 |
my(@args)=@_; |
|---|
| 258 |
$Curr{"InitDone"}=1; |
|---|
| 259 |
local($_)=(); |
|---|
| 260 |
my($internal,$firstday)=(); |
|---|
| 261 |
my($var,$val,$file,@tmp)=(); |
|---|
| 262 |
|
|---|
| 263 |
# InitFilesRead = 0 : no conf files read yet |
|---|
| 264 |
# 1 : global read, no personal read |
|---|
| 265 |
# 2 : personal read |
|---|
| 266 |
|
|---|
| 267 |
$Cnf{"EraseHolidays"}=0; |
|---|
| 268 |
foreach (@args) { |
|---|
| 269 |
s/\s*$//; |
|---|
| 270 |
s/^\s*//; |
|---|
| 271 |
/^(\S+) \s* = \s* (.*)$/x; |
|---|
| 272 |
($var,$val)=($1,$2); |
|---|
| 273 |
if ($var =~ /^GlobalCnf$/i) { |
|---|
| 274 |
$Cnf{"GlobalCnf"}=$val; |
|---|
| 275 |
if ($val) { |
|---|
| 276 |
$Curr{"InitFilesRead"}=0; |
|---|
| 277 |
&EraseHolidays(); |
|---|
| 278 |
} |
|---|
| 279 |
} elsif ($var =~ /^PathSep$/i) { |
|---|
| 280 |
$Cnf{"PathSep"}=$val; |
|---|
| 281 |
} elsif ($var =~ /^PersonalCnf$/i) { |
|---|
| 282 |
$Cnf{"PersonalCnf"}=$val; |
|---|
| 283 |
$Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==2); |
|---|
| 284 |
} elsif ($var =~ /^PersonalCnfPath$/i) { |
|---|
| 285 |
$Cnf{"PersonalCnfPath"}=$val; |
|---|
| 286 |
$Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==2); |
|---|
| 287 |
} elsif ($var =~ /^IgnoreGlobalCnf$/i) { |
|---|
| 288 |
$Curr{"InitFilesRead"}=1 if ($Curr{"InitFilesRead"}==0); |
|---|
| 289 |
$Cnf{"IgnoreGlobalCnf"}=1; |
|---|
| 290 |
} elsif ($var =~ /^EraseHolidays$/i) { |
|---|
| 291 |
&EraseHolidays(); |
|---|
| 292 |
} else { |
|---|
| 293 |
push(@tmp,$_); |
|---|
| 294 |
} |
|---|
| 295 |
} |
|---|
| 296 |
@args=@tmp; |
|---|
| 297 |
|
|---|
| 298 |
# Read global config file |
|---|
| 299 |
if ($Curr{"InitFilesRead"}<1 && ! $Cnf{"IgnoreGlobalCnf"}) { |
|---|
| 300 |
$Curr{"InitFilesRead"}=1; |
|---|
| 301 |
|
|---|
| 302 |
if ($Cnf{"GlobalCnf"}) { |
|---|
| 303 |
$file=&ExpandTilde($Cnf{"GlobalCnf"}); |
|---|
| 304 |
&Date_InitFile($file) if ($file); |
|---|
| 305 |
} |
|---|
| 306 |
} |
|---|
| 307 |
|
|---|
| 308 |
# Read personal config file |
|---|
| 309 |
if ($Curr{"InitFilesRead"}<2) { |
|---|
| 310 |
$Curr{"InitFilesRead"}=2; |
|---|
| 311 |
|
|---|
| 312 |
if ($Cnf{"PersonalCnf"} and $Cnf{"PersonalCnfPath"}) { |
|---|
| 313 |
$file=&SearchPath($Cnf{"PersonalCnf"},$Cnf{"PersonalCnfPath"},"r"); |
|---|
| 314 |
&Date_InitFile($file) if ($file); |
|---|
| 315 |
} |
|---|
| 316 |
} |
|---|
| 317 |
|
|---|
| 318 |
foreach (@args) { |
|---|
| 319 |
s/\s*$//; |
|---|
| 320 |
s/^\s*//; |
|---|
| 321 |
/^(\S+) \s* = \s* (.*)$/x; |
|---|
| 322 |
($var,$val)=($1,$2); |
|---|
| 323 |
$val="" if (! defined $val); |
|---|
| 324 |
&Date_SetConfigVariable($var,$val); |
|---|
| 325 |
} |
|---|
| 326 |
|
|---|
| 327 |
confess "ERROR: Unknown FirstDay in Date::Manip.\n" |
|---|
| 328 |
if (! &IsInt($Cnf{"FirstDay"},1,7)); |
|---|
| 329 |
confess "ERROR: Unknown WorkWeekBeg in Date::Manip.\n" |
|---|
| 330 |
if (! &IsInt($Cnf{"WorkWeekBeg"},1,7)); |
|---|
| 331 |
confess "ERROR: Unknown WorkWeekEnd in Date::Manip.\n" |
|---|
| 332 |
if (! &IsInt($Cnf{"WorkWeekEnd"},1,7)); |
|---|
| 333 |
confess "ERROR: Invalid WorkWeek in Date::Manip.\n" |
|---|
| 334 |
if ($Cnf{"WorkWeekEnd"} <= $Cnf{"WorkWeekBeg"}); |
|---|
| 335 |
|
|---|
| 336 |
my(%lang, |
|---|
| 337 |
$tmp,%tmp,$tmp2,@tmp2, |
|---|
| 338 |
$i,$j,@tmp3, |
|---|
| 339 |
$zonesrfc,@zones)=(); |
|---|
| 340 |
|
|---|
| 341 |
my($L)=$Cnf{"Language"}; |
|---|
| 342 |
|
|---|
| 343 |
if ($Curr{"InitLang"}) { |
|---|
| 344 |
$Curr{"InitLang"}=0; |
|---|
| 345 |
|
|---|
| 346 |
if ($L eq "English") { |
|---|
| 347 |
&Date_Init_English(\%lang); |
|---|
| 348 |
|
|---|
| 349 |
} elsif ($L eq "French") { |
|---|
| 350 |
&Date_Init_French(\%lang); |
|---|
| 351 |
|
|---|
| 352 |
} elsif ($L eq "Swedish") { |
|---|
| 353 |
&Date_Init_Swedish(\%lang); |
|---|
| 354 |
|
|---|
| 355 |
} elsif ($L eq "German") { |
|---|
| 356 |
&Date_Init_German(\%lang); |
|---|
| 357 |
|
|---|
| 358 |
} elsif ($L eq "Polish") { |
|---|
| 359 |
&Date_Init_Polish(\%lang); |
|---|
| 360 |
|
|---|
| 361 |
} elsif ($L eq "Dutch" || |
|---|
| 362 |
$L eq "Nederlands") { |
|---|
| 363 |
&Date_Init_Dutch(\%lang); |
|---|
| 364 |
|
|---|
| 365 |
} elsif ($L eq "Spanish") { |
|---|
| 366 |
&Date_Init_Spanish(\%lang); |
|---|
| 367 |
|
|---|
| 368 |
} elsif ($L eq "Portuguese") { |
|---|
| 369 |
&Date_Init_Portuguese(\%lang); |
|---|
| 370 |
|
|---|
| 371 |
} elsif ($L eq "Romanian") { |
|---|
| 372 |
&Date_Init_Romanian(\%lang); |
|---|
| 373 |
|
|---|
| 374 |
} elsif ($L eq "Italian") { |
|---|
| 375 |
&Date_Init_Italian(\%lang); |
|---|
| 376 |
|
|---|
| 377 |
} elsif ($L eq "Russian") { |
|---|
| 378 |
&Date_Init_Russian(\%lang); |
|---|
| 379 |
|
|---|
| 380 |
} elsif ($L eq "Turkish") { |
|---|
| 381 |
&Date_Init_Turkish(\%lang); |
|---|
| 382 |
|
|---|
| 383 |
} elsif ($L eq "Danish") { |
|---|
| 384 |
&Date_Init_Danish(\%lang); |
|---|
| 385 |
|
|---|
| 386 |
} elsif ($L eq "Catalan") { |
|---|
| 387 |
&Date_Init_Catalan(\%lang); |
|---|
| 388 |
|
|---|
| 389 |
} else { |
|---|
| 390 |
confess "ERROR: Unknown language in Date::Manip.\n"; |
|---|
| 391 |
} |
|---|
| 392 |
|
|---|
| 393 |
# variables for months |
|---|
| 394 |
# Month = "(jan|january|feb|february ... )" |
|---|
| 395 |
# MonL = [ "Jan","Feb",... ] |
|---|
| 396 |
# MonthL = [ "January","February", ... ] |
|---|
| 397 |
# MonthH = { "january"=>1, "jan"=>1, ... } |
|---|
| 398 |
|
|---|
| 399 |
$Lang{$L}{"MonthH"}={}; |
|---|
| 400 |
$Lang{$L}{"MonthL"}=[]; |
|---|
| 401 |
$Lang{$L}{"MonL"}=[]; |
|---|
| 402 |
&Date_InitLists([$lang{"month_name"}, |
|---|
| 403 |
$lang{"month_abb"}], |
|---|
| 404 |
\$Lang{$L}{"Month"},"lc,sort,back", |
|---|
| 405 |
[$Lang{$L}{"MonthL"}, |
|---|
| 406 |
$Lang{$L}{"MonL"}], |
|---|
| 407 |
[$Lang{$L}{"MonthH"},1]); |
|---|
| 408 |
|
|---|
| 409 |
# variables for day of week |
|---|
| 410 |
# Week = "(mon|monday|tue|tuesday ... )" |
|---|
| 411 |
# WL = [ "M","T",... ] |
|---|
| 412 |
# WkL = [ "Mon","Tue",... ] |
|---|
| 413 |
# WeekL = [ "Monday","Tudesday",... ] |
|---|
| 414 |
# WeekH = { "monday"=>1,"mon"=>1,"m"=>1,... } |
|---|
| 415 |
|
|---|
| 416 |
$Lang{$L}{"WeekH"}={}; |
|---|
| 417 |
$Lang{$L}{"WeekL"}=[]; |
|---|
| 418 |
$Lang{$L}{"WkL"}=[]; |
|---|
| 419 |
$Lang{$L}{"WL"}=[]; |
|---|
| 420 |
&Date_InitLists([$lang{"day_name"}, |
|---|
| 421 |
$lang{"day_abb"}], |
|---|
| 422 |
\$Lang{$L}{"Week"},"lc,sort,back", |
|---|
| 423 |
[$Lang{$L}{"WeekL"}, |
|---|
| 424 |
$Lang{$L}{"WkL"}], |
|---|
| 425 |
[$Lang{$L}{"WeekH"},1]); |
|---|
| 426 |
&Date_InitLists([$lang{"day_char"}], |
|---|
| 427 |
"","lc", |
|---|
| 428 |
[$Lang{$L}{"WL"}], |
|---|
| 429 |
[\%tmp,1]); |
|---|
| 430 |
%{ $Lang{$L}{"WeekH"} } = |
|---|
| 431 |
(%{ $Lang{$L}{"WeekH"} },%tmp); |
|---|
| 432 |
|
|---|
| 433 |
# variables for last |
|---|
| 434 |
# Last = "(last)" |
|---|
| 435 |
# LastL = [ "last" ] |
|---|
| 436 |
# Each = "(each)" |
|---|
| 437 |
# EachL = [ "each" ] |
|---|
| 438 |
# variables for day of month |
|---|
| 439 |
# DoM = "(1st|first ... 31st)" |
|---|
| 440 |
# DoML = [ "1st","2nd",... "31st" ] |
|---|
| 441 |
# DoMH = { "1st"=>1,"first"=>1, ... "31st"=>31 } |
|---|
| 442 |
# variables for week of month |
|---|
| 443 |
# WoM = "(1st|first| ... 5th|last)" |
|---|
| 444 |
# WoMH = { "1st"=>1, ... "5th"=>5,"last"=>-1 } |
|---|
| 445 |
|
|---|
| 446 |
$Lang{$L}{"LastL"}=$lang{"last"}; |
|---|
| 447 |
&Date_InitStrings($lang{"last"}, |
|---|
| 448 |
\$Lang{$L}{"Last"},"lc,sort"); |
|---|
| 449 |
|
|---|
| 450 |
$Lang{$L}{"EachL"}=$lang{"each"}; |
|---|
| 451 |
&Date_InitStrings($lang{"each"}, |
|---|
| 452 |
\$Lang{$L}{"Each"},"lc,sort"); |
|---|
| 453 |
|
|---|
| 454 |
$Lang{$L}{"DoMH"}={}; |
|---|
| 455 |
$Lang{$L}{"DoML"}=[]; |
|---|
| 456 |
&Date_InitLists([$lang{"num_suff"}, |
|---|
| 457 |
$lang{"num_word"}], |
|---|
| 458 |
\$Lang{$L}{"DoM"},"lc,sort,back,escape", |
|---|
| 459 |
[$Lang{$L}{"DoML"}, |
|---|
| 460 |
\@tmp], |
|---|
| 461 |
[$Lang{$L}{"DoMH"},1]); |
|---|
| 462 |
|
|---|
| 463 |
@tmp=(); |
|---|
| 464 |
foreach $tmp (keys %{ $Lang{$L}{"DoMH"} }) { |
|---|
| 465 |
$tmp2=$Lang{$L}{"DoMH"}{$tmp}; |
|---|
| 466 |
if ($tmp2<6) { |
|---|
| 467 |
$Lang{$L}{"WoMH"}{$tmp} = $tmp2; |
|---|
| 468 |
push(@tmp,$tmp); |
|---|
| 469 |
} |
|---|
| 470 |
} |
|---|
| 471 |
foreach $tmp (@{ $Lang{$L}{"LastL"} }) { |
|---|
| 472 |
$Lang{$L}{"WoMH"}{$tmp} = -1; |
|---|
| 473 |
push(@tmp,$tmp); |
|---|
| 474 |
} |
|---|
| 475 |
&Date_InitStrings(\@tmp,\$Lang{$L}{"WoM"}, |
|---|
| 476 |
"lc,sort,back,escape"); |
|---|
| 477 |
|
|---|
| 478 |
# variables for AM or PM |
|---|
| 479 |
# AM = "(am)" |
|---|
| 480 |
# PM = "(pm)" |
|---|
| 481 |
# AmPm = "(am|pm)" |
|---|
| 482 |
# AMstr = "AM" |
|---|
| 483 |
# PMstr = "PM" |
|---|
| 484 |
|
|---|
| 485 |
&Date_InitStrings($lang{"am"},\$Lang{$L}{"AM"},"lc,sort,escape"); |
|---|
| 486 |
&Date_InitStrings($lang{"pm"},\$Lang{$L}{"PM"},"lc,sort,escape"); |
|---|
| 487 |
&Date_InitStrings([ @{$lang{"am"}},@{$lang{"pm"}} ],\$Lang{$L}{"AmPm"}, |
|---|
| 488 |
"lc,back,sort,escape"); |
|---|
| 489 |
$Lang{$L}{"AMstr"}=$lang{"am"}[0]; |
|---|
| 490 |
$Lang{$L}{"PMstr"}=$lang{"pm"}[0]; |
|---|
| 491 |
|
|---|
| 492 |
# variables for expressions used in parsing deltas |
|---|
| 493 |
# Yabb = "(?:y|yr|year|years)" |
|---|
| 494 |
# Mabb = similar for months |
|---|
| 495 |
# Wabb = similar for weeks |
|---|
| 496 |
# Dabb = similar for days |
|---|
| 497 |
# Habb = similar for hours |
|---|
| 498 |
# MNabb = similar for minutes |
|---|
| 499 |
# Sabb = similar for seconds |
|---|
| 500 |
# Repl = { "abb"=>"replacement" } |
|---|
| 501 |
# Whenever an abbreviation could potentially refer to two different |
|---|
| 502 |
# strings (M standing for Minutes or Months), the abbreviation must |
|---|
| 503 |
# be listed in Repl instead of in the appropriate Xabb values. This |
|---|
| 504 |
# only applies to abbreviations which are substrings of other values |
|---|
| 505 |
# (so there is no confusion between Mn and Month). |
|---|
| 506 |
|
|---|
| 507 |
&Date_InitStrings($lang{"years"} ,\$Lang{$L}{"Yabb"}, "lc,sort"); |
|---|
| 508 |
&Date_InitStrings($lang{"months"} ,\$Lang{$L}{"Mabb"}, "lc,sort"); |
|---|
| 509 |
&Date_InitStrings($lang{"weeks"} ,\$Lang{$L}{"Wabb"}, "lc,sort"); |
|---|
| 510 |
&Date_InitStrings($lang{"days"} ,\$Lang{$L}{"Dabb"}, "lc,sort"); |
|---|
| 511 |
&Date_InitStrings($lang{"hours"} ,\$Lang{$L}{"Habb"}, "lc,sort"); |
|---|
| 512 |
&Date_InitStrings($lang{"minutes"},\$Lang{$L}{"MNabb"},"lc,sort"); |
|---|
| 513 |
&Date_InitStrings($lang{"seconds"},\$Lang{$L}{"Sabb"}, "lc,sort"); |
|---|
| 514 |
$Lang{$L}{"Repl"}={}; |
|---|
| 515 |
&Date_InitHash($lang{"replace"},undef,"lc",$Lang{$L}{"Repl"}); |
|---|
| 516 |
|
|---|
| 517 |
# variables for special dates that are offsets from now |
|---|
| 518 |
# Now = "now" |
|---|
| 519 |
# Today = "today" |
|---|
| 520 |
# Offset = "(yesterday|tomorrow)" |
|---|
| 521 |
# OffsetH = { "yesterday"=>"-0:0:0:1:0:0:0",... ] |
|---|
| 522 |
# Times = "(noon|midnight)" |
|---|
| 523 |
# TimesH = { "noon"=>"12:00:00","midnight"=>"00:00:00" } |
|---|
| 524 |
# SepHM = hour/minute separator |
|---|
| 525 |
# SepMS = minute/second separator |
|---|
| 526 |
# SepSS = second/fraction separator |
|---|
| 527 |
|
|---|
| 528 |
$Lang{$L}{"TimesH"}={}; |
|---|
| 529 |
&Date_InitHash($lang{"times"}, |
|---|
| 530 |
\$Lang{$L}{"Times"},"lc,sort,back", |
|---|
| 531 |
$Lang{$L}{"TimesH"}); |
|---|
| 532 |
&Date_InitStrings($lang{"now"},\$Lang{$L}{"Now"},"lc,sort"); |
|---|
| 533 |
&Date_InitStrings($lang{"today"},\$Lang{$L}{"Today"},"lc,sort"); |
|---|
| 534 |
$Lang{$L}{"OffsetH"}={}; |
|---|
| 535 |
&Date_InitHash($lang{"offset"}, |
|---|
| 536 |
\$Lang{$L}{"Offset"},"lc,sort,back", |
|---|
| 537 |
$Lang{$L}{"OffsetH"}); |
|---|
| 538 |
$Lang{$L}{"SepHM"}=$lang{"sephm"}; |
|---|
| 539 |
$Lang{$L}{"SepMS"}=$lang{"sepms"}; |
|---|
| 540 |
$Lang{$L}{"SepSS"}=$lang{"sepss"}; |
|---|
| 541 |
|
|---|
| 542 |
# variables for time zones |
|---|
| 543 |
# zones = regular expression with all zone names (EST) |
|---|
| 544 |
# n2o = a hash of all parsable zone names with their offsets |
|---|
| 545 |
# tzones = reguar expression with all tzdata timezones (US/Eastern) |
|---|
| 546 |
# tz2z = hash of all tzdata timezones to full timezone (EST#EDT) |
|---|
| 547 |
|
|---|
| 548 |
$zonesrfc= |
|---|
| 549 |
"idlw -1200 ". # International Date Line West |
|---|
| 550 |
"nt -1100 ". # Nome |
|---|
| 551 |
"hst -1000 ". # Hawaii Standard |
|---|
| 552 |
"cat -1000 ". # Central Alaska |
|---|
| 553 |
"ahst -1000 ". # Alaska-Hawaii Standard |
|---|
| 554 |
"akst -0900 ". # Alaska Standard |
|---|
| 555 |
"yst -0900 ". # Yukon Standard |
|---|
| 556 |
"hdt -0900 ". # Hawaii Daylight |
|---|
| 557 |
"akdt -0800 ". # Alaska Daylight |
|---|
| 558 |
"ydt -0800 ". # Yukon Daylight |
|---|
| 559 |
"pst -0800 ". # Pacific Standard |
|---|
| 560 |
"pdt -0700 ". # Pacific Daylight |
|---|
| 561 |
"mst -0700 ". # Mountain Standard |
|---|
| 562 |
"mdt -0600 ". # Mountain Daylight |
|---|
| 563 |
"cst -0600 ". # Central Standard |
|---|
| 564 |
"cdt -0500 ". # Central Daylight |
|---|
| 565 |
"est -0500 ". # Eastern Standard |
|---|
| 566 |
"act -0500 ". # Brazil, Acre |
|---|
| 567 |
"sat -0400 ". # Chile |
|---|
| 568 |
"clst -0400 ". # Chile Standard |
|---|
| 569 |
"bot -0400 ". # Bolivia |
|---|
| 570 |
"amt -0400 ". # Brazil, Amazon |
|---|
| 571 |
"acst -0400 ". # Brazil, Acre Daylight |
|---|
| 572 |
"edt -0400 ". # Eastern Daylight |
|---|
| 573 |
"ast -0400 ". # Atlantic Standard |
|---|
| 574 |
#"nst -0330 ". # Newfoundland Standard nst=North Sumatra +0630 |
|---|
| 575 |
"nft -0330 ". # Newfoundland |
|---|
| 576 |
#"gst -0300 ". # Greenland Standard gst=Guam Standard +1000 |
|---|
| 577 |
"cldt -0300 ". # Chile Daylight |
|---|
| 578 |
#"bst -0300 ". # Brazil Standard bst=British Summer +0100 |
|---|
| 579 |
"brt -0300 ". # Brazil Standard (official time) |
|---|
| 580 |
#"brst -0300 ". # Brazil Standard |
|---|
| 581 |
"adt -0300 ". # Atlantic Daylight |
|---|
| 582 |
"art -0300 ". # Argentina |
|---|
| 583 |
"amst -0300 ". # Brazil, Amazon Daylight |
|---|
| 584 |
"uyt -0300 ". # Uruguay |
|---|
| 585 |
"ndt -0230 ". # Newfoundland Daylight |
|---|
| 586 |
"brst -0200 ". # Brazil Daylight (official time) |
|---|
| 587 |
"fnt -0200 ". # Brazil, Fernando de Noronha |
|---|
| 588 |
"at -0200 ". # Azores |
|---|
| 589 |
"wat -0100 ". # West Africa |
|---|
| 590 |
"fnst -0100 ". # Brazil, Fernando de Noronha Daylight |
|---|
| 591 |
"gmt +0000 ". # Greenwich Mean |
|---|
| 592 |
"ut +0000 ". # Universal |
|---|
| 593 |
"utc +0000 ". # Universal (Coordinated) |
|---|
| 594 |
"wet +0000 ". # Western European |
|---|
| 595 |
"cet +0100 ". # Central European |
|---|
| 596 |
"fwt +0100 ". # French Winter |
|---|
| 597 |
"met +0100 ". # Middle European |
|---|
| 598 |
"mez +0100 ". # Middle European |
|---|
| 599 |
"mewt +0100 ". # Middle European Winter |
|---|
| 600 |
"swt +0100 ". # Swedish Winter |
|---|
| 601 |
"bst +0100 ". # British Summer bst=Brazil standard -0300 |
|---|
| 602 |
"gb +0100 ". # GMT with daylight savings |
|---|
| 603 |
"west +0000 ". # Western European Daylight |
|---|
| 604 |
"eet +0200 ". # Eastern Europe, USSR Zone 1 |
|---|
| 605 |
"cest +0200 ". # Central European Summer |
|---|
| 606 |
"fst +0200 ". # French Summer |
|---|
| 607 |
"ist +0200 ". # Israel standard |
|---|
| 608 |
"mest +0200 ". # Middle European Summer |
|---|
| 609 |
"mesz +0200 ". # Middle European Summer |
|---|
| 610 |
"metdst +0200 ". # An alias for mest used by HP-UX |
|---|
| 611 |
"sast +0200 ". # South African Standard |
|---|
| 612 |
"sst +0200 ". # Swedish Summer sst=South Sumatra +0700 |
|---|
| 613 |
"bt +0300 ". # Baghdad, USSR Zone 2 |
|---|
| 614 |
"eest +0300 ". # Eastern Europe Summer |
|---|
| 615 |
"eetedt +0300 ". # Eastern Europe, USSR Zone 1 |
|---|
| 616 |
"idt +0300 ". # Israel Daylight |
|---|
| 617 |
"msk +0300 ". # Moscow |
|---|
| 618 |
"eat +0300 ". # East Africa |
|---|
| 619 |
"it +0330 ". # Iran |
|---|
| 620 |
"zp4 +0400 ". # USSR Zone 3 |
|---|
| 621 |
"msd +0400 ". # Moscow Daylight |
|---|
| 622 |
"zp5 +0500 ". # USSR Zone 4 |
|---|
| 623 |
"ist +0530 ". # Indian Standard |
|---|
| 624 |
"zp6 +0600 ". # USSR Zone 5 |
|---|
| 625 |
"novst +0600 ". # Novosibirsk time zone, Russia |
|---|
| 626 |
"nst +0630 ". # North Sumatra nst=Newfoundland Std -0330 |
|---|
| 627 |
#"sst +0700 ". # South Sumatra, USSR Zone 6 sst=Swedish Summer +0200 |
|---|
| 628 |
"javt +0700 ". # Java |
|---|
| 629 |
"ict +0700 ". # Indo China Time |
|---|
| 630 |
"hkt +0800 ". # Hong Kong |
|---|
| 631 |
"sgt +0800 ". # Singapore |
|---|
| 632 |
"cct +0800 ". # China Coast, USSR Zone 7 |
|---|
| 633 |
"awst +0800 ". # Australian Western Standard |
|---|
| 634 |
"wst +0800 ". # West Australian Standard |
|---|
| 635 |
"pht +0800 ". # Asia Manila |
|---|
| 636 |
"kst +0900 ". # Republic of Korea |
|---|
| 637 |
"jst +0900 ". # Japan Standard, USSR Zone 8 |
|---|
| 638 |
"rok +0900 ". # Republic of Korea |
|---|
| 639 |
"acst +0930 ". # Australian Central Standard |
|---|
| 640 |
"cast +0930 ". # Central Australian Standard |
|---|
| 641 |
"aest +1000 ". # Australian Eastern Standard |
|---|
| 642 |
"east +1000 ". # Eastern Australian Standard |
|---|
| 643 |
"gst +1000 ". # Guam Standard, USSR Zone 9 gst=Greenland Std -0300 |
|---|
| 644 |
"chst +1000 ". # Guam Standard, USSR Zone 9 gst=Greenland Std -0300 |
|---|
| 645 |
"acdt +1030 ". # Australian Central Daylight |
|---|
| 646 |
"cadt +1030 ". # Central Australian Daylight |
|---|
| 647 |
"aedt +1100 ". # Australian Eastern Daylight |
|---|
| 648 |
"eadt +1100 ". # Eastern Australian Daylight |
|---|
| 649 |
"idle +1200 ". # International Date Line East |
|---|
| 650 |
"nzst +1200 ". # New Zealand Standard |
|---|
| 651 |
"nzt +1200 ". # New Zealand |
|---|
| 652 |
"nzdt +1300 ". # New Zealand Daylight |
|---|
| 653 |
"z +0000 ". |
|---|
| 654 |
"a +0100 b +0200 c +0300 d +0400 e +0500 f +0600 g +0700 h +0800 ". |
|---|
| 655 |
"i +0900 k +1000 l +1100 m +1200 ". |
|---|
| 656 |
"n -0100 o -0200 p -0300 q -0400 r -0500 s -0600 t -0700 u -0800 ". |
|---|
| 657 |
"v -0900 w -1000 x -1100 y -1200"; |
|---|
| 658 |
|
|---|
| 659 |
$Zone{"n2o"} = {}; |
|---|
| 660 |
($Zone{"zones"},%{ $Zone{"n2o"} })= |
|---|
| 661 |
&Date_Regexp($zonesrfc,"sort,lc,under,back", |
|---|
| 662 |
"keys"); |
|---|
| 663 |
|
|---|
| 664 |
$tmp= |
|---|
| 665 |
"US/Pacific PST8PDT ". |
|---|
| 666 |
"US/Mountain MST7MDT ". |
|---|
| 667 |
"US/Central CST6CDT ". |
|---|
| 668 |
"US/Eastern EST5EDT ". |
|---|
| 669 |
"Canada/Pacific PST8PDT ". |
|---|
| 670 |
"Canada/Mountain MST7MDT ". |
|---|
| 671 |
"Canada/Central CST6CDT ". |
|---|
| 672 |
"Canada/Eastern EST5EDT"; |
|---|
| 673 |
|
|---|
| 674 |
$Zone{"tz2z"} = {}; |
|---|
| 675 |
($Zone{"tzones"},%{ $Zone{"tz2z"} })= |
|---|
| 676 |
&Date_Regexp($tmp,"lc,under,back","keys"); |
|---|
| 677 |
$Cnf{"TZ"}=&Date_TimeZone; |
|---|
| 678 |
|
|---|
| 679 |
# misc. variables |
|---|
| 680 |
# At = "(?:at)" |
|---|
| 681 |
# Of = "(?:in|of)" |
|---|
| 682 |
# On = "(?:on)" |
|---|
| 683 |
# Future = "(?:in)" |
|---|
| 684 |
# Later = "(?:later)" |
|---|
| 685 |
# Past = "(?:ago)" |
|---|
| 686 |
# Next = "(?:next)" |
|---|
| 687 |
# Prev = "(?:last|previous)" |
|---|
| 688 |
|
|---|
| 689 |
&Date_InitStrings($lang{"at"}, \$Lang{$L}{"At"}, "lc,sort"); |
|---|
| 690 |
&Date_InitStrings($lang{"on"}, \$Lang{$L}{"On"}, "lc,sort"); |
|---|
| 691 |
&Date_InitStrings($lang{"future"},\$Lang{$L}{"Future"}, "lc,sort"); |
|---|
| 692 |
&Date_InitStrings($lang{"later"}, \$Lang{$L}{"Later"}, "lc,sort"); |
|---|
| 693 |
&Date_InitStrings($lang{"past"}, \$Lang{$L}{"Past"}, "lc,sort"); |
|---|
| 694 |
&Date_InitStrings($lang{"next"}, \$Lang{$L}{"Next"}, "lc,sort"); |
|---|
| 695 |
&Date_InitStrings($lang{"prev"}, \$Lang{$L}{"Prev"}, "lc,sort"); |
|---|
| 696 |
&Date_InitStrings($lang{"of"}, \$Lang{$L}{"Of"}, "lc,sort"); |
|---|
| 697 |
|
|---|
| 698 |
# calc mode variables |
|---|
| 699 |
# Approx = "(?:approximately)" |
|---|
| 700 |
# Exact = "(?:exactly)" |
|---|
| 701 |
# Business = "(?:business)" |
|---|
| 702 |
|
|---|
| 703 |
&Date_InitStrings($lang{"exact"}, \$Lang{$L}{"Exact"}, "lc,sort"); |
|---|
| 704 |
&Date_InitStrings($lang{"approx"}, \$Lang{$L}{"Approx"}, "lc,sort"); |
|---|
| 705 |
&Date_InitStrings($lang{"business"},\$Lang{$L}{"Business"},"lc,sort"); |
|---|
| 706 |
|
|---|
| 707 |
############### END OF LANGUAGE INITIALIZATION |
|---|
| 708 |
} |
|---|
| 709 |
|
|---|
| 710 |
if ($Curr{"ResetWorkDay"}) { |
|---|
| 711 |
my($h1,$m1,$h2,$m2)=(); |
|---|
| 712 |
if ($Cnf{"WorkDay24Hr"}) { |
|---|
| 713 |
($Curr{"WDBh"},$Curr{"WDBm"})=(0,0); |
|---|
| 714 |
($Curr{"WDEh"},$Curr{"WDEm"})=(24,0); |
|---|
| 715 |
$Curr{"WDlen"}=24*60; |
|---|
| 716 |
$Cnf{"WorkDayBeg"}="00:00"; |
|---|
| 717 |
$Cnf{"WorkDayEnd"}="23:59"; |
|---|
| 718 |
|
|---|
| 719 |
} else { |
|---|
| 720 |
confess "ERROR: Invalid WorkDayBeg in Date::Manip.\n" |
|---|
| 721 |
if (! (($h1,$m1)=&CheckTime($Cnf{"WorkDayBeg"}))); |
|---|
| 722 |
$Cnf{"WorkDayBeg"}="$h1:$m1"; |
|---|
| 723 |
confess "ERROR: Invalid WorkDayEnd in Date::Manip.\n" |
|---|
| 724 |
if (! (($h2,$m2)=&CheckTime($Cnf{"WorkDayEnd"}))); |
|---|
| 725 |
$Cnf{"WorkDayEnd"}="$h2:$m2"; |
|---|
| 726 |
|
|---|
| 727 |
($Curr{"WDBh"},$Curr{"WDBm"})=($h1,$m1); |
|---|
| 728 |
($Curr{"WDEh"},$Curr{"WDEm"})=($h2,$m2); |
|---|
| 729 |
|
|---|
| 730 |
# Work day length = h1:m1 or 0:len (len minutes) |
|---|
| 731 |
$h1=$h2-$h1; |
|---|
| 732 |
$m1=$m2-$m1; |
|---|
| 733 |
if ($m1<0) { |
|---|
| 734 |
$h1--; |
|---|
| 735 |
$m1+=60; |
|---|
| 736 |
} |
|---|
| 737 |
$Curr{"WDlen"}=$h1*60+$m1; |
|---|
| 738 |
} |
|---|
| 739 |
$Curr{"ResetWorkDay"}=0; |
|---|
| 740 |
} |
|---|
| 741 |
|
|---|
| 742 |
# current time |
|---|
| 743 |
my($s,$mn,$h,$d,$m,$y,$wday,$yday,$isdst,$ampm,$wk)=(); |
|---|
| 744 |
if ($Cnf{"ForceDate"}=~ |
|---|
| 745 |
/^(\d{4})-(\d{2})-(\d{2})-(\d{2}):(\d{2}):(\d{2})$/) { |
|---|
| 746 |
($y,$m,$d,$h,$mn,$s)=($1,$2,$3,$4,$5,$6); |
|---|
| 747 |
} else { |
|---|
| 748 |
($s,$mn,$h,$d,$m,$y,$wday,$yday,$isdst)=localtime(time); |
|---|
| 749 |
$y+=1900; |
|---|
| 750 |
$m++; |
|---|
| 751 |
} |
|---|
| 752 |
&Date_DateCheck(\$y,\$m,\$d,\$h,\$mn,\$s,\$ampm,\$wk); |
|---|
| 753 |
$Curr{"Y"}=$y; |
|---|
| 754 |
$Curr{"M"}=$m; |
|---|
| 755 |
$Curr{"D"}=$d; |
|---|
| 756 |
$Curr{"H"}=$h; |
|---|
| 757 |
$Curr{"Mn"}=$mn; |
|---|
| 758 |
$Curr{"S"}=$s; |
|---|
| 759 |
$Curr{"AmPm"}=$ampm; |
|---|
| 760 |
$Curr{"Now"}=&Date_Join($y,$m,$d,$h,$mn,$s); |
|---|
| 761 |
if ($Cnf{"TodayIsMidnight"}) { |
|---|
| 762 |
$Curr{"Today"}=&Date_Join($y,$m,$d,0,0,0); |
|---|
| 763 |
} else { |
|---|
| 764 |
$Curr{"Today"}=$Curr{"Now"}; |
|---|
| 765 |
} |
|---|
| 766 |
|
|---|
| 767 |
$Curr{"Debug"}=$Curr{"DebugVal"}; |
|---|
| 768 |
|
|---|
| 769 |
# If we're in array context, let's return a list of config variables |
|---|
| 770 |
# that could be passed to Date_Init to get the same state as we're |
|---|
| 771 |
# currently in. |
|---|
| 772 |
if (wantarray) { |
|---|
| 773 |
# Some special variables that have to be in a specific order |
|---|
| 774 |
my(@special)=qw(IgnoreGlobalCnf GlobalCnf PersonalCnf PersonalCnfPath); |
|---|
| 775 |
my(%tmp)=map { $_,1 } @special; |
|---|
| 776 |
my(@tmp,$key,$val); |
|---|
| 777 |
foreach $key (@special) { |
|---|
| 778 |
$val=$Cnf{$key}; |
|---|
| 779 |
push(@tmp,"$key=$val"); |
|---|
| 780 |
} |
|---|
| 781 |
foreach $key (keys %Cnf) { |
|---|
| 782 |
next if (exists $tmp{$key}); |
|---|
| 783 |
$val=$Cnf{$key}; |
|---|
| 784 |
push(@tmp,"$key=$val"); |
|---|
| 785 |
} |
|---|
| 786 |
return @tmp; |
|---|
| 787 |
} |
|---|
| 788 |
return (); |
|---|
| 789 |
} |
|---|
| 790 |
|
|---|
| 791 |
sub ParseDateString { |
|---|
| 792 |
print "DEBUG: ParseDateString\n" if ($Curr{"Debug"} =~ /trace/); |
|---|
| 793 |
local($_)=@_; |
|---|
| 794 |
return "" if (! $_); |
|---|
| 795 |
|
|---|
| 796 |
my($y,$m,$d,$h,$mn,$s,$i,$wofm,$dofw,$wk,$tmp,$z,$num,$err,$iso,$ampm)=(); |
|---|
| 797 |
my($date,$z2,$delta,$from,$falsefrom,$to,$which,$midnight)=(); |
|---|
| 798 |
|
|---|
| 799 |
# We only need to reinitialize if we have to determine what NOW is. |
|---|
| 800 |
&Date_Init() if (! $Curr{"InitDone"} or $Cnf{"UpdateCurrTZ"}); |
|---|
| 801 |
|
|---|
| 802 |
my($L)=$Cnf{"Language"}; |
|---|
| 803 |
my($type)=$Cnf{"DateFormat"}; |
|---|
| 804 |
|
|---|
| 805 |
# Mode is set in DateCalc. ParseDate only overrides it if the string |
|---|
| 806 |
# contains a mode. |
|---|
| 807 |
if ($Lang{$L}{"Exact"} && |
|---|
| 808 |
s/$Lang{$L}{"Exact"}//) { |
|---|
| 809 |
$Curr{"Mode"}=0; |
|---|
| 810 |
} elsif ($Lang{$L}{"Approx"} && |
|---|
| 811 |
s/$Lang{$L}{"Approx"}//) { |
|---|
| 812 |
$Curr{"Mode"}=1; |
|---|
| 813 |
} elsif ($Lang{$L}{"Business"} && |
|---|
| 814 |
s/$Lang{$L}{"Business"}//) { |
|---|
| 815 |
$Curr{"Mode"}=2; |
|---|
| 816 |
} elsif (! exists $Curr{"Mode"}) { |
|---|
| 817 |
$Curr{"Mode"}=0; |
|---|
| 818 |
} |
|---|
| 819 |
|
|---|
| 820 |
# Unfortunately, some deltas can be parsed as dates. An example is |
|---|
| 821 |
# 1 second == 1 2nd == 1 2 |
|---|
| 822 |
# But, some dates can be parsed as deltas. The most important being: |
|---|
| 823 |
# 1998010101:00:00 |
|---|
| 824 |
# |
|---|
| 825 |
# We'll check to see if a "date" can be parsed as a delta. If so, we'll |
|---|
| 826 |
# assume that it is a delta (since they are much simpler, it is much |
|---|
| 827 |
# less likely that we'll mistake a delta for a date than vice versa) |
|---|
| 828 |
# unless it is an ISO-8601 date. |
|---|
| 829 |
# |
|---|
| 830 |
# This is important because we are using DateCalc to test whether a |
|---|
| 831 |
# string is a date or a delta. Dates are tested first, so we need to |
|---|
| 832 |
# be able to pass a delta into this routine and have it correctly NOT |
|---|
| 833 |
# interpreted as a date. |
|---|
| 834 |
# |
|---|
| 835 |
# We will insist that the string contain something other than digits and |
|---|
| 836 |
# colons so that the following will get correctly interpreted as a date |
|---|
| 837 |
# rather than a delta: |
|---|
| 838 |
# 12:30 |
|---|
| 839 |
# 19980101 |
|---|
| 840 |
|
|---|
| 841 |
$delta=""; |
|---|
| 842 |
$delta=&ParseDateDelta($_) if (/[^:0-9]/); |
|---|
| 843 |
|
|---|
| 844 |
# Put parse in a simple loop for an easy exit. |
|---|
| 845 |
PARSE: { |
|---|
| 846 |
my(@tmp)=&Date_Split($_); |
|---|
| 847 |
if (@tmp) { |
|---|
| 848 |
($y,$m,$d,$h,$mn,$s)=@tmp; |
|---|
| 849 |
last PARSE; |
|---|
| 850 |
} |
|---|
| 851 |
|
|---|
| 852 |
# Fundamental regular expressions |
|---|
| 853 |
|
|---|
| 854 |
my($month)=$Lang{$L}{"Month"}; # (jan|january|...) |
|---|
| 855 |
my(%month)=%{ $Lang{$L}{"MonthH"} }; # { jan=>1, ... } |
|---|
| 856 |
my($week)=$Lang{$L}{"Week"}; # (mon|monday|...) |
|---|
| 857 |
my(%week)=%{ $Lang{$L}{"WeekH"} }; # { mon=>1, monday=>1, ... } |
|---|
| 858 |
my($wom)=$Lang{$L}{"WoM"}; # (1st|...|fifth|last) |
|---|
| 859 |
my(%wom)=%{ $Lang{$L}{"WoMH"} }; # { 1st=>1,... fifth=>5,last=>-1 } |
|---|
| 860 |
my($dom)=$Lang{$L}{"DoM"}; # (1st|first|...31st) |
|---|
| 861 |
my(%dom)=%{ $Lang{$L}{"DoMH"} }; # { 1st=>1, first=>1, ... } |
|---|
| 862 |
my($ampmexp)=$Lang{$L}{"AmPm"}; # (am|pm) |
|---|
| 863 |
my($timeexp)=$Lang{$L}{"Times"}; # (noon|midnight) |
|---|
| 864 |
my($now)=$Lang{$L}{"Now"}; # now |
|---|
| 865 |
my($today)=$Lang{$L}{"Today"}; # today |
|---|
| 866 |
my($offset)=$Lang{$L}{"Offset"}; # (yesterday|tomorrow) |
|---|
| 867 |
my($zone)=$Zone{"zones"}; # (edt|est|...) |
|---|
| 868 |
my($day)='\s*'.$Lang{$L}{"Dabb"}; # \s*(?:d|day|days) |
|---|
| 869 |
my($mabb)='\s*'.$Lang{$L}{"Mabb"}; # \s*(?:mon|month|months) |
|---|
| 870 |
my($wkabb)='\s*'.$Lang{$L}{"Wabb"}; # \s*(?:w|wk|week|weeks) |
|---|
| 871 |
my($next)='\s*'.$Lang{$L}{"Next"}; # \s*(?:next) |
|---|
| 872 |
my($prev)='\s*'.$Lang{$L}{"Prev"}; # \s*(?:last|previous) |
|---|
| 873 |
my($past)='\s*'.$Lang{$L}{"Past"}; # \s*(?:ago) |
|---|
| 874 |
my($future)='\s*'.$Lang{$L}{"Future"}; # \s*(?:in) |
|---|
| 875 |
my($later)='\s*'.$Lang{$L}{"Later"}; # \s*(?:later) |
|---|
| 876 |
my($at)=$Lang{$L}{"At"}; # (?:at) |
|---|
| 877 |
my($of)='\s*'.$Lang{$L}{"Of"}; # \s*(?:in|of) |
|---|
| 878 |
my($on)='(?:\s*'.$Lang{$L}{"On"}.'\s*|\s+)'; |
|---|
| 879 |
# \s*(?:on)\s* or \s+ |
|---|
| 880 |
my($last)='\s*'.$Lang{$L}{"Last"}; # \s*(?:last) |
|---|
| 881 |
my($hm)=$Lang{$L}{"SepHM"}; # : |
|---|
| 882 |
my($ms)=$Lang{$L}{"SepMS"}; # : |
|---|
| 883 |
my($ss)=$Lang{$L}{"SepSS"}; # . |
|---|
| 884 |
|
|---|
| 885 |
# Other regular expressions |
|---|
| 886 |
|
|---|
| 887 |
my($D4)='(\d{4})'; # 4 digits (yr) |
|---|
| 888 |
my($YY)='(\d{4}|\d{2})'; # 2 or 4 digits (yr) |
|---|
| 889 |
my($DD)='(\d{2})'; # 2 digits (mon/day/hr/min/sec) |
|---|
| 890 |
my($D) ='(\d{1,2})'; # 1 or 2 digit (mon/day/hr) |
|---|
| 891 |
my($FS)="(?:$ss\\d+)?"; # fractional secs |
|---|
| 892 |
my($sep)='[\/.-]'; # non-ISO8601 m/d/yy separators |
|---|
| 893 |
# absolute time zone +0700 (GMT) |
|---|
| 894 |
my($hzone)='(?:[0-1][0-9]|2[0-3])'; # 00 - 23 |
|---|
| 895 |
my($mzone)='(?:[0-5][0-9])'; # 00 - 59 |
|---|
| 896 |
my($zone2)='(?:\s*([+-](?:'."$hzone$mzone|$hzone:$mzone|$hzone))". |
|---|
| 897 |
# +0700 +07:00 -07 |
|---|
| 898 |
'(?:\s*\([^)]+\))?)'; # (GMT) |
|---|
| 899 |
|
|---|
| 900 |
# A regular expression for the time EXCEPT for the hour part |
|---|
| 901 |
my($mnsec)="$hm$DD(?:$ms$DD$FS)?(?:\\s*$ampmexp)?"; |
|---|
| 902 |
|
|---|
| 903 |
# A special regular expression for /YYYY:HH:MN:SS used by Apache |
|---|
| 904 |
my($apachetime)='(/\d{4}):' . "$DD$hm$DD$ms$DD"; |
|---|
| 905 |
|
|---|
| 906 |
my($time)=""; |
|---|
| 907 |
$ampm=""; |
|---|
| 908 |
$date=""; |
|---|
| 909 |
|
|---|
| 910 |
# Substitute all special time expressions. |
|---|
| 911 |
if (/(^|[^a-z])$timeexp($|[^a-z])/i) { |
|---|
| 912 |
$tmp=$2; |
|---|
| 913 |
$tmp=$Lang{$L}{"TimesH"}{lc($tmp)}; |
|---|
| 914 |
s/(^|[^a-z])$timeexp($|[^a-z])/$1 $tmp $3/i; |
|---|
| 915 |
} |
|---|
| 916 |
|
|---|
| 917 |
# Remove some punctuation |
|---|
| 918 |
s/[,]/ /g; |
|---|
| 919 |
|
|---|
| 920 |
# When we have a digit followed immediately by a timezone (7EST), we |
|---|
| 921 |
# will put a space between the digit, EXCEPT in the case of a single |
|---|
| 922 |
# character military timezone. If the single character is followed |
|---|
| 923 |
# by anything, no space is added. |
|---|
| 924 |
$tmp = ""; |
|---|
| 925 |
while ( s/^(.*?\d)$zone(\s|$|[0-9])/$3/i ) { |
|---|
| 926 |
my($bef,$z,$aft) = ($1,$2,$3); |
|---|
| 927 |
if (length($z) != 1 || length($aft) == 0) { |
|---|
| 928 |
$tmp .= "$bef $z"; |
|---|
| 929 |
} else { |
|---|
| 930 |
$tmp .= "$bef$z"; |
|---|
| 931 |
} |
|---|
| 932 |
} |
|---|
| 933 |
$_ = "$tmp$_"; |
|---|
| 934 |
$zone = '\s+' . $zone . '(?:\s+|$)'; |
|---|
| 935 |
|
|---|
| 936 |
# Remove the time |
|---|
| 937 |
$iso=1; |
|---|
| 938 |
$midnight=0; |
|---|
| 939 |
$from="24${hm}00(?:${ms}00)?"; |
|---|
| 940 |
$falsefrom="${hm}24${ms}00"; # Don't trap XX:24:00 |
|---|
| 941 |
$to="00${hm}00${ms}00"; |
|---|
| 942 |
$midnight=1 if (!/$falsefrom/ && s/$from/$to/); |
|---|
| 943 |
|
|---|
| 944 |
$h=$mn=$s=0; |
|---|
| 945 |
if (/$D$mnsec/i || /$ampmexp/i) { |
|---|
| 946 |
$iso=0; |
|---|
| 947 |
$tmp=0; |
|---|
| 948 |
$tmp=1 if (/$mnsec$zone2?\s*$/i or /$mnsec$zone\s*$/i); |
|---|
| 949 |
$tmp=0 if (/$ampmexp/i); |
|---|
| 950 |
if (s/$apachetime$zone()/$1 /i || |
|---|
| 951 |
s/$apachetime$zone2?/$1 /i || |
|---|
| 952 |
s/(^|[^a-z])$at\s*$D$mnsec$zone()/$1 /i || |
|---|
| 953 |
s/(^|[^a-z])$at\s*$D$mnsec$zone2?/$1 /i || |
|---|
| 954 |
s/(^|[^0-9])(\d)$mnsec$zone()/$1 /i || |
|---|
| 955 |
s/(^|[^0-9])(\d)$mnsec$zone2?/$1 /i || |
|---|
| 956 |
(s/(t)$D$mnsec$zone()/$1 /i and (($iso=$tmp) || 1)) || |
|---|
| 957 |
(s/(t)$D$mnsec$zone2?/$1 /i and (($iso=$tmp) || 1)) || |
|---|
| 958 |
(s/()$DD$mnsec$zone()/ /i and (($iso=$tmp) || 1)) || |
|---|
| 959 |
(s/()$DD$mnsec$zone2?/ /i and (($iso=$tmp) || 1)) || |
|---|
| 960 |
s/(^|$at\s*|\s+)$D()()\s*$ampmexp$zone()/ /i || |
|---|
| 961 |
s/(^|$at\s*|\s+)$D()()\s*$ampmexp$zone2?/ /i || |
|---|
| 962 |
0 |
|---|
| 963 |
) { |
|---|
| 964 |
($h,$mn,$s,$ampm,$z,$z2)=($2,$3,$4,$5,$6,$7); |
|---|
| 965 |
if (defined ($z)) { |
|---|
| 966 |
if ($z =~ /^[+-]\d{2}:\d{2}$/) { |
|---|
| 967 |
$z=~ s/://; |
|---|
| 968 |
} elsif ($z =~ /^[+-]\d{2}$/) { |
|---|
| 969 |
$z .= "00"; |
|---|
| 970 |
} |
|---|
| 971 |
} |
|---|
| 972 |
$time=1; |
|---|
| 973 |
&Date_TimeCheck(\$h,\$mn,\$s,\$ampm); |
|---|
| 974 |
$y=$m=$d=""; |
|---|
| 975 |
# We're going to be calling TimeCheck again below (when we check the |
|---|
| 976 |
# final date), so get rid of $ampm so that we don't have an error |
|---|
| 977 |
# due to "15:30:00 PM". It'll get reset below. |
|---|
| 978 |
$ampm=""; |
|---|
| 979 |
if (/^\s*$/) { |
|---|
| 980 |
&Date_Init() if (! $Cnf{"UpdateCurrTZ"}); |
|---|
| 981 |
last PARSE; |
|---|
| 982 |
} |
|---|
| 983 |
} |
|---|
| 984 |
} |
|---|
| 985 |
$time=0 if ($time ne "1"); |
|---|
| 986 |
s/\s+$//; |
|---|
| 987 |
s/^\s+//; |
|---|
| 988 |
|
|---|
| 989 |
# if a zone was found, get rid of the regexps |
|---|
| 990 |
if ($z) { |
|---|
| 991 |
$zone=""; |
|---|
| 992 |
$zone2=""; |
|---|
| 993 |
} |
|---|
| 994 |
|
|---|
| 995 |
# dateTtime ISO 8601 formats |
|---|
| 996 |
my($orig)=$_; |
|---|
| 997 |
|
|---|
| 998 |
# Parse ISO 8601 dates now (which may still have a zone stuck to it). |
|---|
| 999 |
if ( ($iso && /^([0-9-]+(?:W[0-9-]+)?)$zone?$/i) || |
|---|
| 1000 |
($iso && /^([0-9-]+(?:W[0-9-]+)?)$zone2?$/i) || |
|---|
| 1001 |
($iso && /^([0-9-]+(?:T[0-9-]+)?)$zone?$/i) || |
|---|
| 1002 |
($iso && /^([0-9-]+(?:T[0-9-]+)?)$zone2?$/i) || |
|---|
| 1003 |
($iso && /^([0-9-]+T)$zone?$/i) || |
|---|
| 1004 |
($iso && /^([0-9-]+T)$zone2?$/i) || |
|---|
| 1005 |
0) { |
|---|
| 1006 |
|
|---|
| 1007 |
# If we already got a timezone, don't get another one. |
|---|
| 1008 |
my(@z); |
|---|
| 1009 |
if ($z) { |
|---|
| 1010 |
@z=($z,$z2); |
|---|
| 1011 |
$z=""; |
|---|
| 1012 |
} |
|---|
| 1013 |
($_,$z,$z2) = ($1,$2,$3); |
|---|
| 1014 |
($z,$z2)=@z if (@z); |
|---|
| 1015 |
|
|---|
| 1016 |
s,([0-9])\s*-,$1 ,g; # Change all ISO8601 seps to spaces |
|---|
| 1017 |
s/^\s+//; |
|---|
| 1018 |
s/\s+$//; |
|---|
| 1019 |
|
|---|
| 1020 |
if (/^$D4\s*$DD\s*$DD\s*t?$DD(?:$DD(?:$DD(\d*))?)?$/i || |
|---|
| 1021 |
/^$DD\s+$DD\s*$DD\s*t?$DD(?:$DD(?:$DD(\d*))?)?$/i || |
|---|
| 1022 |
0 |
|---|
| 1023 |
) { |
|---|
| 1024 |
# ISO 8601 Dates with times |
|---|
| 1025 |
# YYYYMMDDtHHMNSSFFFF... |
|---|
| 1026 |
# YYYYMMDDtHHMNSS |
|---|
| 1027 |
# YYYYMMDDtHHMN |
|---|
| 1028 |
# YYYYMMDDtHH |
|---|
| 1029 |
# YY MMDDtHHMNSSFFFF... |
|---|
| 1030 |
# YY MMDDtHHMNSS |
|---|
| 1031 |
# YY MMDDtHHMN |
|---|
| 1032 |
# YY MMDDtHH |
|---|
| 1033 |
# The t is an optional letter "t". |
|---|
| 1034 |
($y,$m,$d,$h,$mn,$s,$tmp)=($1,$2,$3,$4,$5,$6,$7); |
|---|
| 1035 |
if ($h==24 && (! defined $mn || $mn==0) && (! defined $s || $s==0)) { |
|---|
| 1036 |
$h=0; |
|---|
| 1037 |
$midnight=1; |
|---|
| 1038 |
} |
|---|
| 1039 |
$z = "" if (! defined $h); |
|---|
| 1040 |
return "" if ($time && defined $h); |
|---|
| 1041 |
last PARSE; |
|---|
| 1042 |
|
|---|
| 1043 |
} elsif (/^$D4(?:\s*$DD(?:\s*$DD)?)?$/ || |
|---|
| 1044 |
/^$DD(?:\s+$DD(?:\s*$DD)?)?$/) { |
|---|
| 1045 |
# ISO 8601 Dates |
|---|
| 1046 |
# YYYYMMDD |
|---|
| 1047 |
# YYYYMM |
|---|
| 1048 |
# YYYY |
|---|
| 1049 |
# YY MMDD |
|---|
| 1050 |
# YY MM |
|---|
| 1051 |
# YY |
|---|
| 1052 |
($y,$m,$d)=($1,$2,$3); |
|---|
| 1053 |
last PARSE; |
|---|
| 1054 |
|
|---|
| 1055 |
} elsif (/^$YY\s+$D\s+$D/) { |
|---|
| 1056 |
# YY-M-D |
|---|
| 1057 |
($y,$m,$d)=($1,$2,$3); |
|---|
| 1058 |
last PARSE; |
|---|
| 1059 |
|
|---|
| 1060 |
} elsif (/^$YY\s*W$DD\s*(\d)?$/i) { |
|---|
| 1061 |
# YY-W##-D |
|---|
| 1062 |
($y,$wofm,$dofw)=($1,$2,$3); |
|---|
| 1063 |
($y,$m,$d)=&Date_NthWeekOfYear($y,$wofm,$dofw); |
|---|
| 1064 |
last PARSE; |
|---|
| 1065 |
|
|---|
| 1066 |
} elsif (/^$D4\s*(\d{3})$/ |
|---|