<?php
/*
Copyright (c) 2007 Sjan Evardsson.  All rights reserved.

Developed by: Sjan Evardsson
              http://www.evardsson.com
                            sjan@evardsson.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal with the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimers.
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimers in the
     documentation and/or other materials provided with the distribution.
  3. Neither the names of Sjan Evardsson, evardsson.com, 
     nor the names of its contributors may be used to endorse
     or promote products derived from this Software without specific prior
     written permission.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
WITH THE SOFTWARE.
*/

/**
 * class TCDate
 * generate a TC date based on local time
 * due to the limitations of PHP this is limited by the underlying system
 * to either Dec 13, 1901 as the earliest date, or Jan 1, 1970
 * and tops out at Jan 19, 2038
 */
class TCDate {

    
// The initial start of the calendar GMT - Dec 19th, 2003
    
private $initial_timestamp 1071795600//gmmktime(0,0,0,12,19,2003);
    // The timestamp we are working with
    
private $current_timestamp;
    
// short or long
    
private $tcmethod;
    
// there are 88 days to a 'year'
    
private $year 88;
    
// there are 4 'years' to a cluster
    
private $cluster 4;
    
// there are 9 clusters to a cycle
    
private $cycle 9;
    
// there are either 9 (long method) or 4 (short method) cycles to a largescale time unit
    
private $largescale 9;
    
// there are 1000 largescales to an aleph
    
private $aleph 1000;
    
// the parts that make up the display date
    
private $date_parts = array(
            
'aleph' => 1,
            
'large_scale' => 1,
            
'cluster' => 1,
            
'year' => 0,
            
'day' => 0,
            
'hour' => 0,
            
'hour_12' => 0,
            
'minute' => 0,
            
'second' => 0,
            
'am' => 'a',
            
'count_method' => 'Long Count'
        
);
    
    
    public function 
__construct($arg=null$tcmethod='long')
    {
        
$this->tcmethod $tcmethod;
        if (
$tcmethod == 'short') { 
            
$this->largescale 4;
            
$this->date_parts['count_method'] = 'Short Count';
        }
        if (
$arg != null) {
            if (
is_numeric($arg)) {
                
$now $arg;
            }
            elseif (
is_string($arg)) {
                
$now strtotime($arg);
            }
            elseif (
is_array($arg)) {
                
$tmp explode('|',date('H|i|s|m|d|Y'));
                for (
$i 0$i count($arg); $i++)
                {
                    
$tmp[$i] = $arg[$i];
                }
                
$now mktime($tmp[0],$tmp[1],$tmp[2],$tmp[3],$tmp[4],$tmp[5]);
            }
            else {
                
trigger_error('Unsupported type: TCDate requires a type of int, string or array'E_USER_ERROR);
            }
        } else {
            
$now mktime();
        }
        
$this->current_timestamp $now;
        
$this->make_date_parts();
    }
    
    public function 
strtotctime($thestring$timestamp=null)
    {
        
$this->current_timestamp strtotime($thestring$timestamp)+(int)date('Z');
        
$this->make_date_parts();
    }
    
    public function 
_get($field)
    {
        if (
array_key_exists($field$this->date_parts))
            return 
$this->date_parts[$field];
        elseif (
$field=='intarray')
            return 
array_values($this->date_parts);
        elseif (
$field=='all')
            return 
$this->date_parts;
        else
            return 
false;
    }
    
    public function 
current_timestamp()
    {
        return 
$this->current_timestamp;
    }
    
    protected function 
make_date_parts()
    {
        
// if short method aleph = 144000 yrs
        // if long method aleph = 
        
$dy 86400;
        
$yr $this->year;
        
$cl $this->cluster;
        
$ls $this->largescale;
        
$al 1000;
        
//$parts = $this->date_parts;
        
$now $this->current_timestamp;
        
$orig $this->initial_timestamp;
        
$adjusted $now $orig// 0 == Aleph 1, LS 1, Cluster 1, Yr 1, Day 1, 00:00:00
        
if ($adjusted == 0) {
            
// this is day one - do nothing
            
return;
        }
        elseif (
$adjusted 0) { 
            
$day = (int)($adjusted/$dy);
            
$year = (int)($day/$yr);
            
$cluster = (int)ceil($year/$cl);
            
$large_scale = (int)ceil($cluster/$ls);
            
$aleph = (int)ceil($large_scale/$al);
            
// now to turn day 8226 or whatever into a valid number
            
$day $day 88;
        }
        else { 
            
$day = (int)($adjusted/$dy);
            
$year = (int)($day/$yr);
            
$cluster = (int)ceil($year/$cl);
            
$large_scale = (int)ceil($cluster/$ls);
            
$aleph = (int)ceil($large_scale/$al);
            
// now to turn day 8226 or whatever into a valid number
            
$day $day 88;
        }
        if (
$day 0) { $day $day*-1; }
        
$this->date_parts['aleph'] = $aleph;
        
$this->date_parts['large_scale'] = $large_scale;
        
$this->date_parts['cluster'] = $cluster;
        
$this->date_parts['year'] = $year;
        
$this->date_parts['day'] = $day;
        
$this->date_parts['hour'] = (int)date('H',$now);
        
$this->date_parts['minute'] = (int)date('i'$now);
        
$this->date_parts['second'] = (int)date('s'$now);
        
$this->date_parts['hour_12'] = (int)date('h'$now);
        
$this->date_parts['am'] = date('a'$now);
    }

}

