 2CN4sip - Time::PT.pm (PipTime) created by Pip@CPAN.org to define 
   simple objects for storing instants in time.
 Desc: PT describes a simple object which encapsulates 10 fields:
     Century, Year, Month, Day, hour, minute, second, frame, jink, zone
   where frame is normally 1/60th-of-a-second && jink is normally 
   1/60th-of-a-frame.  The objects describe a high-precision time-
   instant with fields in decending order of precision such that 
   alphabetic listings will (typically) show time ascension && field 
   arithmetic can be easily performed.  PT objects can
   be added to / subtracted from Time::Frame objects to yield 
   new specific PT instants.
     The common use of PT is for a simple `pt` utility to 
   en/decode dates && times using seven (7) bass64 characters.
     1st: '0A1B2C3'
     2nd: 'Yd:2003,j:A7_,M:a3I' or 'f:3aL9.eP' 
     if field name ends with d, value is read as decimal nstd of default b64.
     Third way is super verbose decimal strings:
       '15 years, 3 months, 7 weeks, 4 jinx' can use any (or none) sep but :
     4th is hash
     Total Jinx possible for PT: 1,680,238,080,000,000 (1.7 quatrillion)
           JnxPTEpoch -> `pt __nWO0000` -> Midnight Jan. 1 7039 BCE
              PTEpoch -> `pt  _nWO`     -> Midnight Jan. 1 1361  CE
   PT members:
     new inits either with pt-param, expanded, or empty

       epoch_(seconds|frames|jinx)() methods (optional frames/jinx as floats)
     ptepoch_(seconds|frames|jinx)() methods 
       (since ptEpoch (`pt _nWO` Midnight, Jan1,1361))
     settle fields with options (like return new Frame object with only 
       total secs of old)
     re-def frame as other than 60th-of-a-second
     re-def jink  as other than 60th-of-a-frame
       eg. def f && j limits as 31.6227766016838 (sqrt(1000)) for ms jinx
           or just def f as 1000 for exactly ms frames
     allow month/year modes to be set to avg or relative

  My bass64 encoding uses characters: 0-9 A-Z a-z . _  since I don't like
    having spaces or plusses in my time strings.  I need times to be easy to
    append to filenames for very precise, consice, time-stamp versioning.
  Each encoded character represents (normally) just a single date or time 
    field.  All fields are 0-based except Month && Day.  The fields are:
      Year-2000, Month, Day, Hour, Minute, Second, Frame (60th-of-a-second)
  There are three (3) exceptions to the rule that each character only
    represents one date or time field.  The bits are there so... why not? =)
  0) Each 12 added to the Month adds  64 to the Year.
  1)      24 added to the Hour  adds 320 to the Year.
  2)      31 added to the Day   makes the year negative just before adding 
            2000.
  So with all this, any valid pt (of 7 b64 characters) represents a unique 
    instant (precise down to a Frame [60th-of-a-second]) that occurred or 
    will occur between the years 1361 && 2639 (eg. New Year's Day of each 
    of those years would be '_nWO' && '_n1O').  These rules break down as:
 Hour   Day  Month     Year    YearWith2000
 24-47 32-62 49-60  -639- -576  1361-1424
             37-48  -575- -512  1425-1488
             25-36  -511- -448  1489-1552
             13-24  -447- -384  1553-1616
              1-12  -383- -320  1617-1680
  0-23 32-62 49-60  -319- -256  1681-1744
             37-48  -255- -192  1745-1808
             25-36  -191- -128  1809-1872
             13-24  -127-  -64  1873-1936
              1-12   -63-   -0  1937-2000
  0-23  1-31  1-12     0-   63  2000-2063
             13-24    64-  127  2064-2127
             25-36   128-  191  2128-2191
             37-48   192-  255  2192-2255
             49-60   256-  319  2256-2319
 24-47  1-31  1-12   320-  383  2320-2383
             13-24   384-  447  2384-2447
             25-36   448-  511  2448-2511
             37-48   512-  575  2512-2575
             49-60   576-  639  2576-2639
 Notz:  
  PT + Frame can become the core of a new input language which accounts
    for time.  It could be game sequences like a fireball that can be rolled
    from d->df && df->f only at a certain speed ... but then also later
    maybe time-sensitive computer input like typematic key repeat rate but
    configurable... smarter?  The combinatorics on the X-Box Live pswd is
    8**4 == 4096 (butn: u,d,l,r,x,y,L,R) so even exhausting the search space
    (assuming you're too wise for a smpl likely 4-char sequence) could be
    finished manually in about 9 hours if you complete a test cycle each
    8 seconds.  Automated would need programmable circuit... plug that
    thang into USB && make an easy sequencer PT+Frame- based IF to perform!
    So cool!
  Could create an easy IF to setup any sort of practice scenario,
    programmable pad behavior, or even store replays as device inputs &&
    feed them back in... woohoo that's fscking cool!  GameOver specialty =)
    umm it would basically need the same IF as a fighting game tool hehe =).
  Don't need Math::BigInt to store pt epoch seconds (pte's) because perl's 
    floats already have enough precision to store them.  Use the fractional 
    part of those values to store 60ths && don't use builtin timelocal 
    functions which only accept 1970-2036 (or whatever limited) epoch 
    seconds (only 32-bit ints or something =( ).
  Interaction with other Time modules: 
    Time::Period  - just have an Epoch export option && Period can use it
    Time::Avail   - doesn't seem useful to my purposes
    Time::Piece   - might be nice to mimic this module's object interface
    Time::Seconds - handy for dealing with lots of seconds but about 60ths?
  old 5-char pt examples: (update these when there's time)
 Xmpl: `pt 01`        == localtime(975657600) # seconds since Epoch
     `pt 1L7Mu`       == unpack time (Sun Jan 21 07:22:56 2001)
     `pt _VNxx`       == localtime(1143878399)
     `pt pt`          == unpack current pt (akin to `pt `pt``)
     `pt e`           == localtime  (eg. Thu Jan 21 07:22:56 2001)
     `pt e e`         == current epoch seconds
     `pt 1L7Mu e`     == convert from pt to epoch (980090576)
     `pt 975657600 E` == convert from Epoch seconds to pt (01)
     `pt Jan 21, 2001 07:22:56`    -> 1L7Mu
     `pt Sun Jan 21 07:22:56 2001` -> 1L7Mu
     `pt 1L7Mu cmp FEET0`          -> lt 
     `pt FEET0 cmp 1L7Mu`          -> gt 
     `pt 2B cmp 2B`                -> eq
       timelocal($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)

NAME

Time::PT - objects to store an instant in time

VERSION

  This documention refers to version 1.0.3CCA2VC of 
    Time::PT, which was released on Fri Dec 12 10:02:31:12 2003.

SYNOPSIS

    use Time::PT;
    
    my $f = Time::PT->new();
    print 'The Day-of-the-Week today is: ', $f->dow(), "\n";

    print 'PipTime is ', pt, "\n";

DESCRIPTION

  This module has been adapted from the Time::Piece module 
    written by Matt Sergeant <matt@sergeant.org> && Jarkko 
    Hietaniemi <jhi@iki.fi>.  Time::PT inherits base 
    data structure && object methods from Time::Fields.  
    PT was written to simplify storage && calculation 
    of encoded, yet distinct && human-readable, time data 
    objects.
  This module (Time::PT) does not replace the standard localtime &&
    gmtime functions like Time::Piece but Time::PT objects behave
    almost identically to Time::Piece objects otherwise (since it
    was adapted from... I said that already =) ).

2DO

  mk interoperable w/ Time::Seconds objects
  add Time::Zone stuff to use && match zone field reasonably
  flesh out constructor init data parsing && formats supported
  consider epoch functions like _epoch([which epoch]) or individuals
    like _jinx_epoch()
  mk PT->new able to create from different 'epoch' init types
  fix weird 0 month && 0 day problems
  replace legacy pt() with new() wrapper
        What else does PT need?

WHY?

  The reason I created PT was that I have grown so enamored with
    Bass64 representations of everything around me that I was 
    compelled to write a simple clock utility ( `pt` ) using Bass64.
    This demonstrated the benefit to be gained from time objects with
    distinct fields && configurable precision.  Thus, Time::Fields
    was written to be the abstract base class for:
      Time::Frame  ( creates objects which represent spans    of time )
          && 
      Time::PT     ( creates objects which represent instants in time )

USAGE

  Many of Time::PT's methods have been patterned after the excellent
    Time::Piece module written by Matt Sergeant <matt@sergeant.org>
    && Jarkko Hietaniemi <jhi@iki.fi>.

  new(<InitType>, <InitData>) - Time::PT's constructor can be called
    as a class method to create a brand new object or as an object 
    method to copy an existing object.  Beyond that, new() can 
    initialize PT objects 3 different ways:
     -1) <packedB64InitStringImplies'str'>
        eg. Time::PT->new('3C79jo0');
      0) 'str'  => <packedB64InitString>
        eg. Time::PT->new('str'  => '0A1B2C3D4E');
      1) 'list' => <arrayRef>
        eg. Time::PT->new('list' => [0, 1, 2..9]);
      2) 'hash' => <hashRef>
        eg. Time::PT->new('hash' => {'jink' => 8, 'year' => 2003})

  color(<DestinationColorTypeFormat>) - This is an object member
    which will join bass64 representations of each field that has
    been specified in use() && joins them with color-codes or color
    escape sequences with formats for varied uses.  Currently
    available DestinationColorTypeFormats are:
      'ANSI'  # eg. \e[1;32m
      'zsh'   # eg. %{\e[1;33m%}
      'HTML'  # eg. <a href="http://Ax9.org/pt?"><font color="#FF1B2B">
      'Simp'  # eg. RbobYbGbCbUbPb

  The following methods allow access to individual fields of 
    Time::PT objects:

    $t->C  or  $t->century
    $t->Y  or  $t->year
    $t->M  or  $t->month
    $t->D  or  $t->day
    $t->h  or  $t->hour
    $t->m  or  $t->minute
    $t->s  or  $t->second
    $t->f  or  $t->frame
    $t->j  or  $t->jink
    $t->z  or  $t->zone

  Any combination of above single letters can be used as well.  
    Following are some common useful examples:
  
    $t->hms                 # returns list of fields eg. [12, 34, 56]
    $t->hms(12, 56, 34)     # sets fields: h = 12, m = 56, s = 34
    $t->hmsf                # [12, 34, 56, 12]
    $t->hmsfj               # [12, 34, 56, 12, 34]
    $t->hmsfjz              # [12, 34, 56, 12, 34, 16]
    $t->time                # same as $t->hms
    $t->alltime             # same as $t->hmsfjz
    $t->YMD                 # [2000,  2,   29]
    $t->MDY                 # [   2, 29, 2000]
    $t->DMY                 # [  29,  2, 2000]
    $t->CYMD                # [  20,  0,    2, 29]
    $t->date                # same as $t->YMD
    $t->alldate             # same as $t->CYMD
    $t->CYMDhmsfjz          # [  20,  0,    2, 29, 12, 13, 56, 12, 13, 16]
    $t->pt7                 # same as $t->YMDhmsf
    $t->all                 # same as $t->CYMDhmsfjz
    $t->dt                  # same as $t->CYMDhmsfjz
    "$t"                    # same as $t->CYMDhmsfjz except only prints
                            #   fields which are "used" which by default
                            #   is the same as the $t->YMDhmsf of pt7()

  Method names can be in any case with the following exceptions.  
    Special handling exists to resolve ambiguity between the Month && 
    minute fields.  If a lowercase 'm' is used adjacent to a 'y' or 'd'
    of either case, it is interpreted as Month.  Otherwise, the case of 
    the 'm' distinguishes Month from minute.  An uppercase 'M' is ALWAYS
    Month.  An adjacent uppercase 'H' or 'S' will not turn an uppercase
    'M' into minute.  Method names which need to specify Month or minute
    fields can also optionally specify the distinguishing vowel 
    ('o' or 'i') instead of 'M' or 'm'.

    $t->ymd                 # same as $t->YMD
    $t->dmy                 # same as $t->DMY
    $t->MmMm                # Month minute Month minute
    $t->HMS                 # hour Month second! NOT same as $t->hms 
    $t->yod                 # same as $t->YMD
    $t->chmod               # Century hour minute Month Day
    $t->FooIsMyJoy          # frame Month Month minute second Month Year
                            #   jink Month Year

  After importing this module, when you use localtime or gmtime in a
    scalar context, you DO NOT get a special Time::PT object like you
    would when using Time::Piece.  This module relies on a new() 
    constructor instead.  The following methods are available on 
    Time::PT objects though && remain as similar to Time::Piece
    functionality as makes sense.

    $t->frm                 # also as $t->frame && $t->subsecond
    $t->sec                 # also available as $t->second
    $t->min                 # also available as $t->minute
    $t->hour                # 24 hour
    $t->mday                # also available as $t->day_of_month
    $t->mon                 # 1 = January
    $t->_mon                # 0 = January
    $t->monname             # Feb
    $t->month               # same as $t->mon
      *NOTE* The above definition is different from the Time::Piece 
        interface which defines month() the same as monname() instead
        of mon().
    $t->fullmonth           # February
    $t->year                # based at 0 (year 0 AD is, of course 1 BC)
    $t->_year               # year minus 1900
    $t->yy                  # 2 digit year
    $t->wday                # 1 = Sunday
    $t->_wday               # 0 = Sunday
    $t->day_of_week         # 0 = Sunday
    $t->wdayname            # Tue
    $t->day                 # same as mday
      *NOTE* The above definition is different from the Time::Piece 
        interface which defines day() the same as wdayname() instead
        of mday().  I'd rather have just day() be of-the-month.
    $t->fullday             # Tuesday
    $t->yday                # also available as $t->day_of_year, 0 = Jan 01
    $t->isdst               # also available as $t->daylight_savings

      The following functions return a list of the named fields.  The
        return value can be joined with any desirable delimiter like:
            join(':', $t->hms)
        but the functions also can take a list of parameters to update
        the corresponding named fields like:
            $t->YMD( 2003, 12, 8 )
        would assign a new date of December 8th, 2003 to $t.
    $t->hms                 # [12, 34, 56]
    $t->hmsf                # [12, 34, 56, 12]
    $t->time                # same as $t->hmsf

    $t->ymd                 # [2000,  2, 29]
    $t->date                # same as $t->ymd
    $t->mdy                 # [ 2, 29, 2000]
    $t->dmy                 # [29,  2, 2000]
    $t->datetime            # 2000-02-29T12:34:56            (ISO 8601)
    $t->expand              # Tue Feb 29 12:34:56:12 2000
    $t->cdate               # same as $t->expand
    $t->compress            # 02TCYuC
    "$t"                    # same as $t->compress

    $t->is_leap_year        # true if it is
    $t->month_last_day      # 28-31

    $t->time_separator($s)  # set the default separator (default ":")
    $t->date_separator($s)  # set the default separator (default "-")
    $t->day_list(@days)     # set the default weekdays
    $t->mon_list(@days)     # set the default months

