#!/usr/local/bin/perl # # AC.log version 1.0beta build 0003 # Advanced HTTP server log file analyser # Copyright (C) 1999 Christian Treber # For information, see www.ctreber.de/AC.log/ or contact ac.log@ctreber.de # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # version 2 as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Usage: # # aclog(.pl) {} # # Stop when accumulated percentage reaches CUT_OFF percent $CUT_OFF = 100; # Stop when percentage drops below MIN_PERCENT $MIN_PERCENT = 1.0; # Stop at MAX_ITEMS $MAX_ITEMS = 50; # No matter what the stop criteria are, show at least MIN_ITEMS $MIN_ITEMS = 25; # Actions closer together than SESSION_TIMEOUT seconds are # regarded as belonging to one session (1800 = 30 minutes) $SESSION_TIMEOUT = 1800; sub Filter { return($_[0] =~ /\.jpg$|\.gif$|\.png$|\.css$|\.js$|\.swf$|\.xbm$|\.bmp$/); #return 0; } # # No user servicable parts below - admins may proceed at own risk # $MONTH_TRANS = "JanFebMarAprMayJunJulAugSepOctNovDec"; @WEEK_DAY = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"); @DAY_SUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365); %SEARCH_ENGINES = {}; ($basePath) = $0 =~/(.+[\/\\])[^\/\\]*$/; # # Read external information # # Read table of top level domains $tmpFileName = $basePath . "tld.txt"; if(!open(IN, "<$tmpFileName")) { print(STDERR "Can't open top level domain definition file ($tmpFileName)\n"); exit -1; } while() { chop; ($tld, $name) = /(\S+)\s*(.+)/; $tld =~ tr/[A-Z]/[a-z]/; $tldName{$tld} = $name; } close(IN); # Read table of HTTP status messages $tmpFileName = $basePath . "httpStatus.txt"; if(!open(IN, "<$tmpFileName")) { print(STDERR "Can't open HTTP status definition file ($tmpFileName)\n"); exit -1; } while() { chop; ($status, $name) = /(\S+)\s*(.+)/; $httpStatusName{$status} = $name; } close(IN); # # Read log file # $started = time; $logFirst = 99999999999; $logLast = -1; $n = 0; foreach $logFileName(@ARGV) { if(!open(IN, "<$logFileName")) { printf(STDERR "Can't open log file '%s'\n", $logFileName); exit -1; } $lineNr = 0; while() { chop; $n++; $lineNr++; if(($n % 1000) == 0) { $deltaT = time - $started; printf(STDERR "%d (%.3f/sec)\n", $n, $n / (($deltaT > 0) ? $deltaT : 1)); } ($host, $username, $login, $datetime, $cmdline, $code, $size, $rest) = /^(\S+)\s+(\S+)\s+(\S+)\s+\[([^\]]+)\]\s+\"([^\"]+)\"\s+(\S+)\s+(\S+)(.*)$/; if($host eq "") { printf(STDERR "%d (%s): Invalid syntax in '%s'\n", $lineNr, $logFileName, $_); next; } ($referrer, $agent) = $rest =~ /^\s+\"([^\"]+)\"\s+\"([^\"]+)\"$/; if($referrer eq "") { # no combined log format $referrer = "unknown"; $agent = "unknown"; } $bytes += $size; ($day, $month, $year, $hour, $min, $sec, $tzone) = $datetime =~ /(.+)\/(.+)\/(.+):(.+):(.+):(.+)\s+(.+)/; if(($day eq "") || ($year < 1970)) { printf(STDERR "%d (%s): Invalid date/time syntax in '%s'\n", $lineNr, $logFileName, $_); next; } $month = index($MONTH_TRANS, $month) / 3 + 1; $date = sprintf("%04d-%02d-%02d", $year, $month, $day); $time = sprintf("%02d:%02d:%02d", $hour, $min, $sec); $secs = &DateToSecs($day, $month, $year, $hour, $min, $sec); if($secs < $logFirst) { #!!!printf("line:%s datetime:%s day:%s\n", $_, $datetime, $day); $logFirst = $secs; } if($secs > $logLast) { $logLast = $secs; } ($cmd, $url, $protocol) = $cmdline =~ /(\S+)\s+(.+)\s+(\S+)/; if($url =~ /([^\?]+)\?/) { # filter out arguments $url = $1; } ($fileType) = $url =~ /\.([^\.]+)$/; # session stuff if($secs - $lastAccessSecs{$host} < $SESSION_TIMEOUT) { # in same session &RegisterURLchange($url, $host, $secs, 0); } else { # session is new or timed out if($lastAccessSecs{$host} ne "") { # old session ends $duration = $lastAccessSecs{$host} - $firstAccessSecs{$host}; $sessionDuration{$host} += $duration; $sessionDuration += $duration; } # new session begins &RegisterURLchange($url, $host, $secs, 1); $firstAccessSecs{$host} = $secs; $sessionN{$host}++; $nSession++; } $lastAccessSecs{$host} = $secs; # apply filter if(&Filter($url)) { $nRejected++; next; } $nPassed++; # process agent (contains browser, might contain browser version # and platform) eg. Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt) ($product, $platform) = $agent =~ /(.+)\s+\((.+)\)/; if($product eq "") { # no platform information $product = $agent; $platform = "unknown"; } ($browser, $browserVersion) = $product =~ /(.+)\/(.+)/; if($browser eq "") { # no version information $browser = $product; $browserVersion = "unknown"; } # platform might contain browser information $platformVersion = "unknown"; @platform = split(/\s*;\s*/, $platform); undef(@tmp); foreach (@platform) { if(/X11|PPC|^I$|^Nav$|^digext$|compatible/i) { # skip irrelevant stuff next; } if(/MSIE\s+(.+)/i) { # it's MSIE in disguise $browser = "MSIE"; $browserVersion = $1; next; } if(/^Win(.{2})$/) { # short form of windows $_ = "Windows"; $platformVersion = $1; } if(/^(\S+)\s+(.+)/) { $_ = $1; $platformVersion = $2; } push(@tmp, $_); } $platform = join(";", @tmp); # determine domain name if(!($host =~ /^\d+\.\d+\.\d+\.\d+$/)) { # symbolic ($domain) = $host =~ /\.(.+)/; $domain = "*.$domain"; ($tld) = $domain =~ /\.([^\.]+$)/; } else { # numeric ($domain) = $host =~ /(.+)\./; $domain = "$domain.*"; $tld = "*"; } # count $urlN{$url}++; $hostN{$host}++; $hostLast{$host} = $secs; $domainN{$domain}++; $domainLast{$domain} = $secs; $tldN{$tld}++; $fileTypeN{$fileType}++; $fileTypeVolume{$fileType} += $size; $protocolN{$protocol}++; $codeN{$code}++; $browserN{$browser}++; $browserVersionN{$browser}{$browserVersion}++; $platformN{$platform}++; $platformVersionN{$platform}{$platformVersion}++; $referrerN{$referrer}++; $hourN{$hour}++; $weekDayN{&GetWeekDay($secs)}++; $dateN{$date}++; } } $logLast = $secs; if($n == 0) { print(STDERR "No entries found in log files\n"); exit -1; } foreach $host(keys(%lastAccessSecs)) { # terminate all open sessions &RegisterURLchange($url, $host, $secs, 1); $duration = $lastAccessSecs{$host} - $firstAccessSecs{$host}; $sessionDuration{$host} += $duration; } close(IN); $n = $nRejected + $nPassed; # # Print reports # printf(< $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Session entry page by number of sessions\n*\n"); undef(%sort); undef($percSum); $count = 0; $nEntry = 0; foreach $url(keys(%sessionFirstPageN)) { $sortKey = sprintf("%20d %s", $sessionFirstPageN{$url}, $url); $sort{$sortKey} = $url; $nEntry += $sessionFirstPageN{$url}; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $url = $sort{$sortKey}; $perc = $sessionFirstPageN{$url} / $nEntry * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s\n", $perc, $percSum, $sessionFirstPageN{$url}, $url); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Pages by number of views\n*\n"); undef(%sort); undef($percSum); $count = 0; $nn = 0; foreach $url(keys(%pageViewN)) { $sortKey = sprintf("%20d %s", $pageViewN{$url}, $url); $sort{$sortKey} = $url; $nn += $pageViewN{$url}; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $url = $sort{$sortKey}; $perc = $pageViewN{$url} / $nn * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s\n", $perc, $percSum, $pageViewN{$url}, $url); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Pages by view time\n*\n"); undef(%sort); undef($percSum); $count = 0; $nn = 0; foreach $url(keys(%pageViewTime)) { $sortKey = sprintf("%20f %s", $pageViewTime{$url} / $pageViewTimedN{$url}, $url); $sort{$sortKey} = $url; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $url = $sort{$sortKey}; printf("%8.1fsec %s (%d times)\n", $pageViewTime{$url} / $pageViewTimedN{$url}, $url, $pageViewTimedN{$url}); if($count > $MAX_ITEMS) { last; } $count++; } print("\n"); if(0){ print("*\n* Session\n*\n"); undef(%sort); undef($percSum); $count = 0; foreach $host(keys(%sessionN)) { $sortKey = sprintf("%20d %s", $sessionN{$host}, $host); $sort{$sortKey} = $host; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $host = $sort{$sortKey}; $perc = $sessionN{$host} / $nSession * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s (%ds/ %.1fmin)\n", $perc, $percSum, $sessionN{$host}, $host, $sessionDuration{$host}, $sessionDuration{$host} / 60.0); foreach $session(sort { $a <=> $b } (keys(%{$sessions{$host}}))) { print(" $session: ", join(", ", @{$sessions{$host}{$session}}), ";\n"); } if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); } print("*\n* Session exit page by number of sessions\n*\n"); undef(%sort); undef($percSum); $count = 0; $nExit = 0; foreach $url(keys(%sessionLastPageN)) { $sortKey = sprintf("%20d %s", $sessionLastPageN{$url}, $url); $sort{$sortKey} = $url; $nExit += $sessionLastPageN{$url}; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $url = $sort{$sortKey}; $perc = $sessionLastPageN{$url} / $nExit * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s\n", $perc, $percSum, $sessionLastPageN{$url}, $url); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Files by number of requests\n*\n"); undef(%sort); undef($percSum); $count = 0; foreach $url(keys(%urlN)) { $sortKey = sprintf("%20d %s", $urlN{$url}, $url); $sort{$sortKey} = $url; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $url = $sort{$sortKey}; $perc = $urlN{$url} / $nPassed * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s\n", $perc, $percSum, $urlN{$url}, $url); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* File type by volume (bytes total, bytes per document, number of requests)\n*\n"); undef(%sort); undef($percSum); $count = 0; $nn = 0; foreach $fileType(keys(%fileTypeN)) { $nn += $fileTypeVolume{$fileType}; $sortKey = sprintf("%20d %s", $fileTypeVolume{$fileType}, $fileType); $sort{$sortKey} = $fileType; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $fileType = $sort{$sortKey}; $perc = $fileTypeVolume{$fileType} / $nn * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %7s %7s %5d %s\n", $perc, $percSum, &FormatSize($fileTypeVolume{$fileType}), &FormatSize($fileTypeVolume{$fileType} / $fileTypeN{$fileType}), $fileTypeN{$fileType}, $fileType); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Requesting host by number of requests (time of last access)\n*\n"); undef(%sort); undef($percSum); $count = 0; foreach $host(keys(%hostN)) { $sortKey = sprintf("%20d %s", $hostN{$host}, $host); $sort{$sortKey} = $host; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $host = $sort{$sortKey}; $perc = $hostN{$host} / $nPassed * 100; $percSum += $perc; ($tld) = $host =~ /\.([^\.]+)$/; printf("%5.1f%% %5.1f%% %5d %s (%s) last at %s\n", $perc, $percSum, $hostN{$host}, $host, $tldName{$tld}, &SecsToDate($hostLast{$host})); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Requesting domain by number of requests (time of last access)\n*\n"); undef(%sort); undef($percSum); $count = 0; foreach $domain(keys(%domainN)) { $sortKey = sprintf("%20d %s", $domainN{$domain}, $domain); $sort{$sortKey} = $domain; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $domain = $sort{$sortKey}; $perc = $domainN{$domain} / $nPassed * 100; $percSum += $perc; ($tld) = $domain =~ /\.([^\.]+)$/; printf("%5.1f%% %5.1f%% %5d %s (%s) last at %s\n", $perc, $percSum, $domainN{$domain}, $domain, $tldName{$tld}, &SecsToDate($domainLast{$domain})); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Top Level Domain by number of requests\n*\n"); undef(%sort); undef($percSum); $count = 0; foreach $tld(keys(%tldN)) { $sortKey = sprintf("%20d %s", $tldN{$tld}, $tld); $sort{$sortKey} = $tld; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $tld = $sort{$sortKey}; $perc = $tldN{$tld} / $nPassed * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s (%s)\n", $perc, $percSum, $tldN{$tld}, $tld, $tldName{$tld}); if(($count > $MIN_ITEMS) && (($perc < $MIN_PERCENT) || ($percSum > $CUT_OFF) || ($count > $MAX_ITEMS))) { last; } $count++; } print("\n"); print("*\n* Result code by number of requests\n*\n"); undef(%sort); undef($percSum); foreach $code(keys(%codeN)) { $sortKey = sprintf("%20d %s", $codeN{$code}, $code); $sort{$sortKey} = $code; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $code = $sort{$sortKey}; $perc = $codeN{$code} / $nPassed * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s %s\n", $perc, $percSum, $codeN{$code}, $code, $httpStatusName{$code}); } print("\n"); print("*\n* Protocol by number of requests\n*\n"); undef(%sort); undef($percSum); foreach $protocol(keys(%protocolN)) { $sortKey = sprintf("%20d %s", $protocolN{$protocol}, $protocol); $sort{$sortKey} = $protocol; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $protocol = $sort{$sortKey}; $perc = $protocolN{$protocol} / $nPassed * 100; $percSum += $perc; printf("%5.1f%% %5.1f%% %5d %s\n", $perc, $percSum, $protocolN{$protocol}, $protocol); } print("\n"); print("*\n* Browser by number of requests (plus version: % relative to browser/to all requests)\n*\n"); undef(%sort); undef($percSum); foreach $browser(keys(%browserN)) { $sortKey = sprintf("%20d %s", $browserN{$browser}, $browser); $sort{$sortKey} = $browser; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $browser = $sort{$sortKey}; $perc = $browserN{$browser} / $nPassed * 100; $percSum += $perc; undef(%sort2); undef($percSum2); foreach $browserVersion(keys(%{$browserVersionN{$browser}})) { $sortKey2 = sprintf("%20d %s", $browserVersionN{$browser}{$browserVersion}, $browserVersion); $sort2{$sortKey2} = $browserVersion; } undef(@tmp); foreach $sortKey2(sort { $b cmp $a } (keys(%sort2))) { $browserVersion = $sort2{$sortKey2}; push(@tmp, sprintf("%s: %.1f%%/%.1f%%", $browserVersion, $browserVersionN{$browser}{$browserVersion} / $browserN{$browser} * 100, $browserVersionN{$browser}{$browserVersion} / $nPassed * 100)); } printf("%5.1f%% %5.1f%% %5d $browser (%s)\n", $perc, $percSum, $browserN{$browser}, join(", ", @tmp)); } print("\n"); print("*\n* Platform by number of requests (plus version: % relative to platform/to all requests)\n*\n"); undef(%sort); undef($percSum); foreach $platform(keys(%platformN)) { $sortKey = sprintf("%20d %s", $platformN{$platform}, $platform); $sort{$sortKey} = $platform; } foreach $sortKey(sort { $b cmp $a } (keys(%sort))) { $platform = $sort{$sortKey}; $perc = $platformN{$platform} / $nPassed * 100; $percSum += $perc; undef(%sort2); undef($percSum2); foreach $platformVersion(keys(%{$platformVersionN{$platform}})) { $sortKey2 = sprintf("%20d %s",$platformVersionN{$platform}{$platformVersion}, $platformVersion); $sort2{$sortKey2} = $platformVersion; } undef(@tmp); foreach $sortKey2(sort { $b cmp $a } (keys(%sort2))) { $platformVersion = $sort2{$sortKey2}; push(@tmp, sprintf("%s: %.1f%%/%.1f%%", $platformVersion, $platformVersionN{$platform}{$platformVersion} / $platformN{$platform} * 100, $platformVersionN{$platform}{$platformVersion} / $nPassed * 100)); } printf("%5.1f%% %5.1f%% %5d %s (%s)\n", $perc, $percSum, $platformN{$platform}, $platform, join(", ", @tmp)); } print("\n"); print("*\n* Distribution of requests over the day\n*\n"); undef($percSum); for ($hour = 0; $hour < 24; $hour++) { $key = sprintf("%02d", $hour); if($hourN{$key} > $maxHitsPerHour) { $maxHitsPerHour = $hourN{$key}; $maxHitsHour = $key; } } for ($hour = 0; $hour < 24; $hour++) { $key = sprintf("%02d", $hour); $perc = $hourN{$key} / $nPassed * 100; $percSum += $perc; $lineChar = ($key eq $maxHitsHour) ? "*" : "="; printf("%5.1f%% %5.1f%% %5d %s %s\n", $perc, $percSum, $hourN{$key}, $key, $lineChar x ($hourN{$key} / $maxHitsPerHour * 50.0)); } print("\n"); print("*\n* Distribution of requests over the week\n*\n"); undef($percSum); for ($weekDay = 0; $weekDay < 7; $weekDay++) { if($weekDayN{$weekDay} > $maxHitsPerWeekDay) { $maxHitsPerWeekDay = $weekDayN{$weekDay}; $maxHitsWeekDay = $weekDay; } } for ($weekDay = 0; $weekDay < 7; $weekDay++) { $perc = $weekDayN{$weekDay} / $nPassed * 100; $percSum += $perc; $lineChar = ($weekDay eq $maxHitsWeekDay) ? "*" : "="; printf("%5.1f%% %5.1f%% %5d %s %s\n", $perc, $percSum, $weekDayN{$weekDay}, $WEEK_DAY[$weekDay], $lineChar x ($weekDayN{$weekDay} / $maxHitsPerWeekDay * 50.0)); } print("\n"); print("*\n* Requests per date\n*\n"); undef($percSum); foreach $date(keys(%dateN)) { if($dateN{$date} > $maxHitsPerDay) { $maxHitsPerDay = $dateN{$date}; $maxHitsDay = $date; } } foreach $date(sort(keys(%dateN))) { $perc = $dateN{$date} / $nPassed * 100; $percSum += $perc; $lineChar = ($date eq $maxHitsDay) ? "*" : "="; printf("%5.1f%% %5.1f%% %5d %s %s\n", $perc, $percSum, $dateN{$date}, $date, $lineChar x ($dateN{$date} / $maxHitsPerDay * 50.0)); } print("\n"); sub RegisterURLchange { local ($url, $host, $secs, $newSessionP) = @_; if($newSessionP) { # first URL of new session; clean up old stuff, prepare new session. if($sessionLastURL{$host} ne "") { # last relevant URL from previous session which timed out; view time is unknown. # !!!actually: it might from a much earlier session if the sessions in between # had no "substance" push(@{$sessions{$host}{$sessionN{$host}}}, sprintf("%s", $sessionLastURL{$host})); $sessionLastPageN{$sessionLastURL{$host}}++; $pageViewN{$sessionLastURL{$host}}++; } $sessionLastURL{$host} = ""; $sessionLastAccess{$host} = ""; } if(Filter($url)) { # new URL in session, but of no interest return; } # this is a relevant URL if($sessionLastURL{$host} ne "") { # there was a previous relevant URL in this session: view time can be determineds push(@{$sessions{$host}{$sessionN{$host}}}, sprintf("%s(%ds)", $sessionLastURL{$host}, $secs - $sessionLastAccess{$host})); $pageViewN{$sessionLastURL{$host}}++; $pageViewTimedN{$sessionLastURL{$host}}++; $pageViewTime{$sessionLastURL{$host}} += $secs - $sessionLastAccess{$host}; } else { $sessionFirstPageN{$url}++; } $sessionLastURL{$host} = $url; $sessionLastAccess{$host} = $secs; } sub DateToSecs { local($day, $month, $year, $hour, $min, $sec) = @_; local $extra, $extras; $extra = (($month > 2) && (($year % 4 == 0) && !($year % 100 == 0) || ($year % 400 == 0))) ? 1 : 0; $extras = int(($year - 1969) / 4); return (((($year - 1970) * 365 + $DAY_SUM[$month - 1] + $day - 1 + $extra + $extras) * 24 + $hour) * 60 + $min) * 60 + $sec; } sub SecsToDate { local($secs) = @_; local $year, $month, $day, $hour, $min, $sec; $year = int($secs / (365 * 24 * 3600)); $secs -= $year * 365 * 24 * 3600; $year += 1970; $extras = int(($year - 1969) / 4); $days = int($secs / (24 * 3600)) - $extras; $extra = (($days >= 59) && (($year % 4 == 0) && !($year % 100 == 0) || ($year % 400 == 0))) ? 1 : 0; $days -= $extra; $month = 0; while($days >= $DAY_SUM[$month + 1]) { $month++; } $secs -= ($DAY_SUM[$month] + $extras + (($extra && $days >= 59) ? $extra : 0)) * 24 * 3600; $day = int($secs / (24 * 3600)); $secs -= $day * 24 * 3600; $hour = int($secs / 3600); $secs -= $hour * 3600; $min = int($secs / 60); $secs -= $min * 60; $sec = $secs; return sprintf("%02d.%02d.%04d %02d:%02d:%02d", 1 + $day, 1 + $month, $year, $hour, $min, $sec); } sub GetWeekDay { return (3 + $_[0] / (24 * 3600)) % 7; } sub FormatSize { local ($size) = @_; local $unit = 0; # kilo, mega, giga, tera, peta, exa, zetta, yotta - we're ready for big numbers @units = (" ", "k", "M", "G", "T", "P", "E", "Z", "Y"); while($size >= 1000) { $unit++; $size /= 1024.0; } return sprintf("%.1f%sB", $size, $units[$unit]); }