/**
 * class TCDateUTC
 * generate a TC date based on GMT 
 * due to the limitations of PHP this is limited by the underlying system
 * to either Dec 13, 1901 as the earliest date, or Jan 1, 1970
 * and tops out at Jan 19, 2038
 */
class TCDateUTC {

    
// The initial start of the calendar GMT - Dec 19th, 2003
    
private $initial_timestamp 1071795600//gmmktime(0,0,0,12,19,2003);
    // The GMT timestamp we are working with
    
private $current_timestamp;
    
// short or long
    
private $tcmethod;
    
// there are 88 days to a 'year'
    
private $year 88;
    
// there are 4 'years' to a cluster
    
private $cluster 4;
    
// there are 9 clusters to a cycle
    
private $cycle 9;
    
// there are either 9 (long method) or 4 (short method) cycles to a largescale time unit
    
private $largescale 9;
    
// there are 1000 largescales to an aleph
    
private $aleph 1000;
    
// the parts that make up the display date
    
private $date_parts = array(
            
'aleph' => 1,
            
'large_scale' => 1,
            
'cluster' => 1,
            
'year' => 0,
            
'day' => 0,
            
'hour' => 0,
            
'hour_12' => 0,
            
'minute' => 0,
            
'second' => 0,
            
'am' => 'a',
            
'count_method' => 'Long Count'
        
);
    
    
    public function 
__construct($arg=null$tcmethod='long')
    {
        
$this->tcmethod $tcmethod;
        if (
$tcmethod == 'short') { 
            
$this->largescale 4;
            
$this->date_parts['count_method'] = 'Short Count';
        }
        if (
$arg != null) {
            if (
is_numeric($arg)) {
                
$now $arg;
            }
            elseif (
is_string($arg)) {
                
$now strtotime($arg)+(int)date('Z');
            }
            elseif (
is_array($arg)) {
                
$tmp explode('|',gmdate('H|i|s|m|d|Y'));
                for (
$i 0$i count($arg); $i++)
                {
                    
$tmp[$i] = $arg[$i];
                }
                
$now gmmktime($tmp[0],$tmp[1],$tmp[2],$tmp[3],$tmp[4],$tmp[5]);
            }
            else {
                
trigger_error('Unsupported type: TCDate requires a type of int, string or array'E_USER_ERROR);
            }
        } else {
            
$now gmmktime();
        }
        
$this->current_timestamp $now;
        
$this->make_date_parts();
    }
    
    public function 
strtotctime($thestring$timestamp=null)
    {
        
$this->current_timestamp strtotime($thestring$timestamp)+(int)date('Z');
        
$this->make_date_parts();
    }
    
    public function 
_get($field)
    {
        if (
array_key_exists($field$this->date_parts))
            return 
$this->date_parts[$field];
        elseif (
$field=='intarray')
            return 
array_values($this->date_parts);
        elseif (
$field=='all')
            return 
$this->date_parts;
        else
            return 
false;
    }
    
    public function 
current_timestamp()
    {
        return 
$this->current_timestamp;
    }
    
