1 (edited by thetorpedodog 2006-07-14 14:38)

Topic: Improved local time system

It seems like it would be a very helpful feature to use PHP's built in timezone system for calculating local times for users on the forum. I put this together on my own forum, and it works remarkably well. It would unfortunately require setting all users' timezones to UTC on an upgrade, but afterwords, I think the benifits would outweigh the initial investment: it automatically handles Daylight Saving Time no matter whether you're in the Northern or Southern hemisphere so long as you select the right timezone.

It would require ALTERing the structure of the user table to change the timezone field from a float to a TEXT or a VARCHAR, and would also require the replacement of the timezone boxes, but other than that, it's quite simple. Most of my code follows, except for the list of timezones because it would make this post REALLY REALLY LONG. It was mostly generated from the appendix to the PHP Manual listing all the timezones they support, removing some of the extra/legacy cruft.

<?php

/**
 * Helper function for timezone_select().
 * $tzlist: The list of timezones, with a nested array indicating a new header.
 * $prefix: What should be printed before the value, for instance "Europe/"
 * $disppre: What should be printed before the option text (spaces used as
 *           padding in this case, since we can't use nested <optgroup>s :(
 * $current: The currently selected index.
 */
function tzselect($tzlist, $prefix, $disppre, $current) {
    if(! is_array($tzlist)) return false ;
    foreach($tzlist as $id => $value) {
        $newprefix = $prefix ;
        $newdisppre = $disppre ;
        if(is_array($value)) {
            $newprefix .= "$id/" ;
            $id = htmlspecialchars($id) ;
            $timestring .= "<option disabled='disabled' class='grouphead'>$disppre$id:</option>\n" ;
            $newdisppre = $newdisppre.'  ' ;
            $timestring .= tzselect($value, $newprefix, $newdisppre, $current) ;
        } else {
            $optvalue = $newprefix.$value ;
            $optvalue = htmlspecialchars($optvalue);
            $value = htmlspecialchars($value) ;
            $attr = selectifactive($optvalue, $current) ;
            $timestring .= "<option $attr>$disppre$value</option>\n" ;
        }
    }
    return $timestring ;
}

/**
 * Returns a timezone box.
 * $name: the name of the <select> in the form.
 * $id: the id of the <select>. If left blank, no ID will be used.
 * $current: The currently selected timezone. Used to put selected="selected" in
 *           the right zone.
 */
function timezone_select($name, $id='', $current='') {
    // Just enough to tell what the structure is; if anybody wants to put my entire filtered list, I will
    $timezones =
        Array(
            'Africa' => Array (
                'Abidjan',
                'Accra',
                'Addis_Ababa',
                'Algiers',
                'Asmera',
                'Bamako'
            ) ,
            'Indian' => Array (
                'Antananarivo',
                'Chagos',
                'Christmas'
            )
        ) ;
    $idp = ($id == '' ? '' : "id='$id'") ;
    $timestr = "<select name='$name' $idp>\n" ;
    if('' == $current) {
        $sel1 = 'selected="selected"' ;
    }
    $timestr .= '<option value="UTC" '.$sel1.'>Select a timezone</option>'."\n" ;
    $timestr .= '<option '.selectifactive('UTC', $current).'>UTC</option>'."\n" ;
    $timestr .= tzselect($timezones, '', '', $current) ;
    $timestr .='</select>'."\n" ;
    return $timestr ;
}

/**
 * Gives pretty much all the parameters for an option.
 * $value: the value= of the option.
 * $active: the one you want selected.
 */
function selectifactive($value, $active) {
    $sel = "value='$value'" ;
    if($value == $active) {
        $sel .= ' selected="selected"' ;
    }
    return $sel ;
}

/**
 * Format a time string according to $time_format and timezones
 */
function format_time($timestamp, $date_only = false)
{
    global $pun_config, $lang_common, $pun_user;

    if ($timestamp == '')
        return $lang_common['Never'];

    $timezone = $pun_user['timezone'];
    $now = time();

    $date = date($pun_config['o_date_format'], $timestamp);
    $today = date($pun_config['o_date_format'], $now);
    $yesterday = date($pun_config['o_date_format'], strtotime('-1 day'));

    if ($date == $today)
        $date = $lang_common['Today'];
    else if ($date == $yesterday)
        $date = $lang_common['Yesterday'];

    if (!$date_only)
        return $date.' '.date($pun_config['o_time_format'], $timestamp);
    else
        return $date;
}


/**
 * Sets up the timezone system. $timezone is the timezone. In PunBB, this is
 * called by setup_dates($pun_user['timezone']) in common.php.
 */
function setup_dates($timezone) {
    if(function_exists('date_default_timezone_set')) {
        date_default_timezone_set($timezone) ;
    } else {
        putenv('TZ='.$timezone) ;
    }
}

?>