wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
#define DEBUG
/*
* ClockCmd.java --
*
*       Implements the built-in "clock" Tcl command.
*
* Copyright (c) 1998-2000 Christian Krone.
* Copyright (c) 1997 Cornell University.
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
* Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
* 
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: ClockCmd.java,v 1.6 2003/02/03 04:48:46 mdejong Exp $
*
*/
using System;
using System.Globalization;
using System.Text;
using System.Collections;


namespace tcl.lang
{

  /// <summary> This class implements the built-in "clock" command in Tcl.</summary>

  class ClockCmd : Command
  {

    private static readonly string[] validCmds = new string[] { "clicks", "format", "scan", "seconds" };

    private const int CMD_CLICKS = 0;
    private const int CMD_FORMAT = 1;
    private const int CMD_SCAN = 2;
    private const int CMD_SECONDS = 3;

    private static readonly string[] clicksOpts = new string[] { "-milliseconds" };

    private const int OPT_CLICKS_MILLISECONDS = 0;

    private static readonly string[] formatOpts = new string[] { "-format", "-gmt" };

    private const int OPT_FORMAT_FORMAT = 0;
    private const int OPT_FORMAT_GMT = 1;

    private static readonly string[] scanOpts = new string[] { "-base", "-gmt" };

    private const int OPT_SCAN_BASE = 0;
    private const int OPT_SCAN_GMT = 1;