    protected function 
make_date_parts()
    {
        
// if short method aleph = 144000 yrs
        // if long method aleph = 
        
$dy 86400;
        
$yr $this->year;
        
$cl $this->cluster;
        
$ls $this->largescale;
        
$al 1000;
        
//$parts = $this->date_parts;
        
$now $this->current_timestamp;
        
$orig $this->initial_timestamp;
        
$adjusted $now $orig// 0 == Aleph 1, LS 1, Cluster 1, Yr 1, Day 1, 00:00:00
        
if ($adjusted == 0) {
            
// this is day one - do nothing
            
return;
        }
        elseif (
$adjusted 0) { 
            
$day = (int)($adjusted/$dy);
            
$year = (int)($day/$yr);
            
$cluster = (int)ceil($year/$cl);
            
$large_scale = (int)ceil($cluster/$ls);
            
$aleph = (int)ceil($large_scale/$al);
            
// now to turn day 8226 or whatever into a valid number
            
$day $day 88;
        }
        else { 
            
$day = (int)($adjusted/$dy);
            
$year = (int)($day/$yr);
            
$cluster = (int)ceil($year/$cl);
            
$large_scale = (int)ceil($cluster/$ls);
            
$aleph = (int)ceil($large_scale/$al);
            
// now to turn day 8226 or whatever into a valid number
            
$day $day 88;
        }
        if (
$day 0) { $day $day*-1; }
        
$this->date_parts['aleph'] = $aleph;
        
$this->date_parts['large_scale'] = $large_scale;
        
$this->date_parts['cluster'] = $cluster;
        
$this->date_parts['year'] = $year;
        
$this->date_parts['day'] = $day;
        
$this->date_parts['hour'] = (int)gmdate('H',$now);
        
$this->date_parts['minute'] = (int)gmdate('i'$now);
        
$this->date_parts['second'] = (int)gmdate('s'$now);
        
$this->date_parts['hour_12'] = (int)gmdate('h'$now);
        
$this->date_parts['am'] = gmdate('a'$now);
    }

}

/**
 * function tc_date([string format, [int timestamp, [string counting method]]])
 * Returns a formatted TC Date string
 * @param format the String format (explained below)
 * @param arg a unix GMT timestamp, null for now
 * @param method the String name of the TC Method, 'short' or 'long'
 * @return a formatted TC Date String
 * Characters in the format string that are not present in the list below are added
 * as is to the return string. To add characters that are in the list below prepend them
 * with a backslash as in '\T\he \D\a\te \i\s tY:d'
 * ----- Epochs and Large scale time -----
 * X : Aleph Example: 1 (Negative numbers represent dates before Aleph 1, Year 1, Day 1)
 * L : Large Scale Count (there are 1000 in an Aleph) Example: 3
 * C : Cycle (there are either 4 or 9 in a Large Scale) Example: 2
 * ----- Dates ----------------------
 * Y : Year (there are 4 in a cycle) Example: 15
 * y : Year [same as Y - added for convenience]
 * d : Day with leading 0 (there are 88 in a year) 01 to 88
 * j : Day with no leading 0 Example : 1 to 88
 * D : A textual representation of a day, three letters      Mon through Sun
 * l (lowercase 'L') : A full textual representation of the day of the week
 * ----- Time ------------------------
 * a : Lowercase Ante meridiem and Post meridiem      am or pm
 * A : Uppercase Ante meridiem and Post meridiem     AM or PM
 * g : 12-hour format of an hour without leading zeros     1 through 12
 * G : 24-hour format of an hour without leading zeros     0 through 23
 * h : 12-hour format of an hour with leading zeros     01 through 12
 * H : 24-hour format of an hour with leading zeros     00 through 23
 * i : Minutes with leading zeros     00 to 59
 * s : Seconds, with leading zeros     00 through 59
 * ----- Timezone     --- 
 * Timezone      ---      ---
 * e     Timezone identifier (added in PHP 5.1.0)     Examples: UTC, GMT, Atlantic/Azores
 * I (capital i)     Whether or not the date is in daylight saving time     1 if Daylight Saving Time, 0 otherwise.
 * O     Difference to Greenwich time (GMT) in hours     Example: +0200
 * P     Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3)     Example: +02:00
 * z     Timezone abbreviation     Examples: EST, MDT ...
 * Z  Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive.
 * ----- Extra -----------------
 * t : Short Identifier as a TC Date Example: TC
 * T : Long Identifier as a TC Date Example: Thoth Count
 * c : TC Count method Example: Long Count
 *
 * ---------- Example usage:
 * print  "<pre>Now : ".tc_date()."
 * Fully formatted: ".tc_date('\A\l\ep\h X, \L\ar\g\e S\c\a\l\e L, \in \t\h\e \c\y\c\l\e C, TY:d l g:i:s A \Coun\t M\e\t\ho\d: c')."</pre>";
 * --------- Ouput:
 * Now : TC16:07
 * Fully formatted: Aleph 1, Large Scale 1, in the cycle 4, Thoth Count16:07 Saturday 9:42:04 PM Count Method: Long Count
 */
