#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 { /// This class implements the built-in "clock" command in Tcl. 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; } } } }