    internal const int EPOCH_YEAR = 1970;
    internal const int MILLIS_PER_HOUR = 60 * 60 * 1000;
    public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv )
    {
      int clockVal; // Time value as seconds of epoch.
      string dateString; // Time value as string.
      int argIx; // Counter over arguments.
      string format = null; // User specified format string.
      bool useGmt = false; // User specified flag to use gmt.
      TclObject baseObj = null; // User specified raw value of baseClock.
      System.DateTime baseClock; // User specified time value.
      System.DateTime date; // Parsed date value.

      if ( objv.Length < 2 )
      {
        throw new TclNumArgsException( interp, 1, objv, "option ?arg ...?" );
      }
      int cmd = TclIndex.get( interp, objv[1], validCmds, "option", 0 );

      switch ( cmd )
      {

        case CMD_CLICKS:
          {
            if ( objv.Length > 3 )
            {
              throw new TclNumArgsException( interp, 2, objv, "?-milliseconds?" );
            }
            if ( objv.Length == 3 )
            {
              // We can safely ignore the -milliseconds options, since
              // we measure the clicks in milliseconds anyway...
              int clicksOpt = TclIndex.get( interp, objv[2], clicksOpts, "switch", 0 );
            }
            long millis = ( System.DateTime.Now.Ticks - 621355968000000000 ) / 10000;
            int clicks = (int)( millis % System.Int32.MaxValue );
            interp.setResult( clicks );
            break;
          }


        case CMD_FORMAT:
          {
            if ( ( objv.Length < 3 ) || ( objv.Length > 7 ) )
            {
              throw new TclNumArgsException( interp, 2, objv, "clockval ?-format string? ?-gmt boolean?" );
            }
            clockVal = TclInteger.get( interp, objv[2] );

            for ( argIx = 3; argIx + 1 < objv.Length; argIx += 2 )
            {
              int formatOpt = TclIndex.get( interp, objv[argIx], formatOpts, "switch", 0 );
              switch ( formatOpt )
              {

                case OPT_FORMAT_FORMAT:
                  {

                    format = objv[argIx + 1].ToString();
                    break;
                  }

                case OPT_FORMAT_GMT:
                  {
                    useGmt = TclBoolean.get( interp, objv[argIx + 1] );
                    break;
                  }
              }
            }
            if ( argIx < objv.Length )
            {
              throw new TclNumArgsException( interp, 2, objv, "clockval ?-format string? ?-gmt boolean?" );
            }
            FormatClock( interp, clockVal, useGmt, format );
            break;
          }


        case CMD_SCAN:
          {
            if ( ( objv.Length < 3 ) || ( objv.Length > 7 ) )
            {
              throw new TclNumArgsException( interp, 2, objv, "dateString ?-base clockValue? ?-gmt boolean?" );
            }

            dateString = objv[2].ToString();

            for ( argIx = 3; argIx + 1 < objv.Length; argIx += 2 )
            {
              int scanOpt = TclIndex.get( interp, objv[argIx], scanOpts, "switch", 0 );
              switch ( scanOpt )
              {

                case OPT_SCAN_BASE:
                  {
                    baseObj = objv[argIx + 1];
                    break;
                  }

                case OPT_SCAN_GMT:
                  {
                    useGmt = TclBoolean.get( interp, objv[argIx + 1] );
                    break;
                  }
              }
            }
            if ( argIx < objv.Length )
            {
              throw new TclNumArgsException( interp, 2, objv, "clockval ?-format string? ?-gmt boolean?" );
            }
            if ( baseObj != null )
            {
              long seconds = TclInteger.get( interp, baseObj );
              baseClock = new System.DateTime( (long)seconds * 10000 * 1000 + 621355968000000000 );
            }
            else
            {
              baseClock = System.DateTime.Now;
            }
            try
            {
              date = GetDate( dateString, baseClock, useGmt );
            }
            catch ( FormatException )
            {
              throw new TclException( interp, "unable to convert date-time string \"" + dateString + "\"" );
            }
            long millis = ( date.Ticks - 621355968000000000 ) / 10000;
            int seconds2 = (int)( millis / 1000 );
            interp.setResult( seconds2 );
            break;
          }


        case CMD_SECONDS:
          {
            if ( objv.Length != 2 )
            {
              throw new TclNumArgsException( interp, 2, objv, null );
            }
            long millis = ( System.DateTime.Now.Ticks - 621355968000000000 ) / 10000;
            int seconds = (int)( millis / 1000 );
            interp.setResult( seconds );
            break;
          }
      }
      return TCL.CompletionCode.RETURN;
    }
    private void FormatClock( Interp interp, int clockVal, bool useGMT, string format )
    {
      DateTime date = new DateTime( (long)clockVal * 10000 * 1000 + 621355968000000000 );

      DateTimeFormatInfo formatInfo = new DateTimeFormatInfo();
      string fmt, locFmt;

      GregorianCalendar calendar = new GregorianCalendar();

      System.Int32[] temp_int_array;
      temp_int_array = new System.Int32[3];
      temp_int_array[0] = 0;
      temp_int_array[1] = 0;
      temp_int_array[2] = 0;
      System.Int32[] fp = temp_int_array;
      StringBuilder result = new StringBuilder();

      if ( (System.Object)format == null )
      {
        format = new StringBuilder( "%a %b %d %H:%M:%S %Z %Y" ).ToString();
      }

      if ( useGMT )
      {
        date = date.ToUniversalTime();
      }
      if ( format.Equals( "%Q" ) )
      {
        // Enterprise Stardate. (seems to be Star Track fan coding)
        // ATK not tested 
        int trekYear = date.Year + 377 - 2323;
        int trekDay = ( date.DayOfYear * 1000 ) / ( calendar.IsLeapYear( date.Year ) ? 366 : 365 );
        int trekHour = ( 24 * 60 + date.Minute ) / 144;

        interp.setResult( "Stardate " + ( trekYear < 10 ? "0" : "" ) + ( trekYear * 1000 + trekDay ) + '.' + trekHour );
        return;
      }

      for ( int ix = 0; ix < format.Length; ix++ )
      {
        if ( format[ix] == '%' && ix + 1 < format.Length )
        {
          switch ( format[++ix] )
          {

            case '%':
              result.Append( '%' );
              break;

            case 'a':
              result.Append( date.ToString( "ddd", formatInfo ) );
              break;

            case 'A':
              result.Append( date.ToString( "dddd", formatInfo ) );
              break;
            case 'b':
            case 'h':
              result.Append( date.ToString( "MMM", formatInfo ) );
              break;
            case 'B':
              result.Append( date.ToString( "MMMM", formatInfo ) );
              break;
            case 'c':
              result.Append( date.ToString() );
              break;
            case 'C':
              int century = date.Year / 100;
              result.Append( ( century < 10 ? "0" : "" ) + century );
              break;
            case 'd':
              result.Append( date.ToString( "dd", formatInfo ) );
              break;
            case 'D':
              result.Append( date.ToString( "MM/dd/yy", formatInfo ) );
              break;
            case 'e':
              result.Append( date.ToString( "%d", formatInfo ) );
              break;
            case 'H':
              result.Append( date.ToString( "HH", formatInfo ) );
              break;
            case 'I':
              result.Append( date.ToString( "hh", formatInfo ) );
              break;
            case 'j':
              result.Append( date.Year.ToString( "0###" ) );
              break;
            case 'k':
              result.Append( date.ToString( "H", formatInfo ) );
              break;
            case 'l':
              result.Append( date.ToString( "%h", formatInfo ) );
              break;
            case 'm':
              // Month number (01 - 12). 
              result.Append( date.ToString( "MM", formatInfo ) );
              break;
            case 'M':
              // Minute (00 - 59). 
              result.Append( date.ToString( "mm", formatInfo ) );
              break;
            case 'n':
              // Insert a newline.
              result.Append( '\n' );
              break;
            case 'p':
              // AM/PM indicator. 
              result.Append( date.ToString( "tt", formatInfo ) );
              break;
            case 'r':
              // %r 
              //Time in a locale-specific "meridian" format. The "meridian" format in the default "C" locale is "%I:%M:%S %p". 
              result.Append( date.ToString( "hh:mm:ss tt", formatInfo ) );
              break;
            case 'R':
              //%R 
              //Time as %H:%M. 
              result.Append( date.ToString( "HH:MM", formatInfo ) );
              break;
            case 's':
              //%s 
              //Count of seconds since the epoch, expressed as a decimal integer. 
              result.Append( ( date.Ticks / 1000 ).ToString() );
              break;

            case 'S':
              //%S 
              //Seconds (00 - 59). 
              result.Append( date.ToString( "ss", formatInfo ) );
              break;
            case 't':
              //%t 
              //Insert a tab. 
              result.Append( '\t' );
              break;

            case 'T':
              //%T 
              //Time as %H:%M:%S. 
              result.Append( date.ToString( "HH:mm:ss", formatInfo ) );
              break;

            case 'u':
              //%u 
              //Weekday number (Monday = 1, Sunday = 7). 
              if ( date.DayOfWeek == DayOfWeek.Sunday )
              {
                result.Append( "7" );
              }
              else
              {
                result.Append( ( (int)date.DayOfWeek ).ToString() );
              }
              break;
            case 'U':
              //%U 
              //Week of year (00 - 52), Sunday is the first day of the week. 
              int weekS = GetWeek( date, System.DayOfWeek.Sunday, false );
              result.Append( ( weekS < 10 ? "0" : "" ) + weekS );
              break;

            case 'V':
              //%V 
              //Week of year according to ISO-8601 rules. Week 1 of a given year is the week containing 4 January. 
              int isoWeek = GetWeek( date, System.DayOfWeek.Monday, true );
              result.Append( ( isoWeek < 10 ? "0" : "" ) + isoWeek );
              break;

            case 'w':
              //%w 
              //Weekday number (Sunday = 0, Saturday = 6). 
              result.Append( ( (int)date.DayOfWeek ).ToString() );
              break;

            case 'W':
              //%W 
              //Week of year (00 - 52), Monday is the first day of the week. 
              int weekM = GetWeek( date, System.DayOfWeek.Monday, false );
              result.Append( ( weekM < 10 ? "0" : "" ) + weekM );
              break;
            case 'x':
              //%x 
              //Locale specific date format. The format for a date in the default "C" locale for Unix/Mac is "%m/%d/%y". On Windows, this value is the locale specific short date format, as specified in the Regional Options control panel settings. 
              result.Append( date.ToShortDateString() );
              break;

            case 'X':
              //%X 
              //Locale specific 24-hour time format. The format for a 24-hour time in the default "C" locale for Unix/Mac is "%H:%M:%S". On Windows, this value is the locale specific time format, as specified in the Regional Options control panel settings. 
              result.Append( date.ToShortTimeString() );
              break;
            case 'y':
              //%y 
              //Year without century (00 - 99). 
              result.Append( date.ToString( "yy", formatInfo ) );
              break;

            case 'Y':
              //%Y 
              //Year with century (e.g. 1990) 
              result.Append( date.ToString( "yyyy", formatInfo ) );
              break;
            case 'Z':
              //%Z 
              //Time zone name. 
              result.Append( date.ToString( "zzz", formatInfo ) );
              break;
            default:
              result.Append( format[ix] );
              break;
          }
        }
        else
        {
          result.Append( format[ix] );
        }
      }
      interp.setResult( result.ToString() );
    }
    private int GetWeek( DateTime date, System.DayOfWeek firstDayOfWeek, bool iso )
    {
      GregorianCalendar cal = new GregorianCalendar();
      CalendarWeekRule weekRule = CalendarWeekRule.FirstFullWeek;
      if ( iso )
      {
        firstDayOfWeek = System.DayOfWeek.Monday;
        weekRule = CalendarWeekRule.FirstFourDayWeek;
      }
      return cal.GetWeekOfYear( date, weekRule, firstDayOfWeek );
    }
    private void SetWeekday( TclDateTime calendar, ClockRelTimespan diff )
    // time difference to evaluate
    {
      int weekday = diff.getWeekday();
      int dayOrdinal = diff.DayOrdinal;

      // ATK
      //                        while (SupportClass.CalendarManager.manager.Get(calendar, SupportClass.CalendarManager.DAY_OF_WEEK) != weekday)
      //                        {
      //                                
      //                                calendar.add(SupportClass.CalendarManager.DATE, 1);
      //                        }
      //                        if (dayOrdinal > 1)
      //                        {
      //                                
      //                                calendar.add(SupportClass.CalendarManager.DATE, 7 * (dayOrdinal - 1));
      //                        }
    }
    private void SetOrdMonth( TclDateTime calendar, ClockRelTimespan diff )
    // time difference to evaluate
    {
      int month = diff.Months;
      int ordMonth = diff.OrdMonth;

      //                        calendar.add(SupportClass.CalendarManager.MONTH, 1); /* we want to get the next month... */
      //                        while (SupportClass.CalendarManager.manager.Get(calendar, SupportClass.CalendarManager.MONTH) != month)
      //                        {
      //                                calendar.add(SupportClass.CalendarManager.MONTH, 1);
      //                        }
      //                        if (ordMonth > 1)
      //                        {
      //                                calendar.add(SupportClass.CalendarManager.YEAR, ordMonth - 1);
      //                        }
      calendar.day = 1;
      calendar.hour = 0;
      calendar.minute = 0;
      calendar.second = 0;
    }
    private System.DateTime GetDate( string dateString, System.DateTime baseDate, bool useGMT )
    {
      if ( useGMT )
      {
        baseDate = baseDate.ToUniversalTime();
      }
      TclDateTime calendar = new TclDateTime();
      calendar.dateTime = baseDate;
      calendar.hour = 0;
      calendar.minute = 0;
      calendar.second = 0;
      calendar.millisecond = 0;

      ClockToken[] dt = GetTokens( dateString, false );

      System.Int32 parsePos = 0;
      ClockRelTimespan diff = new ClockRelTimespan();
      int hasTime = 0;
      int hasZone = 0;
      int hasDate = 0;
      int hasDay = 0;
      int hasOrdMonth = 0;
      int hasRel = 0;

      while ( parsePos < dt.Length )
      {
        if ( ParseTime( dt, ref parsePos, calendar ) )
        {
          hasTime++;
        }
        else if ( ParseZone( dt, ref parsePos, calendar ) )
        {
          hasZone++;
        }
        else if ( ParseIso( dt, ref parsePos, calendar ) )
        {
          hasDate++;
        }
        else if ( ParseDate( dt, ref parsePos, calendar ) )
        {
          hasDate++;
        }
        else if ( ParseDay( dt, ref parsePos, diff ) )
        {
          hasDay++;
        }
        else if ( ParseOrdMonth( dt, ref parsePos, diff ) )
        {
          hasOrdMonth++;
        }
        else if ( ParseRelSpec( dt, ref parsePos, diff ) )
        {
          hasRel++;
        }
        else if ( ParseNumber( dt, ref parsePos, calendar, hasDate > 0 && hasTime > 0 && hasRel == 0 ) )
        {
          if ( hasDate == 0 || hasTime == 0 || hasRel > 0 )
          {
            hasTime++;
          }
        }
        else if ( ParseTrek( dt, ref parsePos, calendar ) )
        {
          hasDate++;
          hasTime++;
        }
        else
        {
          goto failed;
        }
      }

      if ( hasTime > 1 || hasZone > 1 || hasDate > 1 || hasDay > 1 || hasOrdMonth > 1 )
      {
        goto failed;
      }

      // The following line handles years that are specified using
      // only two digits.  The line of code below implements a policy
      // defined by the X/Open workgroup on the millinium rollover.
      // Note: some of those dates may not actually be valid on some
      // platforms.  The POSIX standard startes that the dates 70-99
      // shall refer to 1970-1999 and 00-38 shall refer to 2000-2038.
      // This later definition should work on all platforms.

      int thisYear = calendar.year;
      if ( thisYear < 100 )
      {
        if ( thisYear >= 69 )
        {
          calendar.year = thisYear + 1900;
        }
        else
        {
          calendar.year = thisYear + 2000;
        }
      }

      if ( hasRel > 0 )
      {
        if ( hasTime == 0 && hasDate == 0 && hasDay == 0 )
        {
          calendar.dateTime = baseDate;
        }
        // Certain JDK implementations are buggy WRT DST.
        // Work around this issue by adding a day instead
        // of a days worth of seconds.
        int seconds_in_day = ( 60 * 60 * 24 );
        int seconds = diff.Seconds;
        bool negative_seconds = ( seconds < 0 );
        int days = 0;
        if ( negative_seconds )
          seconds *= ( -1 );
        while ( seconds >= seconds_in_day )
        {
          seconds -= seconds_in_day;
          days++;
        }
        if ( negative_seconds )
        {
          seconds *= ( -1 );
          days *= ( -1 );
        }
        if ( days != 0 )
        {

          //                                    calendar.add(SupportClass.CalendarManager.DATE, days);
        }
        if ( seconds != 0 )
        {

          //                                    calendar.add(SupportClass.CalendarManager.SECOND, seconds);
        }

        //                              calendar.add(SupportClass.CalendarManager.MONTH, diff.Months);
      }

      if ( hasDay > 0 && hasDate == 0 )
      {
        SetWeekday( calendar, diff );
      }

      if ( hasOrdMonth > 0 )
      {
        SetOrdMonth( calendar, diff );
      }
      try
      {
        return calendar.dateTime;
      }
      catch ( Exception )
      {
        throw new FormatException();
      }
failed:
      throw new FormatException();
    }
    private bool ParseTime( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar )
    // calendar object to set
    {
      int pos = parsePos;

      if ( pos + 6 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ':' ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( ':' ) && dt[pos + 4].UNumber && dt[pos + 5].is_Renamed( '-' ) && dt[pos + 6].UNumber )
      {
        ClockToken zone = GetTimeZoneFromRawOffset( ( -dt[pos + 6].Int ) / 100 );
        if ( zone != null )
        {
          calendar.hour = dt[pos].Int;
          calendar.minute = dt[pos + 2].Int;
          calendar.second = dt[pos + 4].Int;
          // TODO
          //                                    calendar.setTimeZone(zone.Zone);
          parsePos = pos + 7;
          return true;
        }
      }
      if ( pos + 4 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ':' ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( ':' ) && dt[pos + 4].UNumber )
      {
        parsePos = pos + 5;
        ParseMeridianAndSetHour( dt, ref parsePos, calendar, dt[pos].Int );
        calendar.minute = dt[pos + 2].Int;
        calendar.second = dt[pos + 4].Int;
        return true;
      }
      if ( pos + 4 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ':' ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( '-' ) && dt[pos + 4].UNumber )
      {
        ClockToken zone = GetTimeZoneFromRawOffset( ( -dt[pos + 4].Int ) / 100 );
        if ( zone != null )
        {
          calendar.hour = dt[pos].Int;
          calendar.minute = dt[pos + 2].Int;

          //                                    calendar.setTimeZone(zone.Zone);
          parsePos = pos + 5;
          return true;
        }
      }
      if ( pos + 2 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ':' ) && dt[pos + 2].UNumber )
      {
        parsePos = pos + 3;
        ParseMeridianAndSetHour( dt, ref parsePos, calendar, dt[pos].Int );
        calendar.minute = dt[pos + 2].Int;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ClockToken.MERIDIAN ) )
      {
        parsePos = pos + 1;
        ParseMeridianAndSetHour( dt, ref parsePos, calendar, dt[pos].Int );
        return true;
      }
      return false;
    }
    private bool ParseZone( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar )
    // calendar object to set
    {
      int pos = parsePos;

      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.ZONE ) && dt[pos + 1].is_Renamed( ClockToken.DST ) )
      {

        //                              calendar.setTimeZone(dt[pos].Zone);
        parsePos = pos + 2;
        return true;
      }
      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.ZONE ) )
      {

        //                              calendar.setTimeZone(dt[pos].Zone);
        parsePos = pos + 1;
        return true;
      }
      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.DAYZONE ) )
      {

        //                              calendar.setTimeZone(dt[pos].Zone);
        parsePos = pos + 1;
        return true;
      }
      return false;
    }
    private bool ParseDay( ClockToken[] dt, ref System.Int32 parsePos, ClockRelTimespan diff )
    // time difference to evaluate
    {
      int pos = parsePos;

      if ( pos + 2 < dt.Length && dt[pos].is_Renamed( '+' ) && dt[pos + 1].UNumber && dt[pos + 2].is_Renamed( ClockToken.DAY ) )
      {
        diff.setWeekday( dt[pos + 2].Int, dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 2 < dt.Length && dt[pos].is_Renamed( '-' ) && dt[pos + 1].UNumber && dt[pos + 2].is_Renamed( ClockToken.DAY ) )
      {
        diff.setWeekday( dt[pos + 2].Int, -dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.NEXT ) && dt[pos + 1].is_Renamed( ClockToken.DAY ) )
      {
        diff.setWeekday( dt[pos + 1].Int, 2 );
        parsePos = pos + 2;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.DAY ) && dt[pos + 1].is_Renamed( ',' ) )
      {
        diff.setWeekday( dt[pos].Int );
        parsePos = pos + 2;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ClockToken.DAY ) )
      {
        diff.setWeekday( dt[pos + 1].Int, dt[pos].Int );
        parsePos = pos + 2;
        return true;
      }
      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.DAY ) )
      {
        diff.setWeekday( dt[pos].Int );
        parsePos = pos + 1;
        return true;
      }
      return false;
    }
    private bool ParseDate( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar )
    // calendar object to set
    {
      int pos = parsePos;

      if ( pos + 4 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( '/' ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( '/' ) && dt[pos + 4].UNumber )
      {
        calendar.day = dt[pos + 2].Int;
        calendar.month = dt[pos].Int;
        calendar.year = dt[pos + 4].Int;
        parsePos = pos + 5;
        return true;
      }
      if ( pos + 4 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( '-' ) && dt[pos + 2].is_Renamed( ClockToken.MONTH ) && dt[pos + 3].is_Renamed( '-' ) && dt[pos + 4].UNumber )
      {
        calendar.year = dt[pos + 4].Int;
        calendar.month = dt[pos + 2].Int;
        calendar.day = dt[pos].Int;
        parsePos = pos + 5;
        return true;
      }
      if ( pos + 4 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( '-' ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( '-' ) && dt[pos + 4].UNumber )
      {
        calendar.year = dt[pos].Int;
        calendar.month = dt[pos + 2].Int;
        calendar.day = dt[pos + 4].Int;
        parsePos = pos + 5;
        return true;
      }
      if ( pos + 3 < dt.Length && dt[pos].is_Renamed( ClockToken.MONTH ) && dt[pos + 1].UNumber && dt[pos + 2].is_Renamed( ',' ) && dt[pos + 3].UNumber )
      {
        calendar.day = dt[pos + 1].Int;
        calendar.month = dt[pos].Int;
        calendar.year = dt[pos + 3].Int;
        parsePos = pos + 4;
        return true;
      }
      if ( pos + 2 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( '/' ) && dt[pos + 2].UNumber )
      {
        calendar.day = dt[pos + 2].Int;
        calendar.month = dt[pos].Int;
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 2 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ClockToken.MONTH ) && dt[pos + 2].UNumber )
      {
        calendar.day = dt[pos].Int;
        calendar.month = dt[pos + 1].Int;
        calendar.year = dt[pos + 2].Int;
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.MONTH ) && dt[pos + 1].UNumber )
      {
        calendar.day = dt[pos + 1].Int;
        calendar.month = dt[pos].Int;
        parsePos = pos + 2;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].UNumber && dt[pos + 1].is_Renamed( ClockToken.MONTH ) )
      {
        calendar.day = dt[pos].Int;
        calendar.month = dt[pos + 1].Int;
        parsePos = pos + 2;
        return true;
      }
      if ( pos < dt.Length && dt[pos].IsoBase )
      {
        calendar.day = dt[pos].Int % 100;
        calendar.month = ( dt[pos].Int % 10000 ) / 100;
        calendar.year = dt[pos].Int / 10000;
        parsePos = pos + 1;
        return true;
      }
      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.EPOCH ) )
      {
        calendar.day = 1;
        calendar.month = 0;
        calendar.year = EPOCH_YEAR;
        parsePos = pos + 1;
        return true;
      }
      return false;
    }
    private bool ParseNumber( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar, bool mayBeYear )
    // number is considered to be year?
    {
      int pos = parsePos;

      if ( pos < dt.Length && dt[pos].UNumber )
      {
        parsePos = pos + 1;
        if ( mayBeYear )
        {
          calendar.year = dt[pos].Int;
        }
        else
        {
          calendar.hour = dt[pos].Int / 100;
          calendar.minute = dt[pos].Int % 100;
          calendar.second = 0;
        }
        return true;
      }
      return false;
    }
    private bool ParseRelSpec( ClockToken[] dt, ref System.Int32 parsePos, ClockRelTimespan diff )
    // time difference to evaluate
    {
      if ( !ParseRelUnits( dt, ref parsePos, diff ) )
      {
        return false;
      }

      int pos = parsePos;
      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.AGO ) )
      {
        diff.negate();
        parsePos = pos + 1;
      }
      return true;
    }
    private bool ParseRelUnits( ClockToken[] dt, ref System.Int32 parsePos, ClockRelTimespan diff )
    // time difference to evaluate
    {
      int pos = parsePos;

      if ( pos + 2 < dt.Length && dt[pos].is_Renamed( '+' ) && dt[pos + 1].UNumber && dt[pos + 2].Unit )
      {
        diff.addUnit( dt[pos + 2], dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 2 < dt.Length && dt[pos].is_Renamed( '-' ) && dt[pos + 1].UNumber && dt[pos + 2].Unit )
      {
        diff.addUnit( dt[pos + 2], -dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].UNumber && dt[pos + 1].Unit )
      {
        diff.addUnit( dt[pos + 1], dt[pos].Int );
        parsePos = pos + 2;
        return true;
      }
      else if ( pos + 2 < dt.Length && dt[pos].is_Renamed( ClockToken.NEXT ) && dt[pos + 1].UNumber && dt[pos + 2].Unit )
      {
        diff.addUnit( dt[pos + 2], dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.NEXT ) && dt[pos + 1].Unit )
      {
        diff.addUnit( dt[pos + 1] );
        parsePos = pos + 2;
        return true;
      }
      if ( pos < dt.Length && dt[pos].Unit )
      {
        diff.addUnit( dt[pos] );
        parsePos = pos + 1;
        return true;
      }
      return false;
    }
    private bool ParseOrdMonth( ClockToken[] dt, ref System.Int32 parsePos, ClockRelTimespan diff )
    // time difference to evaluate
    {
      int pos = parsePos;

      if ( pos + 2 < dt.Length && dt[pos].is_Renamed( ClockToken.NEXT ) && dt[pos + 1].UNumber && dt[pos + 2].is_Renamed( ClockToken.MONTH ) )
      {
        diff.addOrdMonth( dt[pos + 2].Int, dt[pos + 1].Int );
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].is_Renamed( ClockToken.NEXT ) && dt[pos + 1].is_Renamed( ClockToken.MONTH ) )
      {
        diff.addOrdMonth( dt[pos + 1].Int, 1 );
        parsePos = pos + 2;
        return true;
      }
      return false;
    }
    private bool ParseIso( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar )
    // calendar object to set
    {
      int pos = parsePos;

      if ( pos + 6 < dt.Length && dt[pos].IsoBase && dt[pos + 1].is_Renamed( ClockToken.ZONE ) && dt[pos + 2].UNumber && dt[pos + 3].is_Renamed( ':' ) && dt[pos + 4].UNumber && dt[pos + 5].is_Renamed( ':' ) && dt[pos + 6].UNumber )
      {
        calendar.day = dt[pos].Int % 100;
        calendar.month = ( dt[pos].Int % 10000 ) / 100;
        calendar.year = dt[pos].Int / 10000;
        calendar.hour = dt[pos + 2].Int;
        calendar.minute = dt[pos + 4].Int;
        calendar.second = dt[pos + 6].Int;
        parsePos = pos + 7;
        return true;
      }
      if ( pos + 2 < dt.Length && dt[pos].IsoBase && dt[pos + 1].is_Renamed( ClockToken.ZONE ) && dt[pos + 1].Zone.GetUtcOffset( calendar.dateTime ).Hours == ( -7 ) * MILLIS_PER_HOUR && dt[pos + 2].IsoBase )
      {
        calendar.day = dt[pos].Int % 100;
        calendar.month = ( dt[pos].Int % 10000 ) / 100;
        calendar.year = dt[pos].Int / 10000;
        calendar.hour = dt[pos + 2].Int / 10000;
        calendar.minute = ( dt[pos + 2].Int % 10000 ) / 100;
        calendar.second = dt[pos + 2].Int % 100;
        parsePos = pos + 3;
        return true;
      }
      if ( pos + 1 < dt.Length && dt[pos].IsoBase && dt[pos + 1].IsoBase )
      {
        calendar.day = dt[pos].Int % 100;
        calendar.month = ( dt[pos].Int % 10000 ) / 100;
        calendar.year = dt[pos].Int / 10000;
        calendar.hour = dt[pos + 1].Int / 10000;
        calendar.minute = ( dt[pos + 1].Int % 10000 ) / 100;
        calendar.second = dt[pos + 1].Int % 100;
        parsePos = pos + 2;
        return true;
      }
      return false;
    }
    private bool ParseTrek( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar )
    // calendar object to set
    {
      int pos = parsePos;

      if ( pos + 3 < dt.Length && dt[pos].is_Renamed( ClockToken.STARDATE ) && dt[pos + 1].UNumber && dt[pos + 2].is_Renamed( '.' ) && dt[pos + 3].UNumber )
      {
        GregorianCalendar gcal = new GregorianCalendar();
        int trekYear = dt[pos + 1].Int / 1000 + 2323 - 377;
        int trekDay = 1 + ( ( dt[pos + 1].Int % 1000 ) * ( gcal.IsLeapYear( trekYear ) ? 366 : 365 ) ) / 1000;
        int trekSeconds = dt[pos + 3].Int * 144 * 60;
        calendar.year = trekYear;
        calendar.dateTime = gcal.AddDays( calendar.dateTime, trekDay );
        calendar.second = trekSeconds;
        parsePos = pos + 4;
        return true;
      }
      return false;
    }
    private void ParseMeridianAndSetHour( ClockToken[] dt, ref System.Int32 parsePos, TclDateTime calendar, int hour )
    // hour value (1-12 or 0-23) to set.
    {
      int pos = parsePos;
      int hourField;

      if ( pos < dt.Length && dt[pos].is_Renamed( ClockToken.MERIDIAN ) )
      {
        // SupportClass.CalendarManager.manager.Set(calendar, SupportClass.CalendarManager.AM_PM, dt[pos].Int);
        parsePos = pos + 1;
        hourField = SupportClass.CalendarManager.HOUR;
      }
      else
      {
        hourField = SupportClass.CalendarManager.HOUR_OF_DAY;
      }

      if ( hourField == SupportClass.CalendarManager.HOUR && hour == 12 )
      {
        hour = 0;
      }
      calendar.hour = hour;
    }
    private ClockToken[] GetTokens( string in_Renamed, bool debug )
    // Send the generated token list to stderr?
    {
      System.Int32 parsePos = 0;
      ClockToken dt;
      ArrayList tokenVector = new ArrayList( in_Renamed.Length );

      while ( ( dt = GetNextToken( in_Renamed, ref parsePos ) ) != null )
      {
        tokenVector.Add( dt );
      }

      ClockToken[] tokenArray = new ClockToken[tokenVector.Count];
      tokenVector.CopyTo( tokenArray );

#if DEBUG
      for ( int ix = 0; ix < tokenArray.Length; ix++ )
      {
        if ( ix != 0 )
        {
          System.Console.Error.Write( "," );
        }

        System.Console.Error.Write( tokenArray[ix].ToString() );
      }
      System.Console.Error.WriteLine( "" );
#endif

      return tokenArray;
    }
    private ClockToken GetNextToken( string in_Renamed, ref System.Int32 parsePos )
    // Current position in input
    {
      int pos = parsePos;
      int sign;

      while ( true )
      {
        while ( pos < in_Renamed.Length && ( System.Char.GetUnicodeCategory( in_Renamed[pos] ) == System.Globalization.UnicodeCategory.SpaceSeparator ) )
        {
          pos++;
        }
        if ( pos >= in_Renamed.Length )
        {
          break;
        }

        char c = in_Renamed[pos];
        if ( System.Char.IsDigit( c ) )
        {
          int number = 0;
          int count = 0;
          while ( pos < in_Renamed.Length && System.Char.IsDigit( c = in_Renamed[pos] ) )
          {
            number = 10 * number + c - '0';
            pos++;
            count++;
          }
          parsePos = pos;
          return new ClockToken( number, count >= 6 );
        }
        if ( System.Char.IsLetter( c ) )
        {
          int beginPos = pos;
          while ( ++pos < in_Renamed.Length )
          {
            c = in_Renamed[pos];
            if ( !System.Char.IsLetter( c ) && c != '.' )
            {
              break;
            }
          }
          parsePos = pos;
          return LookupWord( in_Renamed.Substring( beginPos, ( pos ) - ( beginPos ) ) );
        }
        parsePos = pos + 1;
        return new ClockToken( in_Renamed[pos] );
      }
      parsePos = pos + 1;
      return null;
    }
    private ClockToken LookupWord( string word )
    // word to lookup
    {
      int ix;
      string[] names;
      string[][] zones;

      if ( word.ToUpper().Equals( "am".ToUpper() ) || word.ToUpper().Equals( "a.m.".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MERIDIAN, SupportClass.CalendarManager.AM );
      }
      if ( word.ToUpper().Equals( "pm".ToUpper() ) || word.ToUpper().Equals( "p.m.".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MERIDIAN, SupportClass.CalendarManager.PM );
      }

      // See if we have an abbreviation for a day or month.

      bool abbrev;
      if ( word.Length == 3 )
      {
        abbrev = true;
      }
      else if ( word.Length == 4 && word[3] == '.' )
      {
        abbrev = true;
        word = word.Substring( 0, ( 3 ) - ( 0 ) );
      }
      else
      {
        abbrev = false;
      }


      DateTimeFormatInfo symbols = new CultureInfo( "en-US" ).DateTimeFormat;
      if ( abbrev )
      {
        names = symbols.AbbreviatedMonthNames;
      }
      else
      {
        names = (string[])symbols.MonthNames;
      }
      for ( ix = 0; ix < names.Length; ix++ )
      {
        if ( word.ToUpper().Equals( names[ix].ToUpper() ) )
        {
          return new ClockToken( ClockToken.MONTH, ix + 1 );
        }
      }
      if ( abbrev )
      {
        names = symbols.AbbreviatedDayNames;
      }
      else
      {
        names = symbols.DayNames;
      }
      for ( ix = 0; ix < names.Length; ix++ )
      {
        if ( word.ToUpper().Equals( names[ix].ToUpper() ) )
        {
          return new ClockToken( ClockToken.DAY, ix );
        }
      }

      // Drop out any periods and try the timezone table.

      StringBuilder withoutDotsBuf = new StringBuilder( word.Length );
      for ( ix = 0; ix < word.Length; ix++ )
      {
        if ( word[ix] != '.' )
        {
          withoutDotsBuf.Append( word[ix] );
        }
      }

      string withoutDots = new string( withoutDotsBuf.ToString().ToCharArray() );

      //                        zones = symbols.getZoneStrings();

      //                        for (ix = 0; ix < zones.Length; ix++)
      //                        {
      //                                if (withoutDots.ToUpper().Equals(zones[ix][2].ToUpper()) || withoutDots.ToUpper().Equals(zones[ix][4].ToUpper()))
      //                                {
      //                                        
      //                                        System.TimeZone zone = TimeZone.getTimeZone(zones[ix][0]);
      //                                        return new ClockToken(ClockToken.ZONE, zone);
      //                                }
      //                        }
      if ( withoutDots.ToUpper().Equals( "dst".ToUpper() ) )
      {
        return new ClockToken( ClockToken.DST, null );
      }

      // Strip off any plural and try the units.

      string singular;
      if ( word.EndsWith( "s" ) )
      {
        singular = word.Substring( 0, ( word.Length - 1 ) - ( 0 ) );
      }
      else
      {
        singular = word;
      }
      if ( singular.ToUpper().Equals( "year".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MONTH_UNIT, 12 );
      }
      else if ( singular.ToUpper().Equals( "month".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MONTH_UNIT, 1 );
      }
      else if ( singular.ToUpper().Equals( "fortnight".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 14 * 24 * 60 );
      }
      else if ( singular.ToUpper().Equals( "week".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 7 * 24 * 60 );
      }
      else if ( singular.ToUpper().Equals( "day".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 24 * 60 );
      }
      else if ( singular.ToUpper().Equals( "hour".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 60 );
      }
      else if ( singular.ToUpper().Equals( "minute".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 1 );
      }
      else if ( singular.ToUpper().Equals( "min".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 1 );
      }
      else if ( singular.ToUpper().Equals( "second".ToUpper() ) )
      {
        return new ClockToken( ClockToken.SEC_UNIT, 1 );
      }
      else if ( singular.ToUpper().Equals( "sec".ToUpper() ) )
      {
        return new ClockToken( ClockToken.SEC_UNIT, 1 );
      }

      if ( singular.ToUpper().Equals( "tomorrow".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 1 * 24 * 60 );
      }
      else if ( singular.ToUpper().Equals( "yesterday".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, ( -1 ) * 24 * 60 );
      }
      else if ( singular.ToUpper().Equals( "today".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 0 );
      }
      else if ( singular.ToUpper().Equals( "now".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 0 );
      }
      else if ( singular.ToUpper().Equals( "last".ToUpper() ) )
      {
        return new ClockToken( -1, false );
      }
      else if ( singular.ToUpper().Equals( "this".ToUpper() ) )
      {
        return new ClockToken( ClockToken.MINUTE_UNIT, 0 );
      }
      else if ( singular.ToUpper().Equals( "next".ToUpper() ) )
      {
        return new ClockToken( ClockToken.NEXT, 1 );
      }
      else if ( singular.ToUpper().Equals( "ago".ToUpper() ) )
      {
        return new ClockToken( ClockToken.AGO, 1 );
      }
      else if ( singular.ToUpper().Equals( "epoch".ToUpper() ) )
      {
        return new ClockToken( ClockToken.EPOCH, 0 );
      }
      else if ( singular.ToUpper().Equals( "stardate".ToUpper() ) )
      {
        return new ClockToken( ClockToken.STARDATE, 0 );
      }

      // Since a military timezone (T) is used in the clock test of 8.3,
      // we can't ignore these timezones any longer...

      if ( withoutDots.Length == 1 )
      {
        int rawOffset = 0;
        bool found = true;
        char milTz = System.Char.ToLower( withoutDots[0] );

        if ( milTz >= 'a' && milTz <= 'm' )
        {
          rawOffset = milTz - 'a' + 1;
        }
        else if ( milTz >= 'n' && milTz < 'z' )
        {
          rawOffset = 'n' - milTz - 1;
        }
        else if ( milTz != 'z' )
        {
          found = false;
        }
        if ( found )
        {
          ClockToken zone = GetTimeZoneFromRawOffset( rawOffset );
          if ( zone != null )
          {
            return zone;
          }
        }
      }

      return new ClockToken( word );
    }
    private ClockToken GetTimeZoneFromRawOffset( int rawOffset )
    {

      //                        string[] tzNames = TimeZone.getAvailableIDs(rawOffset * MILLIS_PER_HOUR);

      //                        if (tzNames.Length > 0)
      //                        {
      //                                
      //                                System.TimeZone zone = TimeZone.getTimeZone(tzNames[0]);
      //                                return new ClockToken(ClockToken.ZONE, zone);
      //                        }
      return null;
    }
  } // end ClockCmd
  class ClockToken
  {
    public bool UNumber
    {
      get
      {
        return kind == UNUMBER;
      }

    }
    public bool IsoBase
    {
      get
      {
        return kind == ISOBASE;
      }

    }
    public bool Unit
    {
      get
      {
        return kind == MINUTE_UNIT || kind == MONTH_UNIT || kind == SEC_UNIT;
      }

    }
    internal int Int
    {
      get
      {
        return number;
      }

    }
    internal System.TimeZone Zone
    {
      get
      {
        return zone;
      }

    }
    internal const int ISOBASE = 1;
    internal const int UNUMBER = 2;
    internal const int WORD = 3;
    internal const int CHAR = 4;
    internal const int MONTH = 5;
    internal const int DAY = 6;
    internal const int MONTH_UNIT = 7;
    internal const int MINUTE_UNIT = 8;
    internal const int SEC_UNIT = 9;
    internal const int AGO = 10;
    internal const int EPOCH = 11;
    internal const int ZONE = 12;
    internal const int DAYZONE = 13;
    internal const int DST = 14;
    internal const int MERIDIAN = 15;
    internal const int NEXT = 16;
    internal const int STARDATE = 17;

    internal ClockToken( int number, bool isIsoBase )
    {
      this.kind = isIsoBase ? ISOBASE : UNUMBER;
      this.number = number;
    }
    internal ClockToken( int kind, int number )
    {
      this.kind = kind;
      this.number = number;
    }
    internal ClockToken( int kind, System.TimeZone zone )
    {
      this.kind = kind;
      this.zone = zone;
    }
    internal ClockToken( string word )
    {
      this.kind = WORD;
      this.word = word;
    }
    internal ClockToken( char c )
    {
      this.kind = CHAR;
      this.c = c;
    }
    public bool is_Renamed( char c )
    {
      return this.kind == CHAR && this.c == c;
    }
    public bool is_Renamed( int kind )
    {
      return this.kind == kind;
    }

    public override string ToString()
    {
      if ( UNumber )
      {
        return "U" + System.Convert.ToString( Int );
      }
      else if ( IsoBase )
      {
        return "I" + System.Convert.ToString( Int );
      }
      else if ( kind == WORD )
      {
        return word;
      }
      else if ( kind == CHAR )
      {
        return c.ToString();
      }
      else if ( kind == ZONE || kind == DAYZONE )
      {
        return zone.StandardName;
      }
      else
      {
        return "(" + kind + "," + Int + ")";
      }
    }

    private int kind;
    private int number;
    private string word;
    private char c;
    private System.TimeZone zone;
  } // end ClockToken
  class ClockRelTimespan
  {
    internal int Seconds
    {
      get
      {
        return seconds;
      }

    }
    internal int Months
    {
      get
      {
        return months;
      }

    }
    internal int OrdMonth
    {
      get
      {
        return ordMonth;
      }

    }
    internal int DayOrdinal
    {
      get
      {
        return dayOrdinal;
      }

    }
    internal ClockRelTimespan()
    {
      seconds = 0;
      months = 0;
      ordMonth = 0;
      weekday = 0;
      dayOrdinal = 0;
    }
    internal void addSeconds( int s )
    {
      seconds += s;
    }
    internal void addMonths( int m )
    {
      months += m;
    }
    internal void addOrdMonth( int m, int c )
    {
      months = m;
      ordMonth += c;
    }
    internal void addUnit( ClockToken unit, int amount )
    {
      if ( unit.is_Renamed( ClockToken.SEC_UNIT ) )
      {
        addSeconds( unit.Int * amount );
      }
      else if ( unit.is_Renamed( ClockToken.MINUTE_UNIT ) )
      {
        addSeconds( unit.Int * 60 * amount );
      }
      else if ( unit.is_Renamed( ClockToken.MONTH_UNIT ) )
      {
        addMonths( unit.Int * amount );
      }
    }
    internal void addUnit( ClockToken unit )
    {
      addUnit( unit, 1 );
    }
    internal void setWeekday( int w, int ord )
    {
      weekday = w;
      dayOrdinal = ord;
    }
    internal void setWeekday( int w )
    {
      setWeekday( w, 1 );
    }
    internal void negate()
    {
      seconds = -seconds;
      months = -months;
    }
    internal int getWeekday()
    {
      return weekday;
    }
    private int seconds;
    private int months;
    private int ordMonth;
    private int weekday;
    private int dayOrdinal;
  }
  class TclDateTime
  {
    public int year, month, day, hour, minute, second, millisecond;
    public DateTime dateTime
    {
      get
      {
        return new DateTime( year, month, day, hour, minute, second, millisecond );
      }
      set
      {
        DateTime dt = value;
        year = dt.Year;
        month = dt.Month;
        day = dt.Day;
        hour = dt.Hour;
        minute = dt.Minute;
        second = dt.Second;
        millisecond = dt.Millisecond;
      }
    }
  }
}