/ / == ==
//
//
// Copyright (C) 2002 Microsoft Corporation. All Rights Reserved.
//
// the use and distribution terms for this Software Are Contained in The File
// Named license.txt, Which can be found in the root of this distribution.
// by using this software in any fashion, you are agreeing to be bound by the
// Terms of this license.
//
// You Must Not Remove this Notice, or Any Other, from this software.
//
//
/ / == - ==
//
// Class: DateTimeParse
//
//
// purpose: this class is called by datetime to parse a date / time string.
//
// Date: July 8, 1999
//
Namespace system {
Using system;
Using system.text;
USING SYSTEM.GLOBALIZATION;
Using system.threading;
Using system.collections;
/ *
Notenote:
There Are Some Nasty Cases in The Parsing of Date / Time:
0x438 fo (Country: Faeroe Islands, LANGUGE: FAEROESE)
Locale_stime = [.]
Locale_stimeFor = [HH.MM.SS]
Locale_sdate = [-]
Locale_sshortdate = [DD-MM-YYYY]
Locale_slongdate = [d. Mmmm yyyy]
The Time Separator is ".", However, IT Also Has A "." In the long date format.
0x437: (Country: Georgia, LANGUGE: Georgian)
Short date: dd.mm.yy
Long Date: YYYY ???? DD MM, DDDD
.
0x0404: (Country: taiwan, languge: Chinese)
Locale_stimeformat = [TT HH: mm: SS]
When General Date Is Used, The Pattern Is "YYYY / M / D TTH: MM: SS". Note That The "TT" is after "YYYY / M / D".
And this is different from motult.
0x0437: (Country: Georgia, LANGUGE: Georgian)
Short date: dd.mm.yy
Long Date: YYYY ???? DD MM, DDDD
0x0456:
* /
/ *
* /
// this class contains Only Static Members and does Not Require The Serializable Attribute.
Internal
Class datetimeparse {
Private static string alternativedomDateseparator = "-";
Private static datetimeformatinfo invariantinfo = datetimeformatinfo.invariantinfo;
//
// this is used to cache the limited amount Month Names of the invariant culture.
//
Private static string [] invariantMonthnames = null;
Private static string [] invariantabbrevmonthnames = null;
//
// this is used to cache the thinger-canceled day names of the invariant culture.
//
Private static string [] invariantdayNames = null;
Private static string [] invariantabbrevdaynames = null;
Private static string invariantamdesignator = invariantIntinfo.amdesignator;
Private static string invariantpmdesignator = invariantinfo.pmdesignator;
Private static datetimeformatinfo m_jajpdtfi = NULL;
Private static datetimeformatinfo m_zhtwdtfi = NULL;
Internal Static DateTime Parsexact (String S, String Format, DateTimeFormatinfo DTFI, DateTimeStyles Style) {
IF (s == null || Format == null) {
Throw new argumentnullexception ((s == null? "s": "format"),
Environment.getResourceString ("argumentnull_string"))));
}
IF (s.length == 0) {
Throw new formatexception (Environment.GetResourceString ("format_baddatetime")));
}
IF (Format.length == 0) {
Throw new formatexception (Environment.GetResourceString ("Format_badFormatspecifier));
}
IF (DTFI == Null)
{
DTFI = DateTimeFormatinfo.currentInfo;
}
DateTime Result;
IF (DostrictParse (S, Format, Style, DTFI, TRUE, OUT RESULT) {
Return (Result);
}
//
// this is Just Used to Keep Compiler Happy.// This Is Because Dostrike () Alwyas Does Either:
// 1. Return True OR
// 2. Throw Exceptions if there is error in paarsing.
// So We will never get here.
//
Return (New DateTime ());
}
Internal static bool parseexactmultiple (string s, string [] Formats,
DateTimeformatinfo DTFI, DateTimeStyles Style, Out DateTime Result) {
IF (s == null || Formats == null) {
Throw new argumentnullexception ((s == NULL? "S": "formats"),
Environment.getResourceString ("argumentnull_string"))));
}
IF (s.length == 0) {
Throw new formatexception (Environment.GetResourceString ("format_baddatetime")));
}
IF (Formats.Length == 0) {
Throw new formatexception (Environment.GetResourceString ("Format_badFormatspecifier));
}
IF (DTFI == NULL) {
DTFI = DateTimeFormatinfo.currentInfo;
}
//
// do a loop through the provided formats and see if we can parse succesfully in
// one of the formats.
//
For (int i = 0; i IF (Formats [i] == NULL || Formats [i] .length == 0) { Throw new formatexception (Environment.GetResourceString ("Format_badFormatspecifier)); } IF (DostrictParse (S, Formats [I], Style, DTFI, False, Out Result) { Return (TRUE); } } Result = new datetime (0); Return (False); } // // Separator Types // Private const INT SEP_UNK = 0; // Unknown Separetor. Private const INT SEP_END = 1; // The end of the parsing string. Private const INT SEP_SPACE = 2; // Whitespace (Including CommA). Private const INT SEP_AM = 3; // AM TIMEMARK. Private const INT SEP_PM = 4; // PM Timemark.Private const INT SEP_DATE = 5; // Date Separe. Private const INT SEP_TIME = 6; // Time Separator. Private const INT SEP_YEARSUFF = 7; // Chinese / japanese / Korean year suffix. Private const INT SEP_MONTHSUFF = 8; // Chinese / Japanese / Korean Month Suffix. Private const INT SEP_DAYSUFF = 9; // Chinese / Japanese / Korean Day SUFFIX. Private const INT SEP_HOURSUFF = 10; // Chinese / japanese / Korean Hour Suffix. Private const INT SEP_MINUTESUFF = 11; // Chinese / japanese / Korean Minute SUFFIX. Private const INT SEP_SECONDSUFF = 12; // chinese / japanese / Korean second suffix. Private const INT SEP_LOCALTIMEMARK = 13; // 't' Private const INT SEP_MAX = 14; // Date Token Types (DTT_ *) // // Following is the set of tokens what can be generated from a date // String. NOTICE That The Legal Set of Trailing Separators Have Been // folded in with the date number, and month name tokens. this set // of tokens is chosen to reduce the number of date parse states. // Private const INT DTT_END = 0; // '/ 0' Private const Int DTT_NUMEND = 1; // Num [] * [/ 0] Private const INT DTT_NUMAMPM = 2; // Num [] ampm Private const Int DTT_NUMSPACE = 3; // Num [] ^ [DSEP | TSEP | '0 /'] Private const INT DTT_NUMDATESEP = 4; // Num [] * DSEP Private const INT DTT_NUMTIMESEP = 5; // Num [] * TSEP Private const Int DTT_MONTHEND = 6; // Month [] * '/ 0' Private const Int DTT_MONTHSPACE = 7; // Month [] ^ [DSEP | TSEP | '/ 0'] Private const INT DTT_MONTHDATESEP = 8; // Month [] * DSEP Private const Int dtt_numdateSUFF = 9; // Month [] * DSUFFPRIVATE const INT DTT_NUMTIMESUFF = 10; // Month [] * TSUFF Private const Int dtt_dayofweek = 11; // days of week name Private const Int DTT_YEARSPACE = 12; // Year ^ [DSEP | TSEP | '0 /'] Private const Int dtt_yeardatesep = 13; // year DSEP Private const Int DTT_Yerend = 14; // Year ['/ 0'] Private const Int dtt_timezone = 15; // Timezone Name PRIVATE const INT DTT_ERA = 16; // Era Name Private constimail = 17; // Num 'Z' // when add a new token which will be in the // State Table, Add IT After DTT_NUMLOCALTIMEMARK. Private const Int dtt_unk = 18; // unknown Private const Int DTT_NUMLOCALTIMEMARK = 19; // Num 'T' Private const Int DTT_MAX = 20; // Marker Private const INTTM_AM = 0; Private const INTTM_PM = 1; // // Year / Month / Day Suffixes // Private const string cjkyearsuff = "/ u5e74"; Private const string cjkmonthsuff = "/ u6708"; Private const string cjkdaysuff = "/ u65e5"; Private const string koreanyearsuff = "/ ub144"; Private const string Koreanmonthsuff = "/ uc6d4"; Private const string KoreandAysuff = "/ uc77c"; Private const string cjkhaiff = "/ u6642"; Private const string chinesehoursuff = "/ u65f6"; Private const string cjkminutesuff = "/ u5206"; Private const string cjksecondsuff = "/ u79d2"; Private const string Localtimemark = "t"; // // DateTime Parsing State Enumeration (DS_ *) // Private const Int ds_begin = 0; Private const Int ds_n = 1; // Have ONE NUMBER Private const Int ds_nn = 2; // Have Two Numbers // The folload area know to be part of a date Private const Int DS_D_ND = 3; // Date String: Have Number Followed by Date Separetor Private const INT DS_D_NN = 4; // Date String: Have Two Numbers Private const Int DS_D_NND = 5; // Date String: Have Two Numbers Followed by Date Separator Private const Int DS_D_M = 6; // Date String: Have A Month PRIVATE CONST INT DS_D_MN = 7; // Date String: Have A Month and A Number Private const INT DS_D_NM = 8; // Date String: Have a Number and A Month Private const Int DS_D_MND = 9; // Date String: Have a month and number backed by date sepator Private const INT DS_D_NDS = 10; // Date String: Have ONE NUMBER FOLLOWED A DATE SUFFIX. Private const INT DS_D_Y = 11; // Date String: Have a year. Private const Int DS_D_YN = 12; // Date String: Have a year and a number Private const Int DS_D_YM = 13; // Date String: Have a year and a month Private const Int DS_D_S = 14; // Have Numbers Followed by a date suffix. Private const INT DS_T_S = 15; // Have Numbers Followed by a time suffix. // the folowing area known to be part of a time Private const Int DS_T_NT = 16; // Have Num Followed by Time Separator Private const Int DS_T_NNT = 17; // Have Two Numbers Followed by Time Separator PRIVATE CONST INT DS_ERROR = 18; // The folload is Terminal State. Thase All Have an Action // Associated with them; and transition back to ds_begin. Private const Int DS_DX_NN = 19; // days from two Numbers Private const Int DS_DX_NNN = 20; // days from three Numbers Private const Int DS_DX_MN = 21; // days from month and one number Private const INT DS_DX_NM = 22; // days from month and one number Private const Int DS_DX_MNN = 23; // days from month and two Numbers Private const INT DS_DX_DS = 24; // a set of date suffixed numbers. PRIVATE CONST INT DS_DX_DSN = 25; // Day from date suffixes and one number. Private const INT DS_DX_NDS = 26; // day from one number and date suffixes. Private const Int ds_dx_nnds = 27; // days from one number and date suffixes. Private const Int DS_DX_YNN = 28; // Date String: Have a year and two Number Private const Int DS_DX_YMN = 29; // Date String: Have a year, a month, and a number. Private const Int DS_DX_YN = 30; // Date String: Have a year and one number Private const Int ds_dx_ym = 31; // Date String: Have a year, a month. Private const Int DS_TX_N = 32; // Time from One Number (Must Have Ampm) Private const Int DS_TX_NN = 33; // Time from Two Numbers Private const Int DS_TX_NNN = 34; // Time from Three Numbers Private const INT DS_TX_TS = 35; // a set of time suffixed number. PRIVATE CONST INT DS_DX_NNY = 36; Private const Int ds_max = 37; // marker: end of enum // // Note: The Following State Machine Table Is Dependent on The Order of The Folk // ds_ and dtt_ enumerations.// // for Each Non Terminal State, The Following Table Defines The Next State // for Each Given Date Token Type. // // DTT_END DTT_NUMEND DTT_NUMAMPM DTT_NUMSPACE // DTT_NUMDAYSEP // DTT_NUMTIMESEP // DTT_MONTHEND DTT_MONTHSPACE // DTT_MONTHDSEP // dtt_numdatesisuff // DTT_NUMTIMESUFF DTT_DAYOFWEEK // DTT_YearSpace // DTT_YEARDATESEP // dtt_yerend, // DTT_TIMEZONE // DTT_ERA DTT_UTCTIMEMARKPRIVATETIC INT [] [] DateParsingStates = { // ds_begin // DS_BEGIN NEW INT [] {DS_BEGIN, DS_ERROR, DS_TX_N, DS_N, DS_D_ND, DS_T_NT, DS_ERROR, DS_D_M, DS_D_M, DS_D_S, DS_T_S, DS_BEGIN, DS_D_Y, DS_D_Y, DS_ERROR, DS_BEGIN, DS_BEGIN, DS_ERROR}, // ds_n // ds_n NEW INT [] {DS_ERROR, DS_DX_NN, DS_ERROR, DS_NN, DS_D_NND, DS_ERROR, DS_DX_NM, DS_D_NM, DS_D_MND, DS_D_NDS, DS_ERROR, DS_N, DS_D_YN, DS_D_YN, DS_DX_YN, DS_N, DS_N, DS_ERROR}, // ds_nn // ds_nn new int [] {DS_DX_NN, DS_DX_NNN, DS_TX_N, DS_DX_NNN, DS_ERROR, DS_T_Nt, DS_DX_MNN, DS_DX_MNN, DS_ERROR, DS_ERROR, DS_T_S, DS_NN, DS_DX_NNY, DS_ERROR, DS_DX_NNY, DS_NN, DS_NN, DS_ERROR}, // DS_D_ND / / DS_D_ND // new int [] {DS_ERROR, DS_DX_NN, DS_ERROR, DS_D_NN, DS_D_NNd, DS_ERROR, DS_DX_MN, DS_D_MN, DS_D_MNd, DS_ERROR, DS_ERROR, DS_D_Nd, DS_D_YN, DS_D_YN, DS_DX_YN, DS_ERROR, DS_D_Nd, DS_ERROR}, new int [] {DS_ERROR, DS_DX_NN, DS_ERROR, DS_D_NN, DS_D_NNd, DS_ERROR, DS_DX_NM, DS_D_MN, DS_D_MNd, DS_ERROR, DS_ERROR, DS_D_Nd, DS_D_YN, DS_D_YN, DS_DX_YN, DS_ERROR, DS_D_Nd, DS_ERROR}, // DS_D_NN // DS_D_NN new int [] {DS_DX_NN, DS_DX_NNN, DS_TX_N, DS_DX_NNN, DS_ERROR, DS_T_Nt, DS_DX_MNN, DS_DX_MNN, DS_ERROR, DS_DX_DS, DS_T_S, DS_D_NN, DS_DX_NNY, DS_ERROR, DS_DX_NNY, DS_ERROR, DS_D_NN, DS_ERROR}, // DS_D_NND / / DS_D_NND new int [] {DS_ERROR, DS_DX_NNN, DS_DX_NNN, DS_DX_NNN, DS_DX_NNN, DS_ERROR, DS_DX_MNN, DS_DX_MNN, DS_ERROR, DS_DX_DS, DS_ERROR, DS_D_NNd, DS_DX_NNY, DS_ERROR, DS_DX_NNY, DS_ERROR, DS_D_NNd, DS_ERROR}, // DS_D_M / / DS_D_M NEW INT [] {DS_ERROR, DS_DX_MN, DS_ERROR, DS_D_MN, DS_D_MND, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_M, DS_D_YM, DS_D_YM, DS_DX_YM, DS_ERROR, DS_D_M, DS_ERROR}, // DS_D_MN / / DS_D_MN new int [] {DS_DX_MN, DS_DX_MNN, DS_DX_MNN, DS_DX_MNN, DS_DX_MNN, DS_T_Nt, DS_ERROR, DS_ERROR, DS_ERROR, DS_DX_DS, DS_T_S, DS_D_MN, DS_DX_YMN, DS_DX_YMN, DS_DX_YMN, DS_ERROR, DS_D_MN, DS_ERROR}, // DS_D_NM / / DS_D_NM new int [] {DS_DX_NM, DS_DX_MNN, DS_DX_MNN, DS_DX_MNN, DS_ERROR, DS_T_Nt, DS_ERROR, DS_ERROR, DS_ERROR, DS_DX_DS, DS_T_S, DS_D_NM, DS_DX_YMN, DS_ERROR, DS_DX_YMN, DS_ERROR, DS_D_NM, DS_ERROR}, // DS_D_MNd // DS_D_MNd // new int [] {DS_ERROR, DS_DX_MNN, DS_ERROR, DS_DX_MNN, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_MNd, DS_DX_YMN, DS_ERROR, DS_DX_YMN, DS_ERROR, DS_D_MNd, DS_ERROR}, new int [] {DS_DX_MN, DS_DX_MNN, DS_ERROR, DS_DX_MNN, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_MNd, DS_DX_YMN, DS_ERROR, DS_DX_YMN, DS_ERROR, DS_D_MNd, DS_ERROR}, // ds_d_nds, // ds_d_nds, new int [] {DS_DX_NDS, DS_DX_NNDS, DS_DX_NNDS, DS_DX_NNDS, DS_ERROR, DS_T_Nt, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_NDS, DS_T_S, DS_D_NDS, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_NDS, DS_ERROR}, // DS_D_Y / / DS_D_Y NEW INT [] {DS_ERROR, DS_ERROR, DS_ERROR, DS_D_YN, DS_D_YN, DS_ERROR, DS_DX_YM, DS_D_YM, DS_D_YM, DS_D_YM, DS_ERROR, DS_D_Y, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_Y, DS_ERROR}, // DS_D_YN / / DS_D_YN new int [] {DS_ERROR, DS_DX_YNN, DS_DX_YNN, DS_DX_YNN, DS_DX_YNN, DS_ERROR, DS_DX_YMN, DS_DX_YMN, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_YN, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_YN, DS_ERROR}, // DS_D_YM // DS_D_YM new int [] {DS_DX_YM, DS_DX_YMN, DS_DX_YMN, DS_DX_YMN, DS_DX_YMN, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_YM, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_YM, DS_ERROR}, // DS_D_S // DS_D_S NEW INT [] {DS_DX_DS, DS_DX_DSN, DS_TX_N, DS_T_NT, DS_ERROR, DS_T_NT, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_S, DS_T_S, DS_D_S, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_S, DS_ERROR}, // DS_T_S // DS_T_S NEW INT [] {DS_TX_TS, DS_TX_TS, DS_TX_TS, DS_T_NT, DS_D_ND, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_D_S, DS_T_S, DS_T_S, DS_ERROR, DS_ERROR, DS_ERROR, DS_T_S, DS_T_S, DS_T_S, DS_T_S, DS_ERROR}, // DS_T_NT / / DS_T_NT // new int [] {DS_ERROR, DS_TX_NN, DS_TX_NN, DS_TX_NN, DS_ERROR, DS_T_NNt, DS_ERROR, DS_D_NM, DS_ERROR, DS_ERROR, DS_T_S, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_T_Nt, DS_T_Nt, DS_TX_NN}, new int [] {DS_ERROR, DS_TX_NN, DS_TX_NN, DS_TX_NN, DS_ERROR, DS_T_NNt, DS_DX_NM, DS_D_NM, DS_ERROR, DS_ERROR, DS_T_S, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_T_Nt, DS_T_Nt, DS_TX_NN}, // DS_T_NNt // DS_T_NNt new int [] {DS_ERROR, DS_TX_NNN, DS_TX_NNN, DS_TX_NNN, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_ERROR, DS_T_S, DS_T_NNt, DS_ERROR, DS_ERROR, DS_ERROR, DS_T_NNt, DS_T_NNt, DS_TX_NNN}, } // end numend Numampm Numspace Numdaysep NumTimesep Monthend Monthspace Monthdsep NumdateSuff NumtimeSuff Dayofweek Yearspace YeardateseP Yearend Private const String gmtname = "gmt"; Private const string zuluname = "z"; // // Search from the index of str at str .index to see letter the target string exissrs in the str. // // AllowPartialMatch: if true, the method will not check if the matching string // is in a word boundary, and will skip to the word boundary. // for example, it will return true for match "january" in "januaryfoo". // if false, The matching word must be in a word boundary. So it will return // false for matching "january" in "januaryfoo". // Private Static Bool Matchword (__ DTString Str, String Target, Bool AllowPartialmatch) { INT length = target.length; IF (str.value.length - str.index) { Return False; } IF (CultureInfo.currentculture.CompareInfo.compare (Str.Value, Str.index, Length, Target, 0, Length, CompareOptions.Ignorecase! = 0) { Return (False); } INT nextcharindex = str.index target.length; if (allowpartialmatch) { // // Skip the remaining part of the word. // // this is to ignore special carated: // a Few Cultures Has Suffix After MMMM, Like "FI" and "EU". While (nextcharindex { IF (! char.ietter (str.value [nextcharindex)) { Break; } Nextcharindex ; } } else { NextCharindex Char nextchch = str.value [nextcharindex]; IF (char.isletter (nextch) || nextch == '/ x00a1') { // the '/ x00a1' is a very unfortunate Hack, Since // GL-ES (0x0456) Used '/ x00a1' in their day of week names. // and '/ x00a1' is a puctuation. This Breaks OUR // Assumption That there is no non-letter characters // Following a day / month name. Return (False); } } } Str.index = nextcharindex; Return (TRUE); } // // Starting At Str.index, Check The Type of The Separetor. // Private Static Int GetSeparator (__ DTString Str, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { INT SEPARATOR = Sep_Space; // Assume The Separetor Is A Space. And try to find a better one. // // Check if We found any White spaces. // IF (! Str.skipwhitespaceComma ()) { // // SkipwhitespaceComma () Will Return True when End of string is reached. // // // Return The Separetor AS Sep_end. // Return (SEP_END); } IF (char.isletter (str.getchar ())) { // // this is a beginning of a word. // IF (raw.timemark == -1) { // // Check if this is an am Time Mark. // Int Timemark; IF ((Timemark = GetTimemark (STR, DTFI))! = -1) { Raw.timemark = TIMEMARK ;; RETURN (TIMEMARK == TM_AM? SEP_AM: SEP_PM); } } IF (Matchword (Str, Localtimemark, False) {separator = sep_localtimemark; Else IF (Matchword (Str, CJKYEARSUFF, FALSE) || Matchword (Str, KoreAnyEarsuff, false) { Separator = sep_yearsuff; } Else IF (Str, Cjkmonthsuff, False) || Matchword (Str, KoreanMonthsuff, False)) { Separator = SEP_MONTHSUFF; } Else IF (Str, Cjkdaysuff, False) || Matchword (Str, KoreAndaysuff, False) { Separator = sep_daysuff; } Else IF (MatchWord (Str, Cjkhoursuff, False) || Matchword (Str, ChineseHoursuff, False) { Separator = sep_hoursuff; } Else IF (Matchword (Str, CjkminuteSuff, False) { Separator = sep_minutesuff; } Else IF (Matchword (Str, Cjksecondsuff, False) { Separator = SEP_SECONDSUFF; } } else { // // NOT a letter. Check if this is a date sepator. // IF ((Matchword (Str, DTFI.DATESEPARATOR, FALSE) || (MatchWord (Str, InvariantIntinfo.dateseParator, False) || (MatchWord (Str, AlternativeDateSeparator, False)))) { // // Notenote: AlternativeDateseParetor is a Special Case Because Some Cultures // (E.G. THE INVARIANT CULTURE) USE "/" .EVER, IN RFC Format, We Use "-" as The // Date Separator. Therefore, We Should Check for IT. // Separator = sep_date; } // // Check if this is a time separator. // Else IF ((Matchword (Str, DTFI.TIMESEPARATOR, FALSE) || (Matchword (STR, InvariantIntinfo.timeseparator, False)))) { Separator = SEP_TIME; } else if (DTFI.CULTUREID == 0x041c) { // Special Case for SQ-Al (0x041c) // ITS Time Pattern IS "H: mm: ss.tt" IF (str.getchar () == '.') { IF (raw.timemark == -1) { // // Check if this is an am Time Mark. // Int Timemark; Str.Index ; IF ((Timemark = GetTimemark (STR, DTFI))! = -1) { Raw.timemark = TIMEMARK ;; RETURN (TIMEMARK == TM_AM? SEP_AM: SEP_PM); } Str.index -; } } } } Return (separator); } // // Check The Word At The Current Index To See IT Matches A Month Name. // Return -1 if a match is not found. OtherWise, a value from 1 to 12 is returned. // Private static int GETMONTHNUMBER (__ DTSTRING STR, DATETIMEFORMATINFO DTFI) { // // Check The Month name specified in DTFI. // INT I; INT MONTHINYEAR = (DTFI.GETMONTHNAME (13) .length == 0? 12: 13); INT MAXLEN = 0; Int results = -1; Int index; String word = str.peekcurrentword (); // // we have to match the Month name with the longest length, // Since There Are Cultures Which Have More Than One Month Names // with the same prefix. // For (i = 1; i <= monthinyear; i ) { String Monthname = DTFI.GETMONTHNAME (i); IF ((INDEX = Str.comPareInfo.indexof) Word, MonthName, CompareOptions.Ignorecase))> = 0) { // this condition allows us to Parse the Month Name for: // 1. The General Cases Like "YYYY MMMM DD". // 2. Prefix in the Month Name Like: "DD '/ X05D1'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm YYYY" (Hebrew - Israel) Result = i; Maxlen = Index Monthname.LENGTH; } else IF (Str.StartSwith (Monthname, True) { // the condition allows us to get the Month Name for Cultures // Which Has Spaces in Their Month Names. // E.G.G. IF (Monthname.Length> maxlen) { Result = i; Maxlen = MONTHNAME.LENGTH; } } } IF (Result> -1) { Str.index = maxlen; Return (Result); } For (i = 1; i <= monthinyear; i ) { IF (Matchword (Str, DTFI.GetabbreviatedMonthname (i), false) { Return (i); } } // // Check the Month name in the invariant culture. // For (i = 1; i <= 12; i ) { IF (MatchWord (Str, InvariantIntinfo.getMonthname (i), false) { Return (i); } } For (i = 1; i <= 12; i ) { IF (Matchword (Str, InvariantIntinfo.getabbreviatedMonthname (i), FALSE)) { Return (i); } } Return (-1); } // // Check The Word At The Current Index To See IT Matches a day of week name. // Return -1 if a match is not found. Otherwise, a value from 0 to 6 is returned. // Private static int getdayofweeknumber (__ dtstring str, datetimeformatinfo dtfi) { // // Check The Month name specified in DTFI. // Dayofweek i; INT MAXLEN = 0; Int results = -1; // // we have to match the day name with the longeest length, // Since There Are Cultures Which Have More Than One Day of Week Names // with the same prefix. // INT endindex = str.findendofcurrentword (); String dayName = null; For (i = dayofweek.sunday; i <= dayofweek.saturday; i ) { DayName = dtfi.getdayname (i); IF (str.matchspecifiedword (dayname, endindex) { IF (DAYNAME.LENGTH> MAXLEN) { Result = (int) i; Maxlen = dayname.length; } } } IF (Result> -1) { Str.index = endindex; Return (Result); } For (i = dayofweek.sunday; i <= dayofweek.saturday; i ) { IF (Matchword (STR, DTFI.GetabbreviatedDayname (i), false) { Return (INT) i); } } // // Check the Month name in the invariant culture. // For (i = dayofweek.sunday; i <= dayofweek.saturday; i ) { IF (Matchword (Str, InvariantIntinfo.getDayName (i), false) { Return (INT) i); } } For (i = dayofweek.sunday; i <= dayofweek.saturday; i ) { IF (Matchword (Str, InvariantIntinfo.getabBBBREVIATEDDAYNAME (I), FALSE) { Return (INT) i); } } Return (-1); } // // Check The Word At The Current Index To See IT Matches Gmt Name or Zulu Name. // Private static bool gettimezonename (__ dtstring str) { // // IF (Matchword (Str, GMTNAME, FALSE) { Return (TRUE); } IF (Matchword (Str, Zuluname, False) { Return (TRUE); } Return (False); } // // Create a Japanese DTFI Which Uses JapaneseCalendar. This is buy to pie // Date String with Japanese Era Name Correctly Even WHEN THE Supplied DTFI // Does Not Use Japase Calendar. // The created instance is stored in global m_jajpdtfi. // Private static void getjapanesecalendardtfi () { // Check Calendar.id Specification To Avoid A Lock Here. IF (m_jajpdtfi == null || m_jajpdtfi.calendar.id! = Calendar.cal_japan) { m_jajpdtfi = New CultureInfo ("JA-JP", FALSE) .DATETIMEFORMAT; M_jajpdtfi.calendar = japanesecalendar.getDefault (); } } // // Create a Taiwan DTFI Which Uses Taiwancalendar. This is buy to pie // Date string with Era Name Correctly Even WHEN THE Supplied DTFI // does not use use taiwan calendar. // The created instance is stored in global m_zhtwdtfi. // Private static void gettaiwancalendardtfi () { // Check Calendar.id Specification To Avoid A Lock Here. IF (m_zhtwdtfi == null || m_zhtwdtfi.calendar.id! = Calendar.cal_taiwan) { m_zhtwdtfi = New CultureInfo ("ZH-TW", FALSE) .DATETIMEFORMAT; M_zhtwdtfi.calendar = taiwancalendar.getDefaultInstance (); } } Private static int getra (__ dtstring str, datetimeresult result, ref datetimeformatinfo DTFI) {int [] eras = dtfi.calendar.ras; IF (ERAS! = null) { String word = str.peekcurrentword (); Int ERA; IF ((Era = DTFI.Getera (Word)> 0) { Str.index = word.length; Return (ERA); } Switch (DTFI.CULTUREID) { Case 0x0411: // 0x0411 is The culture id for japanese. IF (DTFI.CALENDAR.ID! = Calendar.cal_japan) { // if The Calendar for DTFI Is Japanese, We Have Already // done the check Above. No need to re-check again. GetJapanesecalendTfi (); IF ((Era = m_jajpdtfi.Getera (Word))> 0) { Str.index = word.length; Result.calendar = Japanesecalendar.getDefaultInstance (); DTFI = m_jajpdtfi; Return (ERA); } } Break; Case 0x0404: // 0x0404 is The culture id for taiwan. IF (dtfi.calendar.id! = calendar.cal_taiwan) { GettaiwancalendardTfi (); IF ((Era = m_zhtwdtfi.Getera (Word))> 0) { Str.index = word.length; Result.calendar = taiwancalendar.getDefaultInstance (); DTFI = m_zhtwdtfi; Return (ERA); } } Break; } } Return (-1); } Private static int gettimemark (__ dTString str, datetimeformatinfo dtfi) { IF ((DTFI.Amdesignator.length> 0) && Matchword (Str, DTFI.Amdesignator, False) || Matchword (Str, InvariantamDesignator, False) { Return (TM_AM); } Else IF ((DTFI.Pmdesignator.Length> 0) && Matchword (Str, DTFI.PmDesignator, False) || Matchword (Str, InvariantPmdesignator, False)) { // // Check if this is an pm time mark. // Return (TM_PM); } Return (-1); } INTERNAL Static Bool Isdigit (Char CH) { // //How do we map full-width char Return (CH> = '0' && ch <= '9'); / * ================================= PARSEFRAction =============== ============ ** Action: Starting At The str.index, if The Current Character Is A Digit, Parse The Remaining ** Numbers as fraction. for example, if the sub-string starting at str.index is "123", THEN ** The Method Will Return 0.123 ** Returns: The Fraction Number. ** arguments: ** Str the Parsing String ** Exceptions: ============================================================================================================================================================================================================= =========================== * / Private static double parsefraction (__ dTString str) { Double result = 0; Double DecimalBase = 0.1; CHAR CH; While (str.index <= str.len-1 && isdigit (ch = str.value [str.index])) { Result = (CH - '0') * Decimalbase; Decimalbase * = 0.1; Str.Index ; } Return (Result); } / * ================================= Parsetimezone =============== ============ ** Action: Parse the Timezone Offset in The Following Format: ** " 8", " 08", " 0800", " 0800" ** this method is buy by datetime.parse (). ** RETURns: The Timezone Offset. ** arguments: ** Str the Parsing String ** Exceptions: ** Formatexception if INVALID TIMEZONE FORMAT IS FOUND. ============================================================================================================================================================================================================= =========================== * / Private static Timespan Parsetimezone (__ dTString str, char offsetchar) { // The Hour / Minute Offset for Timezone. INT HOUROFFSET = 0; INT minuteOffset = 0; IF (str.getnextdigit ()) { // Get the first Digit, Try if We can Paarse Timezone in The Form of " 8". Houroffset = str.getdigit (); IF (str.getnextdigit ()) { // Parsing " 18" Houroffset * = 10; Houroffset = str.getdigit (); IF (str.getnext ()) { CHAR CH; IF (char.isdigit (ch = str.getchar ())) { // PARSING " 1800" // Put The Char Back, Since We Already Get The Char in The Previous GetNext () Call. Str.index -; IF (Parsedigits (Str, 2, True, Out MinuteOffset) {// pasedigits () Does Not Advance The Char for US, SO do It here. Str.Index ; } else { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } } else if (ch == ':') { // Parsing " 18: 00" IF (Parsedigits (STR, 2, True, Out MinuteOffset) { Str.Index ; } else { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } } else { // NOT A DIGIT, NOT A Colon, Put this char back. Str.index -; } } } // the next char is not a digit, so we get the timezone in the form of " 8". } else { // Invalid Timezone: no number after /-. Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } TimeSpan TimezoneOffset = New TimeSpan (Houroffset, MinuteOffset, 0); IF (OffsetChar == '-') { Timezoneoffset = timezoneoffset.negate (); } Return (TimezoneOffset); } // // this is the lexer. Check the Character At The Current Index, And Put The Found Token In DTOK AND // Some Raw Date / Time Information in RAW. // Private static void lex INT DPS, __DTSTSTRING STR, DATETITOKEN DTOK, DATETIMERAWINFO RAW, DATETIMERESULT RESULT, Ref DateTimeFormatinfo DTFI) { int SEP; DTOK.DTT = DTT_UNK; // Assume The Token is unkown. // // Skip Any White Spaces. // IF (! Str.skipwhitespaceComma ()) { // // SkipwhitespaceComma () Will Return True when End of string is reached. // DTOK.DTT = DTT_END; Return; } CHAR CH = str.getchar (); IF (Char.isletter (CH)) { // // this is a letter. // Int Month, Dayofweek, Era, Timemark // // Check if this is a beginning of a month name. // and check if this is a day of week name.// IF (Raw.MontH == -1 && (Month = getMonthnumber (STR, DTFI))> = 1) { // // this is a month name // Switch (SEP = GetSeparator (STR, RAW, DTFI)) { Case Sep_end: DTOK.DTT = DTT_MONTHEND; Break; Case Sep_Space: DTOK.DTT = DTT_MONTHSPACE; Break; Case sep_date: DTOK.DTT = DTT_MONTHDATESEP; Break; DEFAULT: // Invalid Separetor after Month Name Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Raw.month = month; } Else if (Raw.dayofwek == -1 && (Dayofweek = GetdayofweekNumber (STR, DTFI))> = 0) { // // this is a day of week name. // Raw.dayofweek = dayofweek; DTOK.DTT = DTT_DAYOFWEEK; // // discard the separator. // GetSeparator (STR, RAW, DTFI); } Else IF (GetTimezonename (STR)) { // // this is a Timezone Designator // // notenote: for now, We Only Support "GMT" and "for zulu time). // DTOK.DTT = DTT_TIMEZONE; Result.timezoneused = true; Result.timezoneoffset = new timeespan (0); } else IF ((Raw.era == -1) && ((Era = getra (Str, Result, Ref DTFI))! = -1)) { Raw.era = ERA; DTOK.DTT = DTT_ERA; Else if (Raw.Timemark == -1 && (Timemark = GetTimemark (STR, DTFI))! = -1) { Raw.Timemark = TIMEMARK; GetSeparator (STR, RAW, DTFI); } else { // // NOT A MONTH Name, Not a day of week name. Check if this is one of the // KNown Date Words. this is buy to deal case Like Spanish Cultures, Which // Uses 'de' in Their Date String. // // IF (! Str.matchwords) { Throw new formatexception String.format (Environment.getResourceString ("Format_UnknowDateTimeWord"), str.index); GetSeparator (STR, RAW, DTFI); } } else if (char.isdigit (ch)) { IF (raw.numcount == 3) { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } // // this is a DIGIT. // INT Number = CH - '0'; INT Digitcount = 1; // // Collect Other Digits. // While (Str.GetnextDigit ()) { Number = Number * 10 str.getdigit (); Digitcount ; } // if the prepious paingsing state is ds_t_nnt (like 12:01), and we got Another Number, // SO We Will Have a Terminal State DS_TX_NNN (Like 12:01:02). // if the prepious paingsing state is ds_t_nt (like 12 :), and we got Another Number, // SO We Will Have a Terminal State DS_TX_NN (Like 12:01:02). // // Look Ahead to see et ly the folowing character is a decimal point or timezone offset. // this enables US to Parse Time in the Forms of: // "11: 22: 33.1234" OR "11: 22: 33-08". IF (DPS == DS_T_NNT || DPS == DS_T_NT) { Char nextch; IF (Str.Index Nextch = Str.Value [str.index]; Switch (nextch) { Case '.': IF (DPS == DS_T_NNT) { // yes, advance to the next character. Str.Index ; // collect the second fraction. Raw.fraction = parsefraction (STR); } Break; Case ' ': Case '-': IF (result.timezoneused) { // Should Not Have Two TimeZone Offsets. Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Result.timezoneused = true; Result.timezoneOffset = Parsetimezone (STR, NextCH); Break; } } } IF (Number> = 0) { DTOK.NUM = NUMBER; IF (Digitcount> = 3) { IF (Raw.Year == -1) { Raw.year = Number; // // if we have name which has 3 or more Digits (Like "001" 0001 "), // We Assume this Number is a year. Save the currnet raw.numcount in // raw.year. // Switch (SEP = GetSeparator (STR, RAW, DTFI)) { Case Sep_end: DTOK.DTT = DTT_YEAREnd; Break; Case Sep_AM: Case Sep_PM: Case Sep_Space: DTOK.DTT = DTT_YEARSPACE; Break; Case sep_date: DTOK.DTT = DTT_YAARDATESEP; Break; Case Sep_YearSuff: Case Sep_Monthsuff: Case Sep_daySuff: DTOK.DTT = DTT_NUMDATESUFF; DTOK.SUFFIX = SEP; Break; Case Sep_Hoursuff: Case Sep_MinuteSUFF: Case Sep_secondsuff: DTOK.DTT = DTT_NUMTIMESUFF; DTOK.SUFFIX = SEP; Break; DEFAULT: // Invalid Separator After Number Number. Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } // // Found the token already. Let's bail. // Return; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Else { // // Number is overflowed. // Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Switch (SEP = GetSeparator (STR, RAW, DTFI)) { // // Note Here We Check if The Numcount is Less THREE. // WHEN WE HAVE MORE THREE NUMBERS, IT WILL BE CAUGHT AS ERROR in The State Machine. // Case Sep_end: DTOK.DTT = DTT_NUMEND; Raw.Num [Raw.Numcount ] = DTOK.NUM; Break; Case Sep_AM: Case Sep_PM: DTOK.DTT = DTT_NUMAMPM; Raw.Num [Raw.Numcount ] = DTOK.NUM; Break; Case Sep_Space: DTOK.DTT = DTT_NUMSPACE; Raw.Num [Raw.Numcount ] = DTOK.NUM; Break; Case sep_date: DTOK.DTT = DTT_NUMDATESEP; Raw.Num [Raw.Numcount ] = DTOK.NUM; Break; Case Sep_Time: IF (! result.timezoneused) { DTOK.DTT = DTT_NUMTIMESEP; Raw.Num [Raw.Numcount ] = DTOK.NUM; } else { // if We Already Got Timezone, There Should Be No // Time separator again. Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Break; Case Sep_YearSuff: Dtok.num = dtfi.calendar.tofourdigityear DTOK.DTT = DTT_NUMDATESUFF; DTOK.SUFFIX = SEP; Break; Case Sep_Monthsuff: Case Sep_daySuff: DTOK.DTT = DTT_NUMDATESUFF; DTOK.SUFFIX = SEP; Break; Case Sep_Hoursuff: Case Sep_MinuteSUFF: Case Sep_secondsuff: DTOK.DTT = DTT_NUMTIMESUFF; DTOK.SUFFIX = SEP; Break; Case Sep_localtimemark: DTOK.DTT = DTT_NUMLOCALTIMARK; Raw.Num [Raw.Numcount ] = DTOK.NUM; Break; DEFAULT: // Invalid Separator After Number Number. Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } } Else { // // NOT a letter, NOT A DIGIT. Just Ignore IT. // Str.Index ; } Return; } Private const Int ORDER_YMD = 0; // the order of date is year / month / day. Private const Int ORDER_MDY = 1; // The Order of Date is Month / Day / Year. Private const Int ORDER_DMY = 2; // The Order of Date is day / month / year. Private const Int ORDER_YDM = 3; // The Order of Date is Year / Day / Month Private const Int ORDER_YM = 4; // Year / Month Order. Private const Int ORDER_MY = 5; // Month / Year ORDER. Private const Int ORDER_MD = 6; // Month / Day Order. Private const Int ORDER_DM = 7; // day / month Order. // // DECIDE The year / month / day order from the datepattern. // // Return 0 for YMD, 1 for MDY, 2 for DMY, OtherWise -1. // Private static int getYearMonthdayorder (String DatePattern, DateTimeFormatinfo DTFI) { Int yearorder = -1; INT MONTHORDER = -1; INT dayorder = -1; Int ORDERCOUNT = 0; BOOL Inquote = false; For (int i = 0; i { Char ch = datepattern [i]; IF (CH == '/' '|| CH ==' ") { Inquote =! Inquote; } IF (! inquote) { IF (CH == 'Y') { Yearorder = OrderCount ; // // Skip all year pattern charatrs. // For (; i 1 { // do nothing here. } } ELSE IF (CH == 'M') { Monthorder = OrderCount ; // // Skip all Month Pattern Characters. // For (; i 1 { // do nothing here. } } Else IF (CH == 'D') { INT patterncount = 1; // // Skip all day pattern characters. // For (; i 1 { Patterncount ; } // // Make Sure this is not "DDD" or "dddd", Which means day of week. // IF (Patterncount <= 2) { Dayorder = Ordercount ; } } } } IF (Yearorder == 0 && Monthorder == 1 && dayorder == 2) { Return (ORDER_YMD); } IF (Monthorder == 0 && dayorder == 1 && yearorder == 2) { Return (ORDER_MDY); } IF (Dayorder == 0 && Monthorder == 1 && Yearorder == 2) { Return (ORDER_DMY); } IF (Yearorder == 0 && Dayorder == 1 && Monthorder == 2) { Return (ORDER_YDM); } Throw new formatexception (String.Format (Environment.GetResourceString ("format_baddatepattern"); DatePattern); } // // Decide the year / month ORDER from the pattern. // // Return 0 for YM, 1 for my, OtherWise -1. // Private Static Int GetYarmonthORDER (String Pattern, DateTimeformatinfo DTFI) { Int yearorder = -1; INT MONTHORDER = -1; Int ORDERCOUNT = 0; BOOL Inquote = false; For (int i = 0; i { Char ch = pattern [i]; IF (CH == '/' '|| CH ==' ") { Inquote =! Inquote; } IF (! inquote) { IF (CH == 'Y') { Yearorder = OrderCount ; // // Skip all year pattern charatrs. // For (; i 1 { } } ELSE IF (CH == 'M') { Monthorder = OrderCount ; // // Skip all Month Pattern Characters. // For (; i 1 { } } } } IF (Yearorder == 0 && Monthorder == 1) { Return (ORDER_YM); } IF (Monthorder == 0 && Yearorder == 1) { Return (ORDER_MY); } Throw new formatexception (sincevironment.get ("format_baddatepattern"), Pattern); // // Decide the Month / Day Order from the pattern. // // Return 0 for MD, 1 for DM, OtherWise -1. // Private static int GETMONTHDAYORDER (String Pattern, DateTimeFormatinfo DTFI) { INT MONTHORDER = -1; INT dayorder = -1; Int ORDERCOUNT = 0; BOOL Inquote = false; For (int i = 0; i { Char ch = pattern [i]; IF (CH == '/' '|| CH ==' ") { Inquote =! Inquote; } IF (! inquote) { IF (CH == 'D') { INT patterncount = 1; // // Skip all day pattern charatrs. // For (; i 1 { Patterncount ; } // // Make Sure this is not "DDD" or "dddd", Which means day of week. // IF (Patterncount <= 2) { Dayorder = Ordercount ; } } ELSE IF (CH == 'M') { Monthorder = OrderCount ; // // Skip all Month Pattern Characters. // For (; i 1 { } } } } IF (MonthOrder == 0 && dayorder == 1) { Return (ORDER_MD); } IF (Dayorder == 0 && Monthorder == 1) { Return (ORDER_DM); } Throw new formatexception (sincevironment.get ("Format_BaddatePattern"), Pattern); } Private Static Bool IsvalidMonth (DateTimeResult Result, Int Year, Int Month) { Return (Month> = 1 && Month <= result.calendar.getmonthsinyear (year)); // // Notenote: this funciton assumes That year / month is the correct. So call isvalidmonth Before Calling this. // Private Static Bool Isvalidday (DatetimeResult Result, Int Year, Int Month, Int Day) { Return (day> = 1 && day <= result.calendar.getDaysinmonth (year, month)); } // // adjust the two-Digit year if Necessary. // Private static int adjuthear (DateTimeResult Result, int year) { IF (Year <100) { Year = result.calendar.tofourdigityear (year); } Return (Year); } Private Static Bool SetDateymd (DateTimeResult Result, Int Year, Int Month, Int Day) { IF (isvalidMont (Result, Year, Month) && Isvalidday (Result, Year, Month, DAY) { Result.SetDate (Year, Month, Day); // YMD Return (TRUE); } Return (False); } Private Static Bool SetDatemdy (DateTimeResult Result, Int Month, Int Day, Int Year) { Return (SetD, Year, Month, DAY); } Private Static Bool SetDatedmy (DateTimeResult Result, Int Day, Int Month, Int Year) { Return (SetD, Year, Month, DAY); } Private static bool setdm (DateTimeResult Result, Int Year, Int Day, Int Month) { Return (SetD, Year, Month, DAY); } // processing Teriminal Case: DS_DX_NN Private static void getdayofnn (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { INT N1 = Raw.NUM [0]; INT N2 = Raw.NUM [1]; Int year = result.calendar.getYear (datetime.now); Int ORDER = GetMonthdayorder (DTFI.MOTHDAYPATTERN, DTFI); IF (ORDER == Order_md) { IF (SetDateymd (Result, Year, N1, N2)) // MD { Return; } } else {// Order_dm IF (SetD, Year, N2, N1)) // DM { Return; } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } // processing Teriminal Case: DS_DX_NNN Private static void getdayofnnn (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { INT N1 = Raw.NUM [0]; INT N2 = Raw.NUM [1] ;; INT N3 = Raw.NUM [2]; Int ORDER = GetYearMonthdayorder (DTFI.ShortdatePattern, DTFI); IF (ORDER == Order_YMD) { IF (setDateymd (Result, AdjustYear (Result, N1), N2, N3) // YMD { Return; } } else if (order == Order_mdy) { IF (SetDATEMDY (Result, N1, N2, Adjustyear (Result, N3))) // mdy { Return; } } else if (order == order_dmy) { IF (SetDatedmy (Result, N1, N2, AdjustYear (Result, N3))) // DMY { Return; } } else if (Order == Order_YDM) { IF (setd, Adjustyear (Result, N1), N2, N3) // YDM { Return; } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static void getDayofmn (DateTimeResult Result, DateTimeFormatinfo DTFI) { INT currentyear = result.calendar.getyear (datetime.now); Result.month = raw.month; // // Notenote: in the case of invariant Culture, // We will have an Ambiguous Situation WHEN We Have A String "June 11". // it could be 11-06-01 or currentyear-06-11. // in here, We favor currentyear-06-11 by checking the month. // Int monthdayorder = getMonthdayorder (DTFI.MONTHDAYPATTERN, DTFI); IF (MontHDayOrder == Order_md) { IF (setd, currentyear, raw.month, raw.num [0]))) { Return; } } else if (MonthDayOrder == Order_dm) { IF (setd, currentyear, raw.month, raw.num [0]))) { Return; } } Int Yearmonthorder = getYearMonthorder (DTFI.YearMonthpattern, DTFI); IF (YearmonthOrder == Order_my) { IF (isvalidmonth (result, raw.num [0], raw.month)) { Result.Year = Raw.Num [0]; Result.day = 1; Return; } } IF (isvalidday (result, currentyear, result.month, raw.num [0]))) { Result.Year = currentyear; Result.day = raw.num [0]; Return; } IF (isvalidday (result, raw.num [0], result.mont, 1))) { Result.Year = Raw.Num [0]; Result.day = 1; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static Void getDayOfnm (DateTimeResult Result, DateTimeFormatinfo DTFI) { INT currentyear = result.calendar.getyear (datetime.now); Result.month = raw.month; // Check Month / Day First Before Checking Year / Month. // The logic here is that people often Uses 4 Digit for Years, Which Will Be Captured by getdayofym (). // therefore, we Assume a Number Followed by a month is generally a month / day. Int monthdayorder = getMonthdayorder (DTFI.MONTHDAYPATTERN, DTFI); IF (MontHDayorder == Order_dm) { Result.Year = currentyear; IF (isvalidday (result, result.year, raw.month, raw.num [0])) { Result.day = raw.num [0]; Return; } } Int Yearmonthorder = getYearMonthorder (DTFI.YearMonthpattern, DTFI); IF (YearMonthOrder == Order_YM) { IF (isvalidmonth (result, raw.num [0], raw.month)) { Result.Year = Raw.Num [0]; Result.day = 1; Return; } } // // Notenote: in the case of invariant Culture, // We will have an Ambiguous Situation WHEN WHEN WHEN WHEN WE HAVE A STRING "June 11" .// IT IS Ambiguous Because The Month Day Pattern IS "MMMM DD", // and year month pattern is "mmmm, yyyy". // there, it could be 11-06-01 or currentyear-06-11. // in here, We favor currentyear-06-11 by checking the month. // IF (isvalidday (result, currentyear, result.month, raw.num [0]))) { Result.Year = currentyear; Result.day = raw.num [0]; Return; } IF (isvalidday (result, raw.num [0], result.mont, 1))) { Result.Year = Raw.Num [0]; Result.day = 1; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private static void getdayofmnn (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { INT N1 = Raw.NUM [0]; INT N2 = Raw.NUM [1]; Int ORDER = GetYearMonthdayorder (DTFI.ShortdatePattern, DTFI); Int year; IF (Order == Order_MDY) { IF (isvalidday (Result, Year = AdjustYear (Result, N2), Raw.Month, N1)) { Result.Setdate (Year, Raw.month, N1); // MDY Return; } Else if (isvalidday (result, year = adjustyear (result, n1), raw.month, n2))) { Result.Setdate (Year, Raw.month, N2); // YMD Return; } } Else if (ORDER == Order_YMD) { IF (isvalidday (result, year = adjustyear (result, n1), raw.month, n2)) { Result.Setdate (Year, Raw.month, N2); // YMD Return; } Else if (isvalidday (result, year = adjustyear (result, n2), raw.month, n1))) { Result.SetDate (Year, Raw.month, N1); // DMY Return; } } Else if (ORDER == Order_dmy) { IF (isvalidday (Result, Year = AdjustYear (Result, N2), Raw.Month, N1)) { Result.SetDate (Year, Raw.month, N1); // DMY Return; } Else if (isvalidday (result, year = adjustyear (result, n1), raw.month, n2))) { Result.Setdate (Year, Raw.month, N2); // YMD Return; } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static Void getDayOfynnn (DateTimeResult Result, DateTimeFormatinfo DTFI) { INT N1 = Raw.NUM [0]; INT N2 = Raw.NUM [1]; IF (DTFI.CULTUREID == 0x0437) { // 0x0437 = georgian - georgia (ka-ge) // Very Special Case for KA-GE: // ITS short date patten is "dd.mm.yyyy" (ORDER_DMY). // HOWEVER, ITS Long Date Pattern Is "YYYY '/ X10EC / X10DA / X10D8 / X10E1' DD MM, DDDD" (ORDER_YDM) Int ORDER = GetYearMonthdayorder (DTFI.longdatePattern, DTFI); IF (ORDER == Order_ydm) { IF (SetDATEYMD (Result, Raw.year, N2, N1)) { Return; // Year DM } } else { IF (SetDATEYMD (Result, Raw.year, N1, N2)) { Return; // Year MD } } } else { // OtherWise, Assume IT is Year / Month / Day. IF (SetDATEYMD (Result, Raw.year, N1, N2)) { Return; // Year MD } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private static void getdayofnny (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { INT N1 = Raw.NUM [0]; INT N2 = Raw.NUM [1]; Int ORDER = GetYearMonthdayorder (DTFI.ShortdatePattern, DTFI); IF (Order == Order_MDY || ORDER == ORDER_YMD) { IF (SetDATEYMD (Result, Raw.year, N1, N2)) { Return; // md year } } else { IF (SetDATEYMD (Result, Raw.year, N2, N1)) { Return; // DM year } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static Void, DateTimeResult Result, DateTimeRawinfo Raw, DateTimeformatinfo DTFI {ix (setd, raw.year, raw.month, Raw.Num [0])) { Return; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static void getDayofyn (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { IF (setd, raw.year, raw.num [0], 1)) { Return; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static Void GetdayOfym (DateTimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { IF (setd, raw.year, raw.month, 1)) { Return; } Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Private Static Void AdjustTimemark (DateTimeformatinfo DTFI, datetimerawinfo RAW) { // Specail Case for Culture Which Uses AM as Empty String. // E.g. AF-ZA (0x0436) // S1159 / X0000 // s2359 nm // in this case, IF we are Parsing a string Like "2005/09/14 12:23", We Well Assume this is in am. IF (Raw.Timemark == -1) { IF (DTFI.AMDesignator! = null& Dtfi.pmdesignator! = null) { IF (dtfi.amdesignator.length == 0 && Dtfi.pmdesignator.length! = 0) { Raw.Timemark = TM_AM; } IF (dtfi.pmdesignator.length == 0 && dtfi.amdesignator.length! = 0) { Raw.Timemark = TM_PM; } } } } // // Adjust Hour According to the Time Mark. // Private Static Int Adjust Hour (datetimerawinfo raw) { INT Hour = Raw.NUM [0]; IF (Hour <0 || HOUR> 12) { Throw new formatexception (Environment.GetResourceString ("format_baddatetime");} IF (Raw.Timemark == TM_AM) { Hour = (hour == 12)? 0: hour; } Else { Hour = (hour == 12)? 12: Hour 12; } Return (Hour); } Private static void gettimeOfn (DateTimeFormatinfo DTFI, DateTimeResult Result, DateTimeRawinfo Raw) { // // in this case, we need a time mark. Check if SO. // IF (raw.timemark == -1) { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } AdjustTimemark (DTFI, RAW); Result.Hour = Adjust Hour (RAW); } Private static void gettimeOfnn (DateTimeFormatinfo DTFI, DateTimeResult Result, DateTimeRawinfo Raw) { IF (Raw.Numcount <2) { Throw new invalidoperationException (environments); } AdjustTimemark (DTFI, RAW); Result.Hour = (Raw.Timemark == - 1)? Raw.Num [0]: AdjustHour (RAW); Result.minute = Raw.Num [1]; } Private static void gettimeOfnnn (DateTimeFormatinfo DTFI, DateTimeResult Result, DateTimeRawinfo Raw) { IF (Raw.Numcount <3) { Throw new invalidoperationException (environments); } AdjustTimemark (DTFI, RAW); Result.Hour = (Raw.Timemark == - 1)? Raw.Num [0]: AdjustHour (RAW); Result.minute = Raw.Num [1]; Result.second = Raw.Num [2]; } // // Processing Terminal State: a date suffix backed by one number. // Private static void getdateofdsn (DateTimeResult Result, DateTimeRawinfo RAW) { IF (Raw.Numcount! = 1 || Result.day! = -1) { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Result.day = raw.num [0]; } Private static void getdateofnd (DateTimeResult Result, DateTimeRawnfo Raw) { IF (Result.Month == -1) { // Should Have a Month Suffix Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } IF (result.year! = -1) { // Aleady Has A Year SUFFIX Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } Result.Year = Raw.Num [0]; Result.day = 1; } Private static void getDateOfnnds (DatetimeResult Result, DateTimeRawinfo Raw, DateTimeFormatinfo DTFI) { Int ORDER = GetYearMonthdayorder (DTFI.ShortdatePattern, DTFI); Switch (Order) { Case Order_YMD: Break; Case Order_MDY: Break; Case Order_Dmy: if (Result.day == -1 && Result.Year == -1) { IF (isvalidday (result, raw.num [1], result.month, raw.num [0]))) { Result.Year = Raw.Num [1]; Result.day = raw.num [0]; } } Break; } } // // a Date Suffix Is Found, Use this method to put the number inTo the result. // Private Static Void ProcessDateTimeSuffix (DateTimeResult Result, DateTimeRawinfo Raw, DateTimetoken DTOK) { Switch (dtok.suffix) { Case Sep_YearSuff: Result.Year = Raw.year = dtok.num; Break; Case Sep_Monthsuff: Result.month = Raw.month = DTOK.NUM; Break; Case Sep_daySuff: Result.day = dtok.num; Break; Case Sep_Hoursuff: Result.Hour = dtok.num; Break; Case Sep_MinuteSUFF: Result.minute = dtok.num; Break; Case Sep_secondsuff: Result.second = dtok.num; Break; } } // // a Terminal State Has Been Reached, Call The Appropriate Function To Fill in The Parsing Result. // Return True if The State Is a Terminal State. // Private Static Void ProcessterMinaltState (int DPS, DateTimeResult Result, DateTimeFormatinfo DTFI) { Switch (DPS) { Case ds_dx_nn: GetdayOfnn (Result, RAW, DTFI); Break; Case ds_dx_nnn: GetdayOfnnn (Result, RAW, DTFI); Break; Case DS_DX_MN: GetdayOfmn (Result, RAW, DTFI); Break; Case ds_dx_nm: GetdayOfnm (Result, RAW, DTFI); Break; Case DS_DX_MNN: GetdayOfmnn (Result, RAW, DTFI); Break; Case DS_DX_DS: // The result has got the correct value. No need to process. Break; Case DS_DX_YNN: GetdayOfynn (Result, RAW, DTFI); Break; Case DS_DX_NNY: Getdayofnny (Result, RAW, DTFI); Break; Case DS_DX_YMN: GetdayOfyMn (Result, RAW, DTFI); Break; Case DS_DX_YN: GetdayOfyn (Result, RAW, DTFI); Break; Case DS_DX_YM: GetdayOfym (Result, RAW, DTFI); Break; Case DS_TX_N: GetTimeOfn (DTFI, Result, RAW); Break; Case DS_TX_NN: GetTimeOfnn (DTFI, RESULT, RAW); Break; Case DS_TX_NNN: GetTimeOfnn (DTFI, RESULT, RAW); Break; Case DS_TX_TS: // The result has got the correct value. No need to process. Break; Case DS_DX_DSN: GetDateOfDSN (Result, RAW); Break; Case DS_DX_NDS: GetDateOfnds (Result, RAW); Break; Case ds_dx_nnds: GetDateOfnnds (Result, RAW, DTFI); Break; } IF (DPS> DS_ERROR) { // // We since the RECHED A TERMINAL STATE. RESET The RAW NUM Count. // Raw.Numcount = 0; } Return; } // // this is the real method to do the paingsing. // Internal Static DateTime Parse (String S, DateTimeformatinfo DTFI, DateTimeStyles Styles) { IF (s == NULL) { Throw New Argumentnullexception ("s", Environment.getResourceString ("argumentnull_string")))); } IF (s.length == 0) { Throw new formatexception (Environment.GetResourceString ("format_baddatetime");} IF (DTFI == NULL) { DTFI = DateTimeFormatinfo.currentInfo; } DateTime Time; // // first try the predefined format. // INT DPS = DS_BEGIN; // Date Parsing State. Bool ReachterminalState = false; DatetimeResult result = new datetimeresult (); // The buffer to store the Parsing Result. DateTimeToken DTOK = New DateTimetoken (); // The buffer to store the paingsing token. DateTimeRawinfo Raw = new datetimerawinfo (); // The buffer to store temporary paingsing information. Result.calendar = DTFI.CALENDAR; // // the string to be pased. Use a __dtstring wrapper so what we can travel the index which // indeicates the beginning of next token. // __Dtstring str = new __dtstring (s); Str.getnext (); // // The following loop will Break out at the end by the end. // Do { // // Call the lexer to get the next token. // // if we find a era in lex (), The Era Value Will Be in Raw.ea. Lex (DPS, STR, DTOK, RAW, RESULT, REF DTFI); // // if the token is not unknown, Process IT. // OtherWise, Just Discard IT. // IF (dtok.dt! = dtt_unk) { // // Check if We got any CJK Date / Time Suffix. // Since the date / time suffix tells us the number belongs to year / month / day / hour / minute / second, // Store The Number in The Appropriate Field in The Result. // IF (dtok.suffix! = sep_unk) { ProcessDateTimeSuffix (Result, RAW, DTOK); DTOK.SUFFIX = SEP_UNK; // RESET SUFFIX TO SEP_UNK; } IF (DPS == DS_D_YN && DTOK.DTT == DTT_NUMLOCALTIMARK) { // consider this as ISO 8601 Format: // "YYYY-mm-dd't'HH: mm: ss" 1999-10-31t02: 00: 00Return (Parseiso8601 (RAW, STR, STYLES)); } // // advance to the next state, and continue // DPS = DateParsingStates [DPS] [DTOK.DTT]; IF (DPS == DS_ERROR) { Bcldebug.Trace ("NLS", "DateTime (): DPS IS DS_ERROR"); Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } ELSE IF (DPS> DS_ERROR) { ProcessterminalTState (DPS, Result, RAW, DTFI); REACHTERMINALSTATE = TRUE; // // if we have reached a Terminal State, Start over from DS_BEGIN AGAIN. // for Example, WHEN We pased "1999-12-23 13:30", We Will Reach a Terminal State AT "1999-12-23", // And We Start over So We CAN Continue to Parse "12:30". // DPS = DS_BEGIN; } } } while (dtok.dt! = dtt_end && dtok.dt! = dtt_numend && dtok.dtt! = dtt_monthend); IF (! REACHTERMINALSTATE) { Bcldebug.Trace ("NLS", "DateTimeParse.Dopival (): Terminal State Is Not Reached Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } // Check if the pararated String Only Contains Hour / Minute / Second VALUES. Bool btimeonly = (Result.Year == -1 && results == -1 && result.day == -1); // // Check if any year year / month / day is missing in the paarsing string. // if Yes, Get The Default Value from Today's Date. // CheckdefaultDatetime (Result, Ref Result.calendar (STYLES); Try { IF (raw.era == -1) { Raw.era = calendar.currentera; } Time = result.calendar.todatetime (Result.Year, Result.month, Result.day, Result.Hour, Result.minute, Result.second, 0, Raw.era); if (Raw.fraction> 0) { Time = Time.Addticks ((long) Math.Round (Raw.fraction * Calendar.ticksperseCond); } } catch (exception) { BCLDebug.Trace ("NLS", "DateTimeParse.doparse (): Time is Bad"); Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } // // Notenote: // We Have to Check Day of Week Before We Adjust to The Time Zone. // OtherWise, The Value of Day of Week May Change After Adjustting to the Time Zone. // IF (Raw.dayofweek! = -1) { // // Check if day of week is the correct. // IF (Raw.dayofweek! = (int) result.calendar.getdayofweek (time)) { BCLDebug.Trace ("NLS", "DateTimeParse.Doparse (): Day of Week Is Not Correct"); Throw new formatexception (Environment.GetResourceString ("Format_Baddayofweek)); } } IF (result.timezoneused) { Time = AdjustTimezone (Time, Result.TimezoneOffset, Styles, Btimeonly); } Return (Time); } Private Static DateTime AdjustTIMEZONE (DateTime Time) { IF (Sytles & DateTimeStyles.adjustTouniversal)! = 0) { Return (AdjustTimezoneTouniversal (Time, TimezoneOffset); } Return (AdjustTimezonetolocal (Time, TimezoneOffset, Btimeonly); } // // Adjust The Specified Time To Universal Time Based on The Supplied Timezone. // E.G. When Parsing "2001/06/08 14: 00-07: 00", // The Time IS 2001/06/08 14:00, and TimezoneOffset = -07: 00. // the result will be "2001/06/08 21:00" // Private Static DateTime AdjustTimeZonetouniversal (DateTime Time, TimeSpan TimezoneOffset) { Long Resulttyts = Time.ticks; Resultticks - = TimezoneOffset.ticks; if (Resulttyicks <0) { Resultticks = Calendar.ticksperday; } IF (Resultticks <0) { Throw new formatexception ("format_dateoutofrange)); } Return (New DateTime (Resultticks)); } // // adjust the specified time to universal time based on the support timezone, // and then convert to local time. // E.G. When Parsing "2001/06/08 14: 00-04: 00", And Local Timezone IS GMT-7. // The time is 2001/06/08 14:00, and TimezoneOffset = -05: 00. // the result will be "2001/06/08 11:00" // Private Static DateTime AdjustTIMEZONETOLOCAL (DateTime Time, Timespan Timezoneoffset, Bool Btimeonly) { Long Resulttyts = Time.ticks; // Convert to Local Ticks IF (Resultticks // // this is time of day. // // adjust timezone. Resultticks - = TimezoneOffset.ticks; // if The time is time of day, use the current timezone offset. Resultticks = timezone.currenttimezone.getutcoffset (btimeonly? DateTime.now: Time) .ticks; IF (Resultticks <0) { Resultticks = Calendar.ticksperday; } } else { // Adjust Timezone to GMT. Resultticks - = TimezoneOffset.ticks; IF (Resultticks> DateTime.maxValue.ticks) { // if the result ticks is get Than DateTime.maxValue, We can not create a datetime from this ticks. // in this case, keep using the old code. // this code path is buy to get around bugs 78411. Resultticks = Timezone.currentTimezone.getutcoffset (Time) .ticks; } else { // Convert The Gmt Time to Local Time. Return (New DateTime (Resultticks) .tolocalTime ()); } } IF (Resultticks <0) { Throw new formatexception (Environment.getResourceString ("format_dateoutofrange));} Return (New DateTime (Resultticks)); } // // Parse the iso8601 format string found during parse (); // // Private Static DateTime Parseiso8601 (DateTimeRawinfo Raw, __dtstring str, datetimesteles styles) { IF (Raw.Year <0 | Raw.Num [0] <0 || Raw.Num [1] <0) { } Str.index -; Int Hour, Minute, Second; Bool Timezoneused = false; Timespan timezoneoffset = new timeespan (); DateTime Time = New DateTime (0); Double partsecond = 0; Str.skipwhitespaces (); Parsedigits (STR, 2, True, Out Hour); Str.skipwhitespaces (); IF (str.match (':')) { Str.skipwhitespaces (); Parsedigits (STR, 2, True, Out Minute); Str.skipwhitespaces (); IF (str.match (':')) { Str.skipwhitespaces (); Parsedigits (STR, 2, TRUE, OUT Second); Str.skipwhitespaces (); IF (str.getnext ()) { CHAR CH = str.getchar (); IF (CH == ' ' || CH == '-') { Timezoneused = true; TimezoneOffset = Parsetimezone (Str, Str.getChar ()); } else IF (CH == '.') { Str.Index ; // Parsefraction Requires US to Advance to the next character. Partsecond = PARSEFRAction (STR); } else if (ch == 'Z' || CH == 'z') { Timezoneused = true; } else { Throw new formatexception (Environment.GetResourceString ("format_baddatetime"))); } } Time = new datetime (Raw.year, Raw.NUM [0], Raw.Num [1], Hour, Minute, Second); Time = Time.Addticks ((long) Math.Round (Partsecond * Calendar.tickspersecond); IF (Timezoneused) { Time = AdjustTimezone (Time, TimezoneOffset, Styles, False); } Return Time; } } Throw new formatexception (Environment.GetResourceString ("format_baddatetime");} / * ================================= PARSEDIGITS =============== ==================== ** Action: Parse the number string in __dtstring That is more formatted using ** The Following Patterns: ** "0", "00", And "000..0" ** RETURns: The Integer Value ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if Error In Parsing Number. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Parsedigits (__ DTSTRING STR, INT Digitlen, Bool IsthrowExp, Out Int Result) { Result = 0; IF (! Str.getNextDigit ()) { Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Result = str.getdigit (); IF (Digitlen == 1) { // when digitlen == 1, Wehibe to Parse Number Like "9" and "19". However, // We Won't go beyond two digits. // // So Let's Look Ahead One Character To See if IT Is A Digit. If Yes, add it to result. IF (str.getnextdigit ()) {result = result * 10 str.getdigit (); } else { // NOT A DIGIT, Let's Roll Back The INDEX. Str.index -; } } else if (Digitlen == 2) { IF (! Str.getNextDigit ()) { Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Result = Result * 10 str.getdigit (); } else { For (int i = 1; i IF (! Str.getNextDigit ()) { Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Result = Result * 10 str.getdigit (); } } Return (TRUE); } / * ================================= PARSEFRActionExact =============== ==================== ** Action: Parse the number string in __dtstring That is more formatted using ** The Following Patterns: ** "0", "00", And "000..0" ** Returns: The Fraction Value ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if Error In Parsing Number. ============================================================================================================================================================================================================= ============================= * / Private Static Bool ParsefractionExact (__ DTString Str, Int Digitlen, Bool IsthrowExp, Ref Double Result) {if (! Str.getNextDigit ()) { Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Result = str.getdigit (); For (int i = 1; i IF (! Str.getNextDigit ()) { Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Result = Result * 10 str.getdigit (); } Result = (Double) Result / Math.Pow (10, Digitlen); Return (TRUE); } / * ================================= PARSESIGN =============== ==================== ** Action: Parse a Positive OR a NEGATIVE SIGN. ** Returns: True if Postive Sign. Flase if Negative Sign. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if End of string is encountered or a sign ** Symbol is not found. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Parsesign (__ DTString Str, Bool IsthrowExp, Ref Bool Result) { IF (! Str.getNext ()) { // a Sign Symbol (' ' or '-') is expected. Ho'ver, end of string is encountered.return (PartSeFormaterror (isthrowexp, "format_baddatetime)); } CHAR CH = str.getchar (); IF (CH == ' ') { Result = TRUE; Return (TRUE); } else if (ch == '-') { Result = false; Return (TRUE); } // a Sign Symbol (' ' or '-') is expected. Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= ParsetimezoneOffset ======================================= ==================== ** Action: Parse the string formatted using "z", "zz", "zzz" in datetime.format (). ** RETURns: The TimeSpan for the Parsed Timezone Offset. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** LEN: The Repeated Number of The "Z" ** Exceptions: Formatexception if Errors in paings. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Parsetimezoneoffset (__ DTSTRING STR, INT LEN, BOOL IsthrowExp, Ref Timespan Result) { Bool ispositive = true; INT HOUROFFSET; INT minuteOffset = 0; Switch (len) { Case 1: Case 2: IF (! Parsesign (Str, IsthrowExp, Ref Ispositive) { Return (False); } IF (! Parsedigits (Str, Len, IsthrowExp, Out Houroffset) { Return (False); } Break; DEFAULT: IF (! Parsesign (Str, IsthrowExp, Ref Ispositive) { Return (False); } IF (! Parsedigits (STR, 2, IsthrowExp, Out Houroffset) { Return (False); } // ':' is Optional. IF (str.match (":")) { // Found ':' IF (! Parsedigits (STR, 2, IsthrowExp, Out minuteOffset) { Return (False); } } else { // Since We can not match ':', Put the char back. Str.index -; IF (! Parsedigits (STR, 2, IsthrowExp, Out minuteOffset) { Return (False); } } Break; } Result = (New Timespan (Houroffset, MinuteOffset, 0); IF (! ispositive) { Result = result.negate (); } Return (TRUE); } / * ================================= matchabbreviatedMonthname ============== ==================== ** Action: Parse The Abbreviated Month Name from string starting at str.index. ** RETURns: a value from 1 to 12 for the first month to the Twelveth Month. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if an abbreviated month name can not be found. ============================================================================================================================================================================================================= ======================================================================================================= INT maxMatchstrlen = 0; Result = -1; IF (str.getnext ()) { // // Scan the Month Names (Note That Some Calendars Has 13 Months) and find // the matching month name Which has the max string length. // we need to do this Because Some Cultures (E.G. "CS-CZ") Which Have // Abbreviated Month Names with The Same Prefix. // INT MONTHSINYEAR = (DTFI.GETMONTHNAME (13) .length == 0? 12: 13); For (INT i = 1; i <= monthsinyear; i ) { String searchstr = DTFI.GetabbreviatedMonthname (i); IF (STR.MATCHSPECifiedWord (SearchStr)) { Int matchstrlen = searchstr.length; IF (matchstrlen> maxmatchstrlen) { Maxmatchstrlen = matchstrlen; Result = i; } } } } IF (Result> 0) { Str.Index = (MaxMatchStrlen - 1); Return (TRUE); } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= MatchMonthname =============== ==================== ** Action: Parse The Month Name from string starting at str.index. ** RETURns: a value from 1 to 12 indicating the first month to the twelveth month. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if a month name can not be found. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Matchmonthname (__ DTString Str, DateTimeformatinfo DTFI, Bool IsthrowExp, Ref Int Result) { INT maxMatchstrlen = 0; Result = -1; IF (str.getnext ()) { // // Scan the Month Names (Note That Some Calendars Has 13 Months) and find // the matching month name Which has the max string length. // we need to do this Because Some Cultures (E.G. "VI-VN") Which Have // Month Names with The Same Prefix. // INT MONTHSINYEAR = (DTFI.GETMONTHNAME (13) .length == 0? 12: 13); For (INT i = 1; i <= monthsinyear; i ) { String searchstr = DTFI.GETMONTHNAME (i); IF (STR.MATCHSPECifiedWord (SearchStr)) { Int matchstrlen = (SearchStr.Length - 1); IF (matchstrlen> maxmatchstrlen) { Maxmatchstrlen = matchstrlen; Result = i; } } } } IF (Result> 0) { Str.Index = maxmatchstrlen; Return (TRUE); } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= matchabbreviateddayName =============== ==================== ** Action: Parse the Abbreviated Day of Week Name from string starting at str .index. ** Returns: a value from 0 to 6 indicating sunday to saturday. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if A Abbreviated Day of Week Name Can not be found. ============================================================================================================================================================================================================= ============================= * / Private Static Bool MatchabbreviatedDayName (__ DTString Str, DateTimeformatinfo DTFI, Bool IsthrowExp, Ref Int Result) { IF (str.getnext ()) { For (Dayofweek I = dayofweek.sunday; i <= dayofweek.saturday; i ) { String searchstr = dtfi.getabbreviateddayName (i); IF (STR.MATCHSPECifiedWord (SearchStr)) { Str.Index = (SearchStr.Length - 1); Result = (int) i; Return (TRUE); } } } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= matchdayname ================ =====================================================================================================================================================================================================00 c ** Returns: a value from 0 to 6 indicating sunday to saturday. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if a day of week name can not be found. ============================================================================================================================================================================================================= ============================= * / Private Static Bool MatchDayName (__ DTString Str, DateTimeformatinfo DTFI, Bool IsthrowExp, Ref Int Result) { // Turkish (TR-TR) Got Day Names with The Same Prefix. INT maxMatchstrlen = 0; Result = -1; IF (str.getnext ()) { For (Dayofweek I = dayofweek.sunday; i <= dayofweek.saturday; i ) { String searchstr = dtfi.getdayname (i); IF (STR.MATCHSPECifiedWord (SearchStr)) { Int matchstrlen = (SearchStr.Length - 1); IF (matchstrlen> maxmatchstrlen) { Maxmatchstrlen = matchstrlen; Result = (int) i; } } } } IF (Result> = 0) { Str.Index = maxmatchstrlen; Return (TRUE); } Return (ParseFormaterror (isthrowexp, "format_baddatetime");} / * ================================= MatCheraname =============== ==================== ** Action: Parse Era Name from string starting at str .index. ** RETURns: an Era Value. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if An Era Name Can not be found. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Matcheraname (__ DTString Str, DateTimeformatinfo DTFI, Bool IsthrowExp, Ref Int Result) { IF (str.getnext ()) { Int [] eras = dtfi.calendar.ras; IF (ERAS! = null) { For (int i = 0; i <= Eras.length; i ) { String searchstr = DTFI.Geteraname (ERAS [I]); IF (STR.MATCHSPECifiedWord (SearchStr)) { Str.Index = (SearchStr.Length - 1); Result = ERAS [I]; Return (TRUE); } SearchStr = DTFI.Getabbreviatedraname (ERAS [I]); IF (STR.MATCHSPECifiedWord (SearchStr)) { Str.Index = (SearchStr.Length - 1); Result = ERAS [I]; Return (TRUE); } } } } Return (ParseFormaterror (isthrowexp, "format_baddatetime");} / * ================================= matchtimemark ============== ==================== ** Action: Parse The Time Mark (AM / PM) from string starting at str.index. ** RETURns: TM_AM ORTM_PM. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if A Time Mark Can NOT BE FOUND. ============================================================================================================================================================================================================= ============================= * / Private Static Bool Matchtimemark (__ DTString Str, DateTimeformatinfo DTFI, Bool IsthrowExp, Ref Int Result) { Result = -1; // in Some Cultures Have Empty strings in AM / PM Mark. E.G. AF-ZA (0x0436), THE AM MARK IS ", And PM Mark IS" NM ". IF (dtfi.amdesignator.length == 0) { Result = TM_AM; } IF (dtfi.pmdesignator.length == 0) { Result = TM_PM; } IF (str.getnext ()) { String searchstr = DTFI.AMDesignator; IF (SearchStr.Length> 0) { IF (STR.MATCHSPECifiedWord (SearchStr)) { // Found AN am Timemark with longenet> 0. Str.Index = (SearchStr.Length - 1); Result = tm_am; return (true); } } Searchstr = DTFI.PMDesignator; IF (SearchStr.Length> 0) { IF (STR.MATCHSPECifiedWord (SearchStr)) { // Found a PM Timemark with longenet> 0. Str.Index = (SearchStr.Length - 1); Result = TM_PM; Return (TRUE); } } // if we can not match The Time Mark Strings with length> 0, // Just returnire Return (TRUE); } IF (Result! = -1) { // if one of the am / pm Marks is Empty String, Return The Result. Return (TRUE); } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= matchabbreviatedtimemark =============== ==================== ** Action: Parse The Abbreviated Time Mark (AM / PM) from string starting at str.index. ** RETURns: TM_AM ORTM_PM. ** Arguments: str: a __dtstring. The Parsing Will Start from the ** Next Character After Str.Index. ** Exceptions: Formatexception if A Abbreviated Time Mark Can NOT BE FOUND. ============================================================================================================================================================================================================= ============================= * / private static bool MatchAbbreviatedTimeMark (__ DTString str, DateTimeFormatInfo dtfi, bool isThrowExp, ref int result) {// NOTENOTE: the assumption here is that abbreviated time mark is the first // Character of the AM / PM Designator. if this Invariant Changes, WE Have To // Change the code below. IF (str.getnext ()) { IF (str.getchar () == DTFI.AMDesignator [0]) { Result = TM_AM; Return (TRUE); } IF (str.getchar () == dtfi.pmdesignator [0]) { Result = TM_PM; Return (TRUE); } } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } / * ================================= ChecknewValue =============== ==================== ** Action: Check if CurrentValue is initialized. If not, returnid. ** if Yes, Check if the capital value is equal to newvalue. Throw argumentexception ** if the is used to check the case like "d" and "dd" Are Both ** USED to format a string. ** Returns: The Correct Value for CurrentValue. ** arguments: ** Exceptions: ============================================================================================================================================================================================================= ============================= * / Private Static Bool ChecknewValue (Ref Int CurrentValue, Int NewValue, CHAR PATTERNCHAR, BOOL Isthrowex) { IF (currentValue == -1) { CurrentValue = NewValue; Return (TRUE); } else { IF (NewValue! = CurrentValue) { BCLDebug.Trace ("NLS", "DateTimeParse.checknewValue ():", Patternchar, "Is Repeated); IF (isthrowex) { Throw new argumentexception String.format (Environment.getResourceString ("format_repeatdatetimepattern"), Patternchar, "Format"); } Return (False); } } Return (TRUE); } Private Static Void CheckdefaultDatetime (DateTimeResult Result, Ref Calendar Cal, DateTimeStyles Styles) { IF ((Result.Year == -1) || (Result.Mont == -1) || (Result.day == -1)) { / * The Following Table Describes the Behaviors of getting the default value When a certin year / month / day value.. An "x" Means That the value exissrs. And "-" Means That Value is Missing. Year Month Day => Resultyear ResultMonth Resultday Note X x x x, Year Parsed Month PaSed Day ................. .. X - x pased year first month pased day if The Month Is Mingsing, Assume First Month of That Year. X - PARSED Year First Month First Day If We Have Only the year, Assume The First Day of That Year. - x currentyear pased Month Parsed day if the year is missing, assume the current year. - X - CurrentYear Parsed month First day If we have only a month value, assume the current year and current day .-- - X CurrentYear First month Parsed day If we have only a day value, assume current year and first month . - - Currentyear Current Month Current Day So this Means That, You Will Get Current Date. * / DateTime now = datetime.now; IF (result.month == -1 && result.day == -1) { IF (result.year == -1) { IF (Styles & DateTimeStyles.nocurrentDatedefault)! = 0) { // if there is no year / month / day value, and no, ocurrentdatedefault flag is buy, // set the year / month / day value to the beginning beginning year / month / day of datetime (). // Note We shop be used Gregorian for the year / month / day. Cal = Gregoriancalendar.getDefaultInstance (); Result.Year = result.month = result.day = 1; } else { // Year / Month / Day Are All Missing. Result.Year = caver.getyear (now); Result.month = Cal.getMonth (now); Result.day = caver.getdayofmonth (now); } } else { // Month / day are Both Missing. Result.month = 1; Result.day = 1; } } else { IF (result.year == -1) { Result.Year = caver.getyear (now); } IF (result.month == -1) { Result.month = 1; } IF (result.day == -1) { Result.day = 1; } } } // set hour / minute / second to zero if these value is not in str. IF (result.Hour == -1) Result.Hour = 0; IF (result.minute == -1) result.minute = 0; IF (result.second == -1) result.second = 0; IF (result.era == -1) Result.Ra = Calendar.currentera; } // Expand a pre-defined format string (Like "D" for long Date) to the real format That // We are going to use in the date time paarsing. // this method also set the DTFI According / ParseInfo To Some Special Pre-Defined // Formats. // Private static string expandpredefinedFormat (String Format, Ref DateTimeformatinfo DTFI, ParsingInfo Parseinfo) { // // check the format to see reason we need to override the dtfi to be invariantinfo, // and see if we need to set up the useruniversaltime flag. // Switch (Format [0]) { Case 'R': Case 'r': // RFC 1123 Standard. (in Universal Time) ParseInfo.calendar = Gregoriancalendar.getDefaultInstance (); DTFI = DateTimeFormatinfo.invariantInfo; Break; Case 's': // Sortable Format (In Local Time) DTFI = DateTimeFormatinfo.invariantInfo; ParseInfo.calendar = Gregoriancalendar.getDefaultInstance (); Break; Case 'u': // Universal Time Format in Sortable Format. ParseInfo.calendar = Gregoriancalendar.getDefaultInstance (); DTFI = DateTimeFormatinfo.invariantInfo; Break; Case 'u': // Universal Time Format with Culture-Dependent Format. ParseInfo.calendar = Gregoriancalendar.getDefaultInstance (); ParseInfo.fuseUniversaltime = True; IF (dtfi.calendar.gettype ()! = TypeOf (Gregoriancalendar) { DTFI = (datetimeformatinfo) DTFI.Clone (); DTFI.CALENDAR = Gregoriancalendar.getDefaultInstance (); } Break; } // // Expand the pre-defined format character to the real format from datetimeformatinfo. // Return (DateTimeFormat.getRealFormat (Format, DTFI)); } // Given a Specified Format Character, Parse and Update The Parsing Result. // Private static bool parsebyformat __Dtstring STR, __Dtstring format, ParsingInfo PaSeinfo, DateTimeFormatinfo DTFI, Bool isthrowexp, DateTimeResult result) { Int tokenlen = 0; INT TEMPYEAR = 0, TempMonth = 0, TempDay = 0, Tempdayofweek = 0, Temphour = 0, Tempminute = 0, Tempsecond = 0; Double TempFraction = 0; INT TEMPTIMEMARK = 0; CHAR CH = format.getchar (); Switch (ch) { Case 'Y': Tokenlen = Format.getRepeatcount (); IF (tokenlen <= 2) { Parseinfo.fusetwodigityear = true; } IF (! Parsedigits (Str, Tokenlen, IsthrowExp, Out Tempyear) { Return (False); } IF (! ChecknewValue (Ref results.year, tempyear, ch, isthrowex) { Return (False); } Break; Case 'M': Tokenlen = Format.getRepeatcount (); IF (tokenlen <= 2) { IF (! Parsedigits (Str, Tokenlen, IsthrowExp, Out Tempmonth) { Return (False); } } else { IF (tokenlen == 3) { IF (! MatchabbreviatedMonthname (Str, DTFI, IsthrowExp, Ref Tempmonth) { Return (False); } } else { IF (! MatchMonthname (Str, DTFI, IsthrowExp, Ref Tempmonth) { Return (False); } } } IF (! ChecknewValue (Ref Resrough.month, Tempmonth, Ch, isthrowex) { Return (False); } Break; Case 'd': // day & day of week Tokenlen = Format.getRepeatcount (); IF (tokenlen <= 2) { // "d" & "dd" IF (! Parsedigits (Str, Tokenlen, IsthrowExp, Out Tempday) { Return (False); } IF (! ChecknewValue (Ref Result.day, TempDay, Ch, isthrowex) { Return (False); } } else { IF (tokenlen == 3) { // "DDD" IF (! MatchabbreviatedDayName (Str, DTFI, IsthrowExp, Ref Tempdayofweek) { Return (False); } } else { // "DDDD *" IF (! MatchdayName (Str, DTFI, IsthrowExp, Ref Tempdayofweek) {Return (false); } } IF (! CHECKNEWVALUE (Ref Parseinfo.dayofweek, tempdayofwek, ch, isthrowexp) { Return (False); } } Break; Case 'g': Tokenlen = Format.getRepeatcount (); // put The Era Value in Result.era. IF (! Matcheraname (Str, DTFI, isthrowexp, ref results.era) { Return (False); } Break; Case 'h': ParseInfo.fuseHOUR12 = true; Tokenlen = Format.getRepeatcount (); IF (! Parsedigits (STR, (Tokenlen <2? 1: 2), isthrowExp, Out Temphour) { Return (False); } IF (! ChecknewValue (Ref Result. Hour, Temphour, Ch, isthrowex) { Return (False); } Break; Case 'h': Tokenlen = Format.getRepeatcount (); IF (! Parsedigits (STR, (Tokenlen <2? 1: 2), isthrowExp, Out Temphour) { Return (False); } IF (! ChecknewValue (Ref Result. Hour, Temphour, Ch, isthrowex) { Return (False); } Break; Case 'M': Tokenlen = Format.getRepeatcount (); IF (! parsedigits (STR, (Tokenlen <2? 1: 2), isthrowExp, Out Tempminute)) { Return (False); } IF (! ChecknewValue (Ref Result.minute, Tempminute, Ch, isthrowex) { Return (False); } Break; Case 's': Tokenlen = Format.getRepeatcount (); IF (! parsedigits (STR, (Tokenlen <2? 1: 2), isthrowExp, Out Tempsecond) { Return (False); } IF (! ChecknewValue (ref result.second, tempsecond, ch, isthrowexp) { Return (False); } Break; Case 'f': Tokenlen = Format.getRepeatcount (); IF (tokenlease <= DATETIMEFORMAT.MAXSECONDSFRActionDIGITS) { IF (! Parsefractionexact (Str, Tokenlen, IsthrowExp, Ref Tempfraction) { Return (False); } IF (Result.fraction <0) { Result.fraction = tempfraction; } else { IF (TempFraction! = Result.fraction) {if (isthrowex) { Throw new argumentexception String.Format (Environment.getResourceString ("Format_repeAtDateTimePattern"), CH, "STR"); } else { Return (False); } } } } else { Return PaSeformater (IsthrowExp, "Format_BaddateTime"); } Break; Case 'T': // AM / PM Designator Tokenlen = Format.getRepeatcount (); IF (tokenlen == 1) { IF (! MatchabbreviatedTimemark (Str, DTFI, IsthrowExp, Ref Temptimemark) { Return (False); } } else { IF (! MatchTimemark (STR, DTFI, IsthrowExp, Ref Temptimemark) { Return (False); } } IF (! ChecknewValue (ref parseinfo.timemark, temptimemark, ch, isthrowex) { Return (False); } Break; Case 'Z': // Timezone Offset IF (parseinfo.fusetimezone) { Throw new argumentException (Environment.getResourceString ("argument_twotimezonespecifiers"), "Str"); } PARSEINFO.FUSETIMEZONE = True; Tokenlen = Format.getRepeatcount (); IF (! ParsetimezoneOffset (Str, Tokenlen, Isthrowexp, Ref PaSeinfo.timezoneoffset) { Return (False); } Break; Case 'Z': IF (parseinfo.fusetimezone) { Throw new argumentException (Environment.getResourceString ("argument_twotimezonespecifiers"), "Str"); } PARSEINFO.FUSETIMEZONE = True; ParseInfo.timezoneOffset = new timeespan (0); Str.Index ; IF (! gettimezonename (str)) { BCLDebug.Trace ("NLS", "DatetimeParse.DostrictParse (): 'Z' or 'GMT' Are Expected"); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Break; Case ':': IF (! Str.match (DTFI.TimeSeparator) { // a Time Separetor is expected. BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): ':' is expected); return (isthrowexp," format_baddatetime)); } Break; Case '/': IF (! str.match (dtfi.dateseparator) { // a date separator is expected. BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): Date Separetor is expected; Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } Break; Case '/ "': Case '/' ': Stringbuilder enquotedstring = new stringbuilder (); Try { // Use Parsequotestring So That We can Handle Escape Characters within the quoted string. Tokenlen = DateTimeFormat.Parsequotestring (Format.Value, Format.index, ENQUOTEDSTRING); } catCH (Exception) { IF (isthrowex) { Throw new formatexception (String.Format (Environment.GetResourceString ("Format_Badquote", CH); } else { Return (False); } } Format.index = tokenlen - 1; // Some Cultures Uses Space in The Quote String. E.G. Spanish Has Long Date Format AS: // "DDDD, DD 'DE' MMMM 'DE' YYYY". WHEN Inner Spaces Flag Is Set, We Should Skip Whitespaces IF The IS Space // in the quoted string. String quotedstr = enquotedstring.toString (); For (int i = 0; i IF (quotedstr [i] == '' && paseinfo.fallowinnerwhite) { Str.skipwhitespaces (); } else if (! Str.match (quotedstr [i])) { // can not find the matching quoted string. BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): Quote String Doesn't Match"); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } } Break; Case '%': // Skip this so we can get to the next pattern character.// @ Case Like "% D", "% y" // make Sure the next character is not a '%' again. IF (Format.index> = Format.Value.Length - 1 || Format.Value [Format.index 1] == '%') { BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): %% is not permitted"); Return (Parseformaterror (IsthrowExp, "Format_badFormatSpecifier)); } Break; Case '//': // escape character, "/ d". // Get the next character in format, and seeiff we can // Find a match in str. IF (Format.getNext ()) { IF (! str.match (format.getchar ())) { // can not find a match for the escaped character. BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): can not find a match for the escaped character"); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } } else { BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): // is at the end of the format string"); Return (Parseformaterror (IsthrowExp, "Format_badFormatSpecifier)); } Break; DEFAULT: IF (CH == ') { IF (Parseinfo.fallowinnerwhite) { // Skip Whitespaces IF Allowinnerwhite. // do nothing here. } else { IF (! Str.match (ch)) { // if the space does not match, and trailing space is allowed, WE DO // one more step to see reason the next format character can Lead to // Successful Parsing. // this is buy to deal with special case this a Empty string can match // a specific pattern. // The Example Here IS AF-ZA, WHICH HAS A TIME FORMAT LIKE "HH: MM: SS TT". HOWEVER, // ITS am Symbol IS "" (EMPTY STRING). If FallowTrailingWhite IS Used, And Time Is in // The am, we will trim the whitespaces at the end, Which Will Lead to a failure //when we are trying to match the space "tt". IF (ParseInfo.fallowTrailingWhite) { IF (Format.getNext ()) { IF (Parsebyformat (Str, Format, Parseinfo, DTFI, IsthrowExp, Result) { Return (TRUE); } } } Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } // Found a MACTH. } } else { IF (Format.MatchSpecifiedword (gmtname) { Format.index = (gmtname.length - 1); // Found GMT String in format. This means the datetime string // is in gmt timezone. PARSEINFO.FUSETIMEZONE = True; IF (! Str.match (gmtname)) { BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): GMT in format, but not in str"); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } } else if (! Str.match (ch)) { // CH is expected. Bcldebug.Trace ("NLS", "DateTimeParse.Dostrictpival (): '", ch, "' is expected); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } } Break; } // switch Return (TRUE); } // a Very Small Utility Method to Either Return False Or Throw Format Exception According To The Flag. Private Static Bool ParseFormater (Bool IsthrowException, String ResourceId) { IsthrowException { Throw new formatexception (Environment.getResourceString (ResourceId))); "ENVIRONMENT.GETRESOURSTRING (ResourceId); } Return (False); } / * ================================= DostrictParse ====================== ==================== ** Action: Do DateTime Parsing Using The Format in formatParam. ** Returns: The PaSed DateTime. ** arguments: ** Exceptions: ** ** Notes: ** WHEN The Following General Formats Are Used, InvariantIntinfo is Used in DTFI: ** 'r', 'r', 's'. ** WHEN The Following General Formats Are Used, The Time Is Assumed to Be in Universal Time. ** ** Limitations: ** ONLY GREGARIANCALENDAR IS Supported for Now. ** Only Support Gmt Timezone. ============================================================================================================================================================================================================= ============================= * / Private static bool dostrike ( String s, String FormatParam, DateTimeStyles Styles, DateTimeFormatinfo DTFI, Bool isthrowexp, Out datetime returnvalue) { Bool btimeonly = false; ReturnValue = new datetime (); ParsingInfo Parseinfo = New ParsingInfo (); Parseinfo.calendar = DTFI.CALENDAR; PARSEINFO.FALLOWINNNERWHITE = ((Styles & DateTimeStyles.allowinnerwhite)! = 0); Parseinfo.fallowTrailingWhite = (Styles & DateTimeStyles.allowTrailingWhite)! = 0); IF (FormatParam.length == 1) {FormatParam = ExpandPredEfinedFormat (FormatParam, Ref DTFI, PARSEINFO); } DatetimeResult results = new datetimereSult (); // RESET SESE VALUES TO NEGATIVE ONE SO THAT We Could Throw Exception // if we have pased Every item twice. Result.Hour = Result.minute = result.second = -1; __Dtstring format = new __dtstring (formatparam); __Dtstring str = new __dtstring (s); IF (ParseInfo.fallowTrailingWhite) { // Trim Trailing Spaces IF AllowTrailingWhite. Format.trimtail (); Format.Removetrailinginquotespaces (); Str.trimtail (); } IF (Styles & DateTimeStyles.allowleadingWhite)! = 0) { Format.skipwhitespaces (); Format.RemoveLeadingInquotespaces (); Str.skipwhitespaces (); } // // Scan Every Character in format and match the pattern in str. // While (Format.getNext ()) { // We Trim Inner Spaces Here, SO That We will not eat trailing space // allowtrailingwhite is not used. IF (Parseinfo.fallowinnerwhite) { Str.skipwhitespaces (); } IF (! Parsebyformat (Str, Format, ParseInfo, DTFI, IsthrowEx, Result) && ! isthroWexp) { Return (False); } } IF (str.index // There is the remaining character in str. BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): Still Characters in Str, Str.Index =", Str.Index); Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } IF (Parseinfo.fuseTwodigityear) { // a Two Digit Year Value IS Expected. Check if the pased year value is valid. IF (result.year> = 100) { BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): Invalid Value for Two-Digit Year"); Return (ParseFormaterror (isthrowexp, "format_baddatetime");} Result.year = parseinfo.calendar.tofourdigityear (Result.Year); } IF (ParseInfo.fuseHOUR12) { IF (ParseInfo.timemark == -1) { //HH is buy, but no am / pm designator isot specified. // Assume The Time IS AM. // Don n't throw exceptions in here becasue it is very confusing for mind. // i Always Got Confused Myself When I Use "HH: mm: SS" to Parse a Time String, // and parseexact () THROWS ON ME (Because I Didn't Use the 24-Hour Clock 'HH'). PARSEINFO.TIMEMARK = TM_AM; Bcldebug.Trace ("NLS", "DateTimeParse.DostrictPars (): HH IS Used, But no AM / PM Designator is Specified.") } IF (Result.Hour> 12) { // AM / PM IS Used, But The Value for HH IS TOO BIG. Bcldebug.Trace ("NLS", "DateTimeParse.DostrictParse (): AM / PM IS Used, But The Value for HH is Too Big.") Return (ParseFormaterror (isthrowexp, "format_baddatetime)); } IF (ParseInfo.timemark == TM_AM) { IF (result.hour == 12) { Result.Hour = 0; } } else { Result.Hour = (Result.Hour == 12)? 12: Result.Hour 12; } } // Check if the pararated String Only Contains Hour / Minute / Second VALUES. Btimeonly = (Result.Year == -1 && Result.mont == -1 && results == -1); CheckdefaultDatetime (Result, Ref PaSeinfo.calendar, Styles); Try { ReturnValue = parseInfo.calendar.todatetime (result.year, result.month, result.day, Result.Hour, Result.minute, Result.second, 0, Result.era); IF (Result.fraction> 0) { ReturnValue = RETURNVALUE.Addticks ((long) Math.Round (Result.fraction * Calendar.tickspersecond); } } catch (argumentoutofrangeexception) {return (partformaterror (isthrowexp, "format_dateoutofrange)); } catch (exception exp) { IF (isthrowex) { Throw Exp; } else { Return (False); } } // // Notenote: // We Have to Check Day of Week Before We Adjust to The Time Zone. // it is because the value of day of week may change after adjusting // To the time zone. // IF (ParseInfo.dayofweek! = -1) { // // Check if day of week is the correct. // IF (Parseinfo.dayofweek! = (int) parseinfo.calendar.getdayofweek (returnvalue)) { BCLDebug.Trace ("NLS", "DateTimeParse.DostrictParse (): DAY OF Week Is Not Correct"); Return (Parseformaterror (IsthrowExp, "Format_Baddayofweek); } } IF (parseinfo.fusetimezone) { IF (Styles & DateTimeStyles.adjustTouniversal)! = 0) { ReturnValue = AdjustTimezonetouniversal (ReturnValue, ParseInfo.timezoneoffset); } else { ReturnValue = AdjustTimezonetolocal (ReturnValue, ParseInfo.timezoneoffset, btimeonly); } } else if (parseinfo.fuseUniversaltime) { Try { ReturnValue = returnvalue.tolocaltime (); } catch (argumentoutofrangeexception) { Return (Parseformaterror (IsthrowExp, "Format_DateOutOfRange); } } Return (TRUE); } // this Method Should Never Be Called. ITS Sole Purpose Is To Shut Up The Compiler // Because It Warns About Private Fields That Are Never Used. Most of these Fields // is used in unmanaged code. #if _debug INTERNAL STRING [] nevercallthis () { BCLDebug.Assert (false, "nevercallthis"); String [] i = invariantMonthnames; I = INVARIANTABBREVMONTHNAMES; I = INVARIANTDAYNAMES; Return INVARIANTABBREVDAYNAMES; } #ENDIF } // // this is a string Parsing Helper Which Wraps a string Object.// IT Has A Index Property Which TRACKS // The current Parsing Pointer of The String. // [Serializable ()] Internal Class __dtstring { // // Value ProPERY: Stores the real string to be pased. // INTERNAL STRING VALUE; // // Index Property: Points to the Character That We Are Currently Parsing. // INTERNAL INDEX = -1; // the length of value string. INTERNAL INT LEN = 0; Private compareinfo m_info; INTERNAL __DTSTSTRING () { Value = ""; } INTERNAL __DTSTRING (String STR) { Value = STR; Len = Value.LENGTH; M_INFO = thread.currentthread.currentculture.compareinfo; } INTERNAL COMPAREINFO COMPAREINFO { Get { Return M_INFO; } } // // Advance the index. // Return True if index is not at the end of the string. // // Typical Usage: // while (Str.getNext ()) // { // char ch = str.getchar () //} INTERNAL BOOL getNext () { INDEX ; Return (INDEX } // // Return the Word Starting from the current index. // Index Will NOT BE Updated. // INTERNAL INT FINDENDOFCURRENTWORD () { INT i = index; While (i IF (value [i] == '|| value [i] ==', '|| value [i] ==' / '' || char.isdigit (value [i])) { Break; } i ; } Return I; } INTERNAL STRING peekcurrentword () { Int endindex = findendofcurrentword (); Return Value.substring (INDEX, (EndIndex - Index)); } INTERNAL BOOL MATCHSPECifiedWord (String Target) { Return MatchSpecifiedWord (Target, Target.Length Index); } INTERNAL BOOL MATCHSPECifiedWord (String Target, int endindex) { INT count = endindex - index; IF (count! = target.length) {Return False; } IF (INDEX Count> Len) { Return False; } Return (M_Info.compare (Value, Index, Count, Target, 0, Count, CompareOptions.ignorecase) == 0); } Internal Bool Startswith (String Target, Bool CHECKWORDBOUNDARY) { IF (value.length - index) { Return False; } IF (M_Info.compare (Value, Index, Target.length, Target, 0, Target.length)! = 0) { Return (False); } Checkwordboundary { INT nextcharindex = index target.length; NextCharindex IF (Char.isletter (Value [nextcharindex])) { Return (False); } } } Return (TRUE); } // // check to see if the string starting from index is a prefix of // Str. // if a match is found, true value is returned and index is updated to the next character to be pased. // OtherWise, INDEX IS Unchanged. // INTERNAL BOOL MATCH (String Str) { IF ( index> = len) { Return (False); } IF (str.length> (value.length - index)) { Return False; } IF (M_Info.compare (Value, Index, Str.length, Str, 0, Str.length, CompareOptions.ordinal) == 0) { // Update the index to the end of the matching string. // so the folcoming getnext () / match () Opertion Will Get // The next character to be parse. INDEX = (str.length - 1); Return (TRUE); } Return (False); } INTERNAL BOOL MATCH (CHAR CH) { IF ( index> = len) { Return (False); } IF (value [index] == CH) { Return (TRUE); } Return (False); } // // Trying to match an array of words. // Return Truehen One of the Word in the array matching with substring // Starting from the current index. // if the words array is null, also return true, assuming what there is a match.// INTERNAL BOOL MATCHWORDS (String [] Words) { IF (words == null) { Return (TRUE); } IF (index> = len) { Return (False); } For (INT i = 0; i IF (Words [i] .length <= (value.length - index) { IF (m_info.compare) Value, Index, Words [I] .length, Words [i], 0, Words [i] .length, compareOptions.ignorecase) == 0) { INDEX = Words [i] .length; Return (TRUE); } } } Return (False); } // // Get The Number of Repeat Character After The Current Character. // for a string "hh: mm: s" at index of 3. getrepeatcount () = 2, and index // Will Point to the second ':'. // INTERNAL INT GETREPEATCOUNT () { CHAR REPEATCHAR = VALUE [INDEX]; INT POS = INDEX 1; While ((POS POS ; } INT repeatCount = (POS - INDEX); // Update the index to the end of the repeated character. // so the folload getnext () OPEATION Will Get // The next character to be parse. INDEX = POS - 1; Return (repeatcount); } // RETURN FALSE WHEN End of string is encountered or a non-Digit Character is found. INTERNAL BOOL getNextdigit () { IF ( index> = len) { Return (False); } Return (DateTimeParse.IsDigit (Value [Index])); } // Return Null when end of string is encountered or a matching quote character is not found. // throws formatexception if the match quote Character Can NOT BE FOUND. INTERNAL STRING GETQUOTEDSTRING (Char quotecha) { // WHEN We Enter this Method, Index Points to The First Quote Character. INT OLDPOS = Index; While (Index } IF (index == len) { // if We Move Past Len, IT Means A Matching Quote Character Is Not Found. // 'ABC' 'ABC // 01234 0123 Return (NULL); } // WHEN We Leave this Method, Index Points to the matching quote character. Return (Value.Substring (Oldpos, INDEX - OLDPOS); } // // Get The Current Character. // INTERNAL CHAR getchar () { BCLDebug.Assert (INDEX> = 0 && Index Return (Value [index]); } // // Convert The Current Character to a Digit, And Return IT. // INTERNAL INT getDigit () { BCLDebug.Assert (INDEX> = 0 && Index Bcldebug.Assert (DateTimeParse.IsDigit (Value [Index]), "Isdigit (Value [Index])") Return (Value [index] - '0'); } // // Enjoy Eating White Spaces. // // Return False if End of string is encountered. // Internal void Skipwhitespaces () { // Look Ahead to See eti the next character // is a whitespace. While (INDEX 1 { CHAR CH = Value [Index 1]; IF (! char.swhitespace (ch)) { Return; } INDEX ; } Return; } // // Enjoy Eating White Spaces and Commas. // // Return False if End of string is encountered. // Internal Bool SkipwhitespaceComma () { CHAR CH; IF (index> = len) { Return (False); } IF (! char.iswhitespace (ch = value [index]) && ch! = ',') { Return (TRUE); } While ( Index { CH = value [index]; IF (! char.iswhitespace (ch) && ch! = ',') { Return (TRUE); } // Nothing here. } Return (False); } INTERNAL VOID TRIMTAIL () { INT i = LEN - 1; While (i> = 0 && char.iswhitespace (value [i])) { I-; } Value = value.substring (0, i 1); Len = Value.LENGTH; } // Trim The Trailing Spaces within a quoted string. // Call this after trimtail () is done. INTERNAL VOID REMOVETRAILINGINGINGINGINGINGINGINGINGINGINGINGINGINGINGINQUOTESPACES () { INT i = LEN - 1; IF (i <= 1) { Return; } CHAR CH = Value [i]; // Check if The last character is a quote. IF (CH == '/' '|| CH ==' / ") { IF (Char.IsWhitespace (Value [i-1])) { I-; While (i> = 1 && char.iswhitespace (value [i-1])) { I-; } Value = Value.Remove (I, Value.length - 1 - i); Len = Value.LENGTH; } } } // Trim The Leading Spaces within a quoted string. // Call this After the Leading Spaces Before Quoted String Are Trimmed. INTERNAL VOID RemoveLeadingInquotespaces () { IF (len <= 2) { Return; } INT i = 0; CHAR CH = Value [I]; // Check if The last character is a quote. IF (CH == '/' '|| CH ==' / ") { While ((i 1) i ; } IF (i! = 0) { Value = value.Remove (1, i); Len = Value.LENGTH; } } } } // // The buffer to store the paingsing token. // [Serializable ()] Internal Class datetimetoken { INTERNAL INT DTT; // Store the Token INTERNAL INT SuFFIX; // Store the cjk year / month / day suffix (if any) INTERNAL INT NUM; // Store The Number That We Are Parsing (if any) } // // The buffer to store temporary paarsing information. // [Serializable ()] Internal Class datetimerawinfo { INTERNAL INT [] NUM INTERNAL INT NUMCOUNT = 0; Internal Int Month = -1; INTERNAL INT YEAR = -1; INTERNAL INT dayofweek = -1; INTERNAL INT ERA = -1; INTERNAL INT TIMEMARK = -1; // Value Could BE -1, TM_AM ORTM_PM. INTERNAL DOUBLE FRACTION = -1; // // INTERNAL BOOL TIMEZONE = FALSE INTERNAL dateTimerawinfo () { Num = new int = {-1, -1, -1}; } } // // this Will Store The Result of The Parsing. And It Will Be Eventually // use to construct a datetime instance. // [Serializable ()] Internal Class DateTimeResult { INTERNAL INT YEAR = -1; INTERNAL INT MONTH = -1; INTERNAL INT DAY = -1; // // set time defualt to 00:00:00. // INTERNAL INT Hour = 0; INTERNAL INT minute = 0; INTERNAL INT SECOND = 0; INTERNAL DOUBLE FRACTION = -1; INTERNAL INT ERA = -1; INTERNAL BOOL TIMEZONEUSED = FALSE; INTERNAL TIMESPAN TIMEZONEOFFSET; INTERNAL CALENDAR CALENDAR; INTERNAL dateTimeResult () { } INTERNAL Virtual Void SetDate (int year, int month, int day) { Year = year; Month = month; Day = day; } } [Serializable] INTERNAL CLASS PARSINGINFO { INTERNAL CALENDAR CALENDAR; INTERNAL INT dayofweek = -1; INTERNAL INT TIMEMARK = -1; INTERNAL TIMESPAN TIMEZONEOFFSET = New Timespan (); INTERNAL BOOL FUSEUNIVERTIME = FALSE; INTERNAL BOOL FUSEHOUR12 = false; INTERNAL BOOL FUSETWodigityear = false INTERNAL BOOL FUSETIMEZONE = FALSE; INTERNAL BOOL FALLLOWINNNERWHITE = FALSE; INTERNAL BOOL FALLLOWTRAILINGWHITE = FALSE; INTERNAL PARSINFO () { } } }