update: timezone offset calc and date formatting

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Hi all,

a few days ago I posted on this list about timezone offset calculations.
After some suggestions, I both tested with the use of some Date modules,
as rewrote my own routine for calcutating the time offset and formatting a
date-time string. Benchmark tests show that my own routine is about 8000%
(!!!) faster than using the Date::Manip module. This is especially
interesting when you have to format dates at a high rate, such as for
http-logging. I'll elaborate some more on my findings in this post.

I need a formatted date-time string for a mod_perl module that writes
access logs. Apache uses it's own date format, which includes the timezone
offset. As a log entry is written for every request, and thus the date
format has to be calculated for every request, I want it's overhead to be

I wanted to test two different modules for date formatting and calcs. I
tried to install the - very complete - module DateTime. But for some
reasons it wouldn't install on my system. Besides the fact that it is very
complete, it also seemed like to be a bit over-the-top for what I needed.
I succeeded in using Date::Manip for constructing the date-format string,
which looks like: [08/Apr/2005:11:24:03 +0100]. The code for constructing
this string is fairly simple and concise:

sub DateManip {
my $time = &UnixDate("today", "[%d/%b/%Y:%H:%M:%S %z]"); return $time;

Where as my own routine is a bit more complex:

sub homeBrew {
my @lctime = localtime();
my @gmtime = gmtime();
my $mo = (($lctime[2]-$gmtime[2])%24)*60+($lctime[1]-$gmtime[1])%60;
my $tz = int($mo/60)*100+($mo/abs($mo))*($mo%60);
my $time = sprintf "[%02d/%3s/%4d:%02d:%02d:%02d %+05d]",
    (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$lctime[4]]),
return $time;

The main calculations are for the timezone difference. I used Benchmark to
measure the performace of both routines, and here are the results (P4

Benchmark: running DateManip, homeBrew for at least 10 CPU seconds...
 DateManip: 10 wallclock secs (10.46 usr +  0.02 sys = 10.48 CPU) @
280.34/s (n=2938)
  homeBrew: 11 wallclock secs ( 9.74 usr +  0.32 sys = 10.06 CPU) @
22500.00/s (n=226350)

             Rate DateManip  homeBrew
DateManip   280/s        --      -99% homeBrew  22500/s     7926%      

I'd say that is quite a difference! Since I want to use this on a
webserver, where a probable 100-300 requests/second arrive, I don't want
my CPU to be fully occupied formatting a date.

The main performance gain is in the algorithm for calculating the timezone
offset. In the previous discussion, "Geoff" pointed out, that timezone
offset is not always in full hours, but can also differ 30 minutes. He
suggested calculating the difference in seconds. So I did, using
Time::Local to convert localtime() and gmtime() to seconds. After
calculating the difference in seconds, I have to convert it back to hours
and minutes in order to get the proper format. This converting was
obviously CPU-consuming, as this routine was approx. 15x slower than the
algorithm used in the current routine. This algorithm works as follows: 1.
calculate hour difference:
    a = (localtime(hours) - gmtime(hours))%24
2. convert these hours to minutes:
    b = a*60
3. calculate the minute difference:
    c = (localtime(minutes) - gmtime(minutes))%60
4. sum to get the total time diff in minutes:
    d = b + c
5. prepare to format to 4 digit hhmm number:
    e = int(d/60)*100+(d/abs(d))*(d%60)

To demonstrate the efficiency of using this algorithm in stead of the
Date::Manip method, I did another test where I only formatted the
time-zone offset. The routines:

sub homeBrew {
my @lctime = localtime();
my @gmtime = gmtime();
my $mo = (($lctime[2]-$gmtime[2])%24)*60+($lctime[1]-$gmtime[1])%60;
my $tz = int($mo/60)*100+($mo/abs($mo))*($mo%60);
my $time = sprintf "%+05d", $tz;
return $time;
sub DateManip {
    my $time = &UnixDate("today", "%z");
    return $time;

The Benchmark results:

Benchmark: running DateManip, homeBrew for at least 10 CPU seconds...
 DateManip: 11 wallclock secs (10.51 usr +  0.02 sys = 10.53 CPU) @
256.13/s (n=2697)
  homeBrew: 11 wallclock secs ( 9.85 usr +  0.38 sys = 10.23 CPU) @
28406.06/s (n=290594)

             Rate DateManip  homeBrew
DateManip   256/s        --      -99% homeBrew  28406/s    10991%      

This shows that my algorithm is about 100x faster than using Date::Manip.
A significant difference I'd say.

Conclusion: if you have to format time in a high load environment, it's
definitely worth it to programm your own algorithms for formatting date
and time. If anyone has an even more efficient algorithm than mine, I'd
like to hear it.

-leendert bottelberghs

Site Timeline