Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Details

Calculations
Design Goals
Tradeoffs: Stability, Predictability, and Approximations
Serialization
Terminology
References
Build-Compiler Information
Tests
Change History
Acknowledgements

Calculations

Timepoints -- Durations -- Intervals (Periods) -- Special Value Handling

Timepoints

This section describes some of basic arithmetic rules that can be performed with timepoints. In general, Timepoints support basic arithmetic in conjunction with Durations as follows:

      Timepoint + Duration  --> Timepoint
      Timepoint - Duration  --> Timepoint
      Timepoint - Timepoint --> Duration
    

Unlike regular numeric types, the following operations are undefined:

      Duration + Timepoint  --> Undefined
      Duration - Timepoint  --> Undefined
      Timepoint + Timepoint --> Undefined
    

Durations

Durations represent a length of time and can have positive and negative values. It is frequently useful to be able to perform calculations with other durations and with simple integral values. The following describes these calculations:

      Duration + Duration  --> Duration
      Duration - Duration  --> Duration
      
      Duration * Integer   --> Duration
      Integer  * Duration  --> Duration
      Duration / Integer   --> Duration  (Integer Division rules)
    

Intervals (Periods)

Interval logic is extremely useful for simplifying many 'calculations' for dates and times. The following describes the operations provided by periods which are based on half-open range. The following operations calculate new time periods based on two input time periods:

      Timeperiod intersection Timeperiod --> Timeperiod (null interval if no intersection)
      Timeperiod merge Timeperiod        --> Timeperiod (null interval if no intersection)
      Timeperiod shift Duration          --> Timeperiod (shift start and end by duration amount)
    

In addition, periods support various queries that calculate boolean results. The first set is caluculations with other time periods:

      Timeperiod == Timeperiod           --> bool
      Timeperiod < Timeperiod            --> bool (true if lhs.last <= rhs.begin)
      Timeperiod intersects Timeperiod   --> bool
      Timeperiod contains Timeperiod     --> bool
      Timeperiod is_adjacent Timeperiod  --> bool 
    

The following calculations are performed versus the Timepoint.

      Timeperiod contains Timepoint      --> bool
      Timeperiod is_before Timepoint     --> bool
      Timeperiod is_after Timepoint      --> bool 
    

Special Value Handling

For many temporal problems it is useful for Duration and Timepoint types to support special values such as Not A Date Time (NADT) and infinity. In general special values such as Not A Date Time (NADT) and infinity should follow rules like floating point values. Note that it should be possible to configure NADT based systems to throw an exception instead of result in NADT.

      Timepoint(NADT) + Duration --> Timepoint(NADT)
      Timepoint(∞) + Duration    --> Timepoint(∞)
      Timepoint + Duration(∞)    --> Timepoint(∞)
      Timepoint - Duration(∞)    --> Timepoint(-∞)
    

When performing operations on both positive and negative infinities, the library will produce results consistent with the following.

      Timepoint(+∞) + Duration(-∞) --> NADT
      Duration(+∞) + Duration(-∞)  --> NADT
      Duration(±∞) * Zero          --> NADT
      
      Duration(∞) * Integer(Not Zero) --> Duration(∞)
      Duration(+∞) * -Integer         --> Duration(-∞)
      Duration(∞) / Integer           --> Duration(∞)
    

Design Goals

Category Description Functions
Interfaces Provide concrete classes for manipulation of dates and times
  • date, time, date_duration, time_duration, date_period, time_period, etc
  • support for infinity - positive infinity, negative infinity
  • iterators over time and date ranges
  • allow date and time implementations to be separate as much as possible
Calculation Provide a basis for performing efficient time calculations
  • days between dates
  • durations of times
  • durations of dates and times together
Representation Flexibility Provide the maximum possible reusability and flexibility
  • traits based customization of internal representations for size versus resolution control
  • Allowing the use of different epochs and resolution (eg: seconds versus microseconds, dates starting at the year 2000 versus dates starting in 1700)
  • Options for configuring unique calendar representations (Gregorian + others)
  • the use of Julian Day number and the conversion between this and the Gregorian/Julian calendar date
  • Allow for flexible adjustments including leap seconds
Date Calculations Provide tools for date calculations
  • provide basis for calculation of complex event specs like holidays
  • calendar to calendar conversions
  • provide for ability to extend to new calendar systems