function tc_date($format=null$arg=null$method='long')
{
    if (
is_null($format)) $format 'tY:d';
    
$tcd = new TCDate($arg$method);
    
$x $tcd->_get('all');
    
$replace = array(
            
'X' => $x['aleph'],
            
'L' => $x['large_scale'],
            
'C' => $x['cluster'],
            
'Y' => $x['year'],
            
'y' => $x['year'],
            
'd' => ($x['day']<10)?'0'.$x['day']:$x['day'],
            
'j' => $x['day'],
            
'D' => date('D',$tcd->current_timestamp()),
            
'l' => date('l',$tcd->current_timestamp()),
            
'h' => ($x['hour_12']<10)?'0'.$x['hour_12']:$x['hour_12'],
            
'g' => $x['hour_12'],
            
'H' => ($x['hour']<10)?'0'.$x['hour']:$x['hour'],
            
'G' => $x['hour'],
            
'i' => ($x['minute']<10)?'0'.$x['minute']:$x['minute'],
            
's' => ($x['second']<10)?'0'.$x['second']:$x['second'],
            
'a' => $x['am'],
            
'A' => strtoupper($x['am']),
            
'c' => $x['count_method'],
            
't' => 'TC',
            
'T' => 'Thoth Count',
            
'e' => date('e',$tcd->current_timestamp()),
            
'I' => date('I',$tcd->current_timestamp()),
            
'O' => date('O',$tcd->current_timestamp()),
            
'P' => date('P',$tcd->current_timestamp()),
            
'z' => date('T',$tcd->current_timestamp()),
            
'Z' => date('Z',$tcd->current_timestamp())
        );
    
$chars str_split($format);
    
$skip false;
    
$result '';
    foreach (
$chars as $c)
    {
        if (!
$skip) {
            if (
$c == '\\') { 
                
$skip true
            } elseif (
array_key_exists($c$replace)) {
                
$result .= $replace[$c];
            } else {
                
$result .= $c;
            }
        } else {
            
$result .= $c;
            
$skip false;
        }
    }
    return 
$result;
}
/** Same as tc_date except uses UTC 
 * (Coordinated Universal Time, aka Greenwich Mean Time or GMT)
 * see tc_date for usage and formatting
 * Note: all timezone references will return UTC values,
 * so all offsets are 0
 */
function tc_date_utc($format=null$arg=null$method='long')
{
    if (
is_null($format)) $format 'tY:d';
    
$tcd = new TCDateUTC($arg$method);
    
$x $tcd->_get('all');
    
$replace = array(
            
'X' => $x['aleph'],
            
'L' => $x['large_scale'],
            
'C' => $x['cluster'],
            
'Y' => $x['year'],
            
'y' => $x['year'],
            
'd' => ($x['day']<10)?'0'.$x['day']:$x['day'],
            
'j' => $x['day'],
            
'D' => gmdate('D',$tcd->current_timestamp()),
            
'l' => gmdate('l',$tcd->current_timestamp()),
            
'h' => ($x['hour_12']<10)?'0'.$x['hour_12']:$x['hour_12'],
            
'g' => $x['hour_12'],
            
'H' => ($x['hour']<10)?'0'.$x['hour']:$x['hour'],
            
'G' => $x['hour'],
            
'i' => ($x['minute']<10)?'0'.$x['minute']:$x['minute'],
            
's' => ($x['second']<10)?'0'.$x['second']:$x['second'],
            
'a' => $x['am'],
            
'A' => strtoupper($x['am']),
            
'c' => $x['count_method'],
            
't' => 'TC',
            
'T' => 'Thoth Count',
            
'e' => gmdate('e',$tcd->current_timestamp()),
            
'I' => gmdate('I',$tcd->current_timestamp()),
            
'O' => gmdate('O',$tcd->current_timestamp()),
            
'P' => gmdate('P',$tcd->current_timestamp()),
            
'z' => gmdate('T',$tcd->current_timestamp()),
            
'Z' => gmdate('Z',$tcd->current_timestamp())
        );
    
$chars str_split($format);
    
$skip false;
    
$result '';
    foreach (
$chars as $c)
    {
        if (!
$skip) {
            if (
$c == '\\') { 
                
$skip true
            } elseif (
array_key_exists($c$replace)) {
                
$result .= $replace[$c];
            } else {
                
$result .= $c;
            }
        } else {
            
$result .= $c;
            
$skip false;
        }
    }
    return 
$result;
}

?>