Local Locales

  Both wdayname() && monname() can accept the same list parameter 
    as day_list() && mon_list() respectively for temporary help with
    simple localization.

    my @days = ( 'Yom Rishone', 'Yom Shayni', 'Yom Shlishi', 'Yom Revi\'i', 
                 'Yom Khahmishi', 'Yom Hashishi', 'Shabbat' );

    my $hebrew_day = pt->wdayname(@days);
                   # pt->monname() can be used similarly

  To update the global lists, use:

    Time::PT::day_list(@days);
      &&
    Time::PT::mon_list(@months);

Calculations

  PT object strings (both in normal initialization && printing) grow
    left-to-right starting from the Year to specify whatever precision
    you need while Frame objects grow right-to-left from the frame field.

  It's possible to use simple addition and subtraction of objects:

    use Time::Frame;
    
    my $cur_pt       = Time::PT->new();# Dhmsf
    my $one_week     = Time::Frame->new('70000');
    my $one_week_ago = $cur_pt - $one_week;

  If a calculation is done with a raw string parameter instead of an
    instantiated object, the most likely appropriate object 
    constructor is called on it.  These init strings must adhere to
    the implied 'str' format for auto-creating objects;  I aim to
    support a much wider array of operations && to interoperate with 
    Time::Piece && Time::Seconds someday but not yet.

    my $cur_pt             = Time::PT->new();# Dhmsf
    my $half_hour_from_now = $cur_pt + 'U00';

  The following are valid (where $t0 and $t1 are Time::PT objects
    && $f is a Time::Frame object):

    $t0 - $t1;  # returns Time::Frame object
    $t0 - '63'; # returns Time::PT object
    $t0 + $f;   # returns Time::PT object

Comparisons

  All normal numerical && string comparisons should work reasonably on
    Time::PT objects: "<",  ">",  "<=", ">=", "<=>", "==" &&  "!="
                     "lt", "gt", "le", "ge", "cmp", "eq" and "ne"

YYYY-MM-DDThh:mm:ss

  The ISO 8601 standard defines the date format to be YYYY-MM-DD, and
    the time format to be hh:mm:ss (24 hour clock), and if combined,
    they should be concatenated with date first and with a capital 'T'
    in front of the time.

Week Number

  The ISO 8601 standard specifies that weeks begin on Monday and the first
    week of the year is the one that includes both January 4th and the
    first Thursday of the year.  In other words, if the first Monday of
    January is the 2nd, 3rd, or 4th, the preceding days are part of the 
    final week of the prior year.  Week numbers range from 1 to 53.

NOTES

  Whenever individual Time::PT attributes are going to be 
    printed or an entire object can be printed with multi-colors,
    the following mapping should be employed whenever possible:
           D      Century -> DarkRed
           A      Year    -> Red
           T      Month   -> Orange
           E      Day     -> Yellow
                   hour   -> Green
            t      minute -> Cyan
            i      second -> Blue
            m      frame  -> Purple
            e      jink   -> DarkPurple
                   zone   -> Grey or White
  Please see the color() member function in the USAGE section.

  There's some weird behavior for PipTimes created with a zero month
    or day field since both are 1-based.  I aim to fix all these bugs
    but be warned that this issue may be causing math errors for a bit.

  I hope you find Time::PT useful.  Please feel free to e-mail
    me any suggestions || coding tips || notes of appreciation 
    ("app-ree-see-ay-shun").  Thank you.  TTFN.

CHANGES

  Revision history for Perl extension Time::PT:

- 1.0.3CCA2VC  Fri Dec 12 10:02:31:12 2003

  * removed indenting from POD NAME section

- 1.0.3CBIQv7  Thu Dec 11 18:26:57:07 2003

  * updated test.pl to use normal comments

- 1.0.3CB7Vxh  Thu Dec 11 07:31:59:43 2003

  * added HTML color option && prepared for release

- 1.0.3CA8ipi  Wed Dec 10 08:44:51:44 2003

  * built class to inherit from Time::Fields && mimic Time::Piece

- 1.0.37VG26k  Thu Jul 31 16:02:06:46 2003

  * original version

INSTALL

  Please run:
        `perl -MCPAN -e "install Time::PT"`
    or uncompress the package && run the standard:
        `perl Makefile.PL; make; make test; make install`

FILES

  Time::PT requires:
    Carp                to allow errors to croak() from calling sub
    Math::BaseCnv       to handle simple number-bass conversion
    Time::DayOfWeek       also stores global day && month names
    Time::DaysInMonth   
    Time::HiRes         to provide subsecond time precision
    Time::Local         to turn epoch seconds back into a real date
    Time::Zone;         not utilized yet
    Time::Fields        to provide underlying object structure
    Time::Frame         to represent spans of time

SEE ALSO

  Time::Frame

LICENSE

  Most source code should be Free!
    Code I have lawful authority over is && shall be!
  Copyright: (c) 2003, Pip Stuart.  All rights reserved.
  Copyleft :  I license this software under the GNU General Public
    License (version 2).  Please consult the Free Software Foundation
    (http://www.fsf.org) for important information about your freedom.

AUTHOR

  Pip Stuart <Pip@CPAN.org>