Time Calculations Provide concrete classes for manipulation of time
  • provide the ability to handle cross time-zone issues
  • provide adjustments for daylight savings time (summer time)
Clock Interfaces Provide classes for retrieving time current time
  • access to a network / high resolution time sources
  • retrieving the current date time information to populate classes
I/O Interfaces Provide input and output for time including
  • multi-lingual support
  • provide ISO8601 compliant time facet
  • use I/O facets for different local behavior

Tradeoffs: Stability, Predictability, and Approximations

Unavoidable Trade-offs

The library does its best to provide everything a user could want, but there are certain inherent constraints that limit what any temporal library can do. Specifically, a user must choose which two of the following three capabilities are desired in any particular application:

  • exact agreement with wall-clock time
  • accurate math, e.g. duration calculations
  • ability to handle timepoints in the future

Some libraries may implicitly promise to deliver all three, but if you actually put them to the test, only two can be true at once. This limitation is not a deficiency in the design or implementation of any particular library; rather it is a consequence of the way different time systems are defined by international standards. Let's look at each of the three cases:

If you want exact agreement with wall-clock time, you must use either UTC or local time. If you compute a duration by subtracting one UTC time from another and you want an answer accurate to the second, the two times must not be too far in the future because leap seconds affect the count but are only determined about 6 months in advance. With local times a future duration calculation could be off by an entire hour, since legislatures can and do change DST rules at will.

If you want to handle wall-clock times in the future, you won't be able (in the general case) to calculate exact durations, for the same reasons described above.

If you want accurate calculations with future times, you will have to use TAI or an equivalent, but the mapping from TAI to UTC or local time depends on leap seconds, so you will not have exact agreement with wall-clock time.

Stability, Predictability, and Approximations

Here is some underlying theory that helps to explain what's going on. Remember that a temporal type, like any abstract data type (ADT), is a set of values together with operations on those values.

Stability

The representation of a type is stable if the bit pattern associated with a given value does not change over time. A type with an unstable representation is unlikely to be of much use to anyone, so we will insist that any temporal library use only stable representations.

An operation on a type is stable if the result of applying the operation to a particular operand(s) does not change over time.

Predictability

Sets are most often classified into two categories: well-defined and ill-defined. Since a type is a set, we can extend these definitions to cover types. For any type T, there must be a predicate is_member( x ) which determines whether a value x is a member of type T. This predicate must return true, false, or dont_know.

If for all x, is_member( x ) returns either true or false, we say the set T is well-defined.

If for any x, is_member( x ) returns dont_know, we say the set T is ill-defined.

Those are the rules normally used in math. However, because of the special characteristics of temporal types, it is useful to refine this view and create a third category as follows:

For any temporal type T, there must be a predicate is_member( x, t ) which determines whether a value x is a member of T. The parameter t represents the time when the predicate is evaluated. For each xi, there must be a time ti and a value v such that:

  • v = true or v = false, and
  • for all t < ti, is_member( xi, t ) returns dont_know, and
  • for all t >= ti, is_member( xi, t ) returns v.

ti is thus the time when we "find out" whether xi is a member of T. Now we can define three categories of temporal types:

If for all xi, ti = negative infinity, we say the type T is predictable.

If for some xi, ti = positive infinity, we say the type T is ill-formed.

Otherwise we say the type T is unpredictable (this implies that for some xi, ti is finite).

Ill-formed sets are not of much practical use, so we will not discuss them further. In plain english the above simply says that all the values of a predictable type are known ahead of time, but some values of an unpredictable type are not known until some particular time.

Stability of Operations

Predictable types have a couple of important properties:

  • there is an order-preserving mapping from their elements onto a set of consecutive integers, and
  • duration operations on their values are stable

The practical effect of this is that duration calculations can be implemented with simple integer subtraction. Examples of predictable types are TAI timepoints and Gregorian dates.

Unpredictable types have exactly the opposite properties:

  • there is no order-preserving mapping from their elements onto a set of consecutive integers, and
  • duration operations on their values are not stable.

Examples of unpredictable types are UTC timepoints and Local Time timepoints.

We can refine this a little by saying that a range within an unpredicatable type can be predictable, and operations performed entirely on values within that range will be stable. For example, the range of UTC timepoints from 1970-01-01 through the present is predictable, so calculations of durations within that range will be stable.

Approximations

These limitations are problematical, because important temporal types like UTC and Local Time are in fact unpredictable, and therefore operations on them are sometimes unstable. Yet as a practical matter we often want to perform this kind of operation, such as computing the duration between two timepoints in the future that are specified in Local Time.

The best the library can do is to provide an approximation, which is generally possible and for most purposes will be good enough. Of course the documentation must specify when an answer will be approximate (and thus unstable) and how big the error may be. In many respects calculating with unpredictable sets is analogous to the use of floating point numbers, for which results are expected to only be approximately correct. Calculating with predictable sets would then be analogous to the user of integers, where results are expected to be exact.

For situations where exact answers are required or instability cannot be tolerated, the user must be able to specify this, and then the library should throw an exception if the user requests a computation for which an exact, stable answer is not possible.

Serialization

The boost::date_time library is compatible with the boost::serialization library's text and xml archives. The list of classes that are serializable are:

boost::gregorian

boost::posix_time

No extra steps are required to build the date_time library for serialization use.

Example text_archive usage:

      using namespace boost::posix_time;
      using namespace boost::gregorian;
      ptime pt(date(2002, Feb, 14)), hours(10)), pt2(not_a_date_time);
      std::ofstream ofs("tmp_file");
      archive::test_oarchive oa(ofs);
      oa << pt;                        // NOTE: no macro
      ofs.close();
      std::ifstream ifs("tmp_file");
      archive::text_iarchive ia(ifs);
      ia >> pt2;                       // NOTE: no macro
      ifs.close();
      pt == pt2; // true

Example xml_archive usage:

      using namespace boost::gregorian;
      date d(2002, Feb, 14), d2(not_a_date_time);
      std::ofstream ofs("tmp_file");
      archive::xml_oarchive oa(ofs);
      oa << BOOST_SERIALIZATION_NVP(d); // macro required for xml_archive
      ofs.close();
      std::ifstream ifs("tmp_file");
      archive::xml_iarchive ia(ifs);
      ia >> BOOST_SERIALIZATION_NVP(d2); // macro required for xml_archive
      ifs.close();
      d == d2; // true

To use the date_time serialization code, the proper header files must be explicitly included. The header files are:

      boost/date_time/gregorian/greg_serialize.hpp

and

      boost/date_time/posix_time/time_serialize.hpp

Terminology

The following are a number of terms relevant to the date-time domain.

A taxonomy of temporal types:

  • Timepoint -- Specifier for a location in the time continuum. Similar to a number on a ruler.
  • Timelength -- A duration of time unattached to any point on the time continuum.
  • Timeinterval -- A duration of time attached to a specific point in the time continuum.

And some other terms:

  • Accuracy -- A measure of error, the difference between the reading of a clock and the true time.
  • Calendar System -- A system for labeling time points with day level resolution.
  • Clock Device -- A software component (tied to some hardware) that provides the current date or time with respect to a calendar or clock system.
  • Precision -- A measure of repeatability of a clock.
  • Resolution -- A specification of the smallest representable duration (eg: 1 second, 1 century) for a clock/calendar system or temporal type.
  • Stability -- The property of a class which says that the underlying representation (implementation) associated with a particular (abstract) value will never change.
  • Time System -- A system for labeling time points with higher resolution than day-level.

Some standard date-time terminology:

  • Epoch -- Starting time point of a calendar or clock system.
  • DST -- Daylight savings time - a local time adjustment made in some regions during the summer to shift the clock time of the daylight hours
  • Time zone -- A region of the earth that provides for a 'local time' defined by DST rules and UT offset.
  • UTC Time -- Coordinated Universal Time - Civil time system as measured at longitude zero. Kept adjusted to earth rotation by use of leap seconds. Also known as Zulu Time. Replaced the similar system known as Greenwich Mean Time. For more see http://aa.usno.navy.mil/faq/docs/UT.html
  • TAI Time -- A high-accuracy monotonic (need better term) time system measured to .1 microsecond resolution by atomic clocks around the world. Not adjusted to earth rotation. For more see http://www.bipm.fr/enus/5_Scientific/c_time/time_server.html

Some more experimental ones:

  • Local Time -- A time measured in a specific location of the universe.
  • Time Label -- A tuple that either completely or partially specifies a specific date-time with respect to a calendar or clock system. This is the year-month-day representation.
  • Adjusting Time Length -- A duration that represents varying physical durations depending on the moment in time. For example, a 1 month duration is typically not a fixed number of days and it depends on the date it is measured from to determine the actual length.

These are design sorts of terms:

  • Generation function -- A function that generates a specific set of time points, lengths, or intervals based on one or more parameters.

References

Date Calendar References

Time

Other C/C++ Libraries

JAVA Date & Time Library Quick Reference

Scripting Language Libraries

Related Commercial and Fanciful Pages

Resolution, Precision, and Accuracy

Build-Compiler Information

Overview -- Compilation Options -- Compiler/Portability Notes -- Directory Structure -- Required Boost Libraries

Overview

The library has several functions that require the creation of a library file. The Jamfile in the build directory will produce a "static" library (libboost_date_time) and a "dynamic/shared" library (boost_date_time) that contains these functions.

Compilation Options

By default the posix_time system uses a 64 bit integer and a 32 bit integer internally to provide nano-second level resolutions. As an alternative, a single 64 bit integer can be used to provide a microsecond level resolution. This alternative implementation may provide better performance and more compact memory usage for many applications that do not require nano-second resolutions.

To use the alternate resolution (64 bit microsecond) the variable BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG must be defined in the library users project files (ie Makefile, Jamfile, etc). This macro is not used by the Gregorian system and therefore has no effect when building the library.

Compiler/Portability Notes

The Boost Date-Time library has been built and tested with many compilers. However, some compilers and standard libraries have issues. While some of these issues can be worked around, others are difficult to work around. The following compilers fully support all aspects of the library:

  • GCC 3.2-7 on Linux
  • GCC 2.95.3 with STLport-4.5.3 on Linux
  • GCC 3.1 (cygwin)
  • MSVC 7.1

In particular, a lack of support for standard locales limits the ability of the library to support iostream based input output. For these compilers a set of more limited string based input-output is provided. The compilers/standard libraries with this limitation include:

  • GCC 2.9x on Linux
  • Borland 5.1.1 and 5.6
  • MSVC 6 SP5

Other compilers such as Comeau and Metroworks have been tested successfully with earlier versions of the library.

Directory Structure

The directory tree has the following structure:

      /boost/date_time                    -- common headers and template code
      /boost/date_time/gregoran           -- Gregorian calendar system header files
      /boost/date_time/posix_time         -- posix time system headers
      /libs/date_time/build               -- build files and output directory
      /libs/date_time/test                -- test battery for generic code
      /libs/date_time/test/gregorian      -- test battery for the Gregorian system
      /libs/date_time/test/posix_time     -- test battery for the posix_time system
      /libs/date_time/examples/posix_time -- time example programs
      /libs/date_time/examples/gregorian  -- nifty example programs
      /libs/date_time/src/gregorian       -- cpp files for libboost_date_time
      /libs/date_time/src/posix_time      -- empty (one file, but no source code...)
    

Required Boost Libraries

The library depends on

so at least these libraries need to be installed.

Tests

The library provides a large number of tests in the

      libs/date_time/test
      libs/date_time/test/gregorian
      libs/date_time/test/posix_time
    

directories. Building and executing these tests assures that the installation is correct and that the library is functioning correctly. In addition, these tests facilitate the porting to new compilers. Finally, the tests provide examples of many functions not explicitly described in the usage examples.

Change History

Changes from Boost 1.31 to 1.32 (date_time 1.02 to 1.03)

Type Description
Bug Fix Snap to end of month behavior corrected for year_functor. Previously, starting from 2000-Feb-28 (leap year and not end of month) and iterating through the next leap year would result in 2004-Feb-29 instead of 2004-Feb-28. This behavior has been corrected to produce the correct result of 2004-Feb-28. Thanks to Bart Garst for this change.
Feature Free function for creating a ptime object from a FILETIME struct. This function is only available on platforms that define BOOST_HAS_FTIME.
Feature Microsecond time clock is now available on most windows compilers as well as Unix.
Feature Use of the boost::serialization library is now available with most of the date_time classes. Classes capable of serialization are: date_generator classes, date, days, date_period, greg_month, greg_weekday, greg_day, ptime, time_duration, and time_period. Thanks to Bart Garst for this change.
Feature Functions added to convert date and time classes to wstring. The library now provides to_*_wstring as well as to_*_string functions for: simple, iso, iso_extended, and sql for dates and compilers that support wstrings. Thanks to Bart Garst for this change.
Feature Period classes now handle zero length and NULL periods correctly. A NULL period is a period with a negative length. Thanks to Frank Wolf and Bart Garst for this change.
Feature Added end_of_month function to gregorian::date to return the last day of the current month represented by the date. Result is undefined for not_a_date_time or infinities.
Bug Fix Removed incorrect usage of BOOST_NO_CWCHAR macro throughout library.
Feature New names added for some date classes. Original names are still valid but may some day be deprecated. Changes are:
date_duration is now days
nth_kday_of_month is now nth_day_of_the_week_in_month
first_kday_of_month is now first_day_of_the_week_in_month
last_kday_of_month is now last_day_of_the_week_in_month
first_kday_after is now first_day_of_the_week_after
first_kday_before is now first_day_of_the_week_before
Feature Free functions for date generators added. Functions are: days_until_weekday, days_before_weekday, next_weekday, and previous_weekday.
              days days_until_weekday(date, greg_weekday);
              days days_before_weekday(date, greg_weekday);
              date next_weekday(date, greg_weekday);
              date previous_weekday(date, greg_weekday);
Thanks to Bart Garst for this change.
Feature New experimental duration types added for months, years, and weeks. These classes also provide mathematical operators for use with date and time classes. Be aware that adding of months or years a time or date past the 28th of a month may show non-normal mathematical properties. This is a result of 'end-of-month' snapping used in the calculation. The last example below illustrates the issue.
              months m(12);
              years y(1);
              m == y; // true
              days d(7);
              weeks w(1);
              d == w; // true
              ptime t(...);
              t += months(3);
              date d(2004,Jan,30);
              d += months(1); //2004-Feb-29
              d -= months(1); //2004-Jan-29
            
Input streaming is not yet implemented for these types. Thanks to Bart Garst for this change.
Feature Unifying base class for date_generators brought in to gregorian namespace. See Print Holidays Example.
Feature Added constructors for date and ptime that allow for default construction (both) and special values construction (ptime, both now support this). Default constructors initialize the objects to not_a_date_time (NADT).
              ptime p_nadt(not_a_date_time), p_posinf(pos_infin);
              ptime p; // p == NADT
              date d;  // d == NADT
            
Thanks to Bart Garst for this change.
Feature Output streaming now supports wide stream output on compiler / standard library combinations that support wide streams. This allows code like:
	      std::wstringstream wss;
	      date d(2003,Aug,21);
	      wss << d;
Thanks to Bart Garst for this change.
Feature Input streaming for date and time types is now supported on both wide and narrow streams:
	      date d(not_a_date_time);
	      std::stringstream ss("2000-FEB-29");
	      ss >> d; //Feb 29th, 2000
              std::wstringstream ws("2000-FEB-29");
              ws >> d; //Feb 29th, 2000
            
Thanks to Bart Garst for this change.
Bug Fix Fixed bug in duration_from_string() where a string formatted with less than full amount of fractional digits created an incorrect time_duration. With microsecond resolution for time durations the string "1:01:01.010" created a time duration of 01:01:01.000010 instead of 01:01:01.010000
Bug Fix Fixed the special value constructor for gregorian::date and posix_time::ptime when constructing with min_date_time or max_date_time. The wrong value was constructed for these.

Changes from Boost 1.30 to 1.31 (date_time 1.01 to 1.02)

Type Description
Bug Fix Build configuration updated so dll, statically, and dynamically linkable library files are now produced with MSVC compilers. See Build/Compiler Information for more details.
Bug Fix Time_duration from_string is now correctly constructed from a negative value. (ie "-0:39:00.000") Code provided by Bart Garst.
Bug Fix Fixed many MSVC compiler warnings when compiled with warning level 4.
Feature Added prefix decrement operator (--) for date and time iterators. See Time Iterators and Date Iterators for more details. Code provided by Bart Garst.
Feature Special_values functionality added for date_duration, time_duration and time classes. Code provided by Bart Garst.
Bug Fix Fixed time_duration_traits calculation bug which was causing time duration to be limited to 32bit range even when 64 bits were available. Thanks to Joe de Guzman for tracking this down.
Bug Fix Provided additional operators for duration types (eg: date_duration, time_duration). This includes dividable by integer and fixes to allow +=, -= operators. Thanks to Bart Garst for writing this code. Also, the documentation of Calculations has been improved.
Bug Fix Added typedefs to boost::gregorian gregorian_types.hpp various date_generator function classes.
Feature Added from_time_t function to convert time_t to a ptime.
Feature Added a span function for combining periods. See date period and time period docs.
Feature Added a function to time_duration to get the total number of seconds in a duration truncating any fractional seconds. In addition, other resolutions were added to allow for easy conversions. For example
               seconds(1).total_milliseconds() == 1000
               seconds(1).total_microseconds() == 1000000
               hours(1).total_milliseconds() == 3600*1000 //3600 sec/hour
               seconds(1).total_nanoseconds() == 1000000000
            
Feature Added output streaming operators for the date generator classes - partial_date, first_kday_after, first_kday_before, etc. Thanks to Bart Garst for this work.
Feature Added unary- operators for durations for reversing the sign of a time duration. For example:
	      time_duration td(5,0,0); //5 hours
	      td = -td; //-5 hours
Thanks to Bart Garst for this work.
Feature Added support for parsing strings with 'month names'. Thus creating a date object from string now accepts multiple formats ("2003-10-31","2003-Oct-31", and "2003-October-31"). Thus, date d = from_simple_string("2003-Feb-27") is now allowed. A bad month name string ( from_simple_string("2003-SomeBogusMonthName-27")) will cause a bad_month exception. On most compilers the string compare is case insensitive. Thanks to Bart Garst for this work.
Feature In addition to support for month names or numbers, functions have been added to create date objects from multi-ordered date strings. Ex: "January-21-2002", "2002-Jan-21", and "21-Jan-2003". See Date Class for more details.
Bug-Fix Various documentation fixes. Thanks to Bart Garst for updates.

Changes from Boost 1.29 to 1.30 (date_time 1.00 to 1.01)

Notice: The interface to the partial_date class (see date_algorithms) was changed. The order of construction parameters was changed which will cause some code to fail execution. This change was made to facilitate more generic local time adjustment code. Thus instead of specifying partial_date pd(Dec,25) the code needs to be changed to partial_date pd(25, Dec);

Type Description
Bug Fix Added new experimental feature for Daylight Savings Time calculations. This allows traits based specification of dst rules.
Feature Added new interfaces to calculate julian day and modified julian day to the gregorian date class. See boost::gregorian::date.
Feature Add new interface to calculate iso 8601 week number for a date. See boost::gregorian::date.
Feature Add an iso 8601 time date-time format (eg: YYYYMMDDTHHHMMSS) parsing function. See Class ptime for more information.
Feature Added a length function to the period template so that both date_periods and time_periods will now support this function.
Bug Fix Split Jamfiles so that libs/date_time/build/Jamfile only builds library and /libs/date_time/libs/test/Jamfile which runs tests.
Bug Fix Fixed many minor documentation issues.
Bug Fix Removed the DATE_TIME_INLINE macro which was causing link errors. This macro is no longer needed in projects using the library.
Bug Fix Added missing typedef for year_iterator to gregorian_types.hpp
Bug Fix Fixed problem with gregorian ostream operators that prevented the use of wide streams.
Bug-Fix Tighten error handling for dates so that date(2002, 2, 29) will throw a bad_day_of_month exception. Previously the date would be incorrectly constructed. Reported by sourceforge bug: 628054 among others.

Acknowledgements

Many people have contributed to the development of this library. In particular Hugo Duncan and Joel de Guzman for help with porting to various compilers. For initial development of concepts and design Corwin Joy and Michael Kenniston deserve special thanks. Also extra thanks to Michael for writing up the theory and tradeoffs part of the documentation. Dave Zumbro for initial inspiration and sage thoughts. Many thanks to boost reviewers and users including: William Seymour, Kjell Elster, Beman Dawes, Gary Powell, Andrew Maclean, William Kempf, Peter Dimov, Chris Little, David Moore, Darin Adler, Gennadiy Rozental, Joachim Achtzehnter, Paul Bristow, Jan Langer, Mark Rodgers, Glen Knowles, Matthew Denman, and George Heintzelman.

Copyright © 2001-2004 CrystalClear Software, Inc

PrevUpHomeNext