/// <summary> /// ToValidValue - enforce max and min value of a nullable datetime /// SOURCE: https://stackoverflow.com/questions/3115678/converting-string-to-int-using-c-sharp /// </summary> /// <param name="dateToClean"></param> /// <returns></returns> public DateTime?ToValidValue(string dateToClean, DateTime dateMaxValue, DateTime dateMinValue, DateUtil.DataType dateDataType, DateUtil.Delim dateDelimiter, DateFormat dateFormat, bool expectTrailingAMorPM) { String[] strFormat = null; DateTime?tmpResult = null; try { if (String.IsNullOrWhiteSpace(dateToClean)) { tmpResult = null; //Always return null. Protects against a gigabyte of whitespace!!! } else { DateTime value; if (DateTime.Compare(dateMinValue.ToUniversalTime(), dateMaxValue.ToUniversalTime()) > 0) { throw new Exception("Invalid parameters: minimum date cannot be greater than the maximum date."); } if (dateDelimiter == DateUtil.Delim.ForwardSlash) { if (dateToClean.IndexOf(@"/") == -1) { throw new Exception("Invalid date: missing forward slash delimiter."); } } if (dateDelimiter == DateUtil.Delim.Dash) { if (dateToClean.IndexOf(@"-") == -1) { throw new Exception("Invalid date: missing dash delimiter."); } } if (dateDelimiter == DateUtil.Delim.Dot) { if (dateToClean.IndexOf(@".") == -1) { throw new Exception("Invalid date: missing dot delimiter."); } } //This includes Truncate to 33 chars (longest datetime format) dateToClean = SaniCore.NormalizeOrLimit.ToASCIIDateTimesOnly(dateToClean, dateDelimiter, dateDataType, expectTrailingAMorPM); #region Regex checks and strFormat assignment DateRegex dateRegexObj = new DateRegex(SaniCore.CompileRegex); //Perform specific Regex checks where possible after having already normalized the unicode string and reduced it to ASCII-like characters. if ((dateDataType == DateUtil.DataType.Date) && (dateFormat == DateFormat.US)) //Delimiter slash, dash, or dot { strFormat = new string[] { "M/d/yyyy", "MM/dd/yyyy" }; //Example 6/14/2020 if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy", "MM.dd.yyyy" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy", "MM-dd-yyyy" }; } dateRegexObj.PerformRegexForDateInUSFormat(dateToClean); } if ((dateDataType == DateUtil.DataType.Date) && (dateFormat == DateFormat.Euro)) //Delimiter slash, dash, or dot { strFormat = new string[] { "d/M/yyyy", "dd/MM/yyyy" }; //Example 28/02/2005 if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "d.M.yyyy", "dd.MM.yyyy" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "d-M-yyyy", "dd-MM-yyyy" }; } dateRegexObj.PerformRegexForDateInEuroFormat(dateToClean); } if ((dateDataType == DateUtil.DataType.Date) && (dateFormat == DateFormat.China)) //Delimiter slash, dash, or dot { strFormat = new string[] { "yyyy/M/d", "yyyy/MM/dd" }; //Example 2009/6/15 if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "yyyy.M.d", "yyyy.MM.dd" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "yyyy-M-d", "yyyy-MM-dd" }; } dateRegexObj.PerformRegexForDateInChineseFormat(dateToClean); } //Not the best regex here but we still have DateTime.ParseExact further below. if ((dateDataType == DateUtil.DataType.DateTime) && (dateFormat == DateFormat.US)) //Delimiter slash, dash, or dot { strFormat = null; //Example 02/18/1753 15:15 NOTE: capital H indicates 24-hour time. if (dateDelimiter == DateUtil.Delim.ForwardSlash) { strFormat = new string[] { "M/d/yyyy H:m", "MM/dd/yyyy H:m" }; } if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy H:m", "MM.dd.yyyy H:m" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy H:m", "MM-dd-yyyy H:m" }; } dateRegexObj.PerformRegexForDateTimeInUSFormat(dateToClean); } //Not the best regex here but we still have DateTime.ParseExact further below. if ((dateDataType == DateUtil.DataType.DateTimeWithSeconds) && (dateFormat == DateFormat.US) && !(dateDelimiter == DateUtil.Delim.UTCWithDelimiters || dateDelimiter == DateUtil.Delim.UTCWithoutDelimiters || dateDelimiter == DateUtil.Delim.UTCWithDelimitersAndZone)) //Delimiter slash, dash, or dot { strFormat = null; //Example 06/05/2009 15:15:33 or 06/05/2009 03:15:33 PM //Date in US format with single space H:m:ss and with optional AM or PM if (expectTrailingAMorPM == false) { if (dateDelimiter == DateUtil.Delim.ForwardSlash) //NOTE: capital H indicates 24-hour time. { strFormat = new string[] { "M/d/yyyy H:m:s", "MM/dd/yyyy H:m:s" }; } if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy H:m:s", "MM.dd.yyyy H:m:s" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy H:m:s", "MM-dd-yyyy H:m:s" }; } } else //expect AM or PM { if (dateDelimiter == DateUtil.Delim.ForwardSlash) //NOTE: capital h indicates regular time not military. { strFormat = new string[] { "M/d/yyyy h:m:s tt", "M/d/yyyy hh:mm:ss tt", "MM/dd/yyyy h:m:s tt", "MM/dd/yyyy hh:mm:ss tt" }; } if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy h:m:s tt", "M.d.yyyy hh:mm:ss tt", "MM.dd.yyyy h:m:s tt", "MM.dd.yyyy hh:mm:ss tt" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy h:m:s tt", "M-d-yyyy hh:mm:ss tt", "MM-dd-yyyy h:m:s tt", "MM-dd-yyyy hh:mm:ss tt" }; } } dateRegexObj.PerformRegexForDateTimeWithSecondsInUSFormat(dateToClean, expectTrailingAMorPM); } //Not the best regex here but we still have DateTime.ParseExact further below. if ((dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) && (dateFormat == DateFormat.US)) //Delimiter slash, dash, or dot { strFormat = null; //Example 06/05/2009 15:15:33.001 OR 06/05/2009 03:05:03.003 PM //Date in US format with single space H:m:ss.fff and with optional AM or PM //NOTE: M = single-digit month is formatted WITHOUT a leading zero. MM = single-digit month is formatted WITH a leading zero. // H = single-digit hour is formatted WITHOUT a leading zero. HH = single-digit hour is formatted WITH a leading zero. // d = single-digit day is formatted WITHOUT a leading zero. dd = single-digit day is formatted WITH a leading zero. if (expectTrailingAMorPM == false) { if (dateDelimiter == DateUtil.Delim.ForwardSlash) //NOTE: capital H indicates 24-hour time. { strFormat = new string[] { "M/d/yyyy H:m:s.fff", "MM/dd/yyyy H:m:s.fff", "MM/dd/yyyy HH:mm:ss.fff", "M/d/yyyy HH:m:s.fff" }; } if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy H:m:s.fff", "MM.dd.yyyy H:m:s.fff", "MM.dd.yyyy HH:mm:ss.fff", "M.d.yyyy HH:m:s.fff" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy H:m:s.fff", "MM-dd-yyyy H:m:s.fff", "MM-dd-yyyy HH:mm:ss.fff", "M-d-yyyy HH:m:s.fff" }; } } else //expect AM or PM { if (dateDelimiter == DateUtil.Delim.ForwardSlash) //NOTE: capital h indicates regular time not military. { strFormat = new string[] { "M/d/yyyy h:m:s.fff tt", "M/d/yyyy hh:mm:ss.fff tt", "MM/dd/yyyy h:m:s.fff tt", "MM/dd/yyyy hh:mm:ss.fff tt" }; } if (dateDelimiter == DateUtil.Delim.Dot) { strFormat = new string[] { "M.d.yyyy h:m:s.fff tt", "M.d.yyyy hh:mm:ss.fff tt", "MM.dd.yyyy h:m:s.fff tt", "MM.dd.yyyy hh:mm:ss.fff tt" }; } if (dateDelimiter == DateUtil.Delim.Dash) { strFormat = new string[] { "M-d-yyyy h:m:s.fff tt", "M-d-yyyy hh:mm:ss.fff tt", "MM-dd-yyyy h:m:s.fff tt", "MM-dd-yyyy hh:mm:ss.fff tt" }; } } dateRegexObj.PerformRegexForDateTimeWithMillisecondsInUSFormat(dateToClean, expectTrailingAMorPM); } if ((dateDataType == DateUtil.DataType.SQLServerDateTime) && (dateFormat == DateFormat.SQLServer)) //Delimiter slash, dash, or dot { strFormat = strFormat = new string[] { "yyyy-MM-dd H:m:s.fff", "yyyy-MM-dd HH:mm:ss.fff" }; //Example 2019-01-25 16:01:36.000 //Date in SQL Server format dateRegexObj.PerformRegexForDateTimeInSQLServerFormat(dateToClean); } if (dateDelimiter == DateUtil.Delim.UTCWithDelimiters) { //Example 2015-12-08T15:15:19 strFormat = new string[] { "yyyy-MM-dd'T'H:m:s", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'H:m:s'Z'", "yyyy-MM-dd'T'HH:mm:ss'Z'" }; dateRegexObj.PerformRegexForDateTimeWithSecondsAsUTCWithDelimiters(dateToClean); } if (dateDelimiter == DateUtil.Delim.UTCWithDelimitersAndZone) { //Example 2020-06-10T22:03:15-05:00 strFormat = new string[] { "yyyy-MM-dd'T'H:m:sK", "yyyy-MM-dd'T'HH:mm:ssK", "yyyy-MM-dd'T'H:m:sK'Z'", "yyyy-MM-dd'T'HH:mm:ssK'Z'" }; dateRegexObj.PerformRegexForDateTimeWithSecondsAsUTCWithDelimitersAndZone(dateToClean); } if (dateDelimiter == DateUtil.Delim.UTCWithoutDelimiters) { strFormat = new string[] { "yyyyMMdd'T'HHmmss", "yyyyMMdd'T'Hms", "yyyyMd'T'Hms" }; //Example 20151208T151519 //TODO: support yyyyMMdd'T'HHmmss.SSSZ with Milliseconds ?!? dateRegexObj.PerformRegexForDateTimeWithSecondsAsUTCWithoutDelimiters(dateToClean); } #endregion CultureInfo culture = null; if (dateFormat == DateFormat.US || dateFormat == DateFormat.SQLServer) //Example 6/15/2009 1:45:30 PM { culture = CultureInfo.CreateSpecificCulture("en-US"); } if (dateFormat == DateFormat.Euro) //Example 15/06/2009 13:45:30 { culture = CultureInfo.CreateSpecificCulture("es-ES"); //Spain } if (dateFormat == DateFormat.China) //Example 2009/6/15 13:45:30 { culture = CultureInfo.CreateSpecificCulture("zh-CN"); //China } try { value = DateTime.ParseExact(dateToClean, strFormat, culture, DateTimeStyles.None); } catch (FormatException) { throw new Exception("Unable to parse date."); } //SOURCE: https://blog.submain.com/4-common-datetime-mistakes-c-avoid/ if (DateTime.Compare(value.ToUniversalTime(), dateMinValue.ToUniversalTime()) < 0) //convert to utc prior to comparison { tmpResult = dateMinValue; //if minimum needs to be applied then apply it. } else //check for maximum { if (DateTime.Compare(value.ToUniversalTime(), dateMaxValue.ToUniversalTime()) > 0) { tmpResult = dateMaxValue; } else { tmpResult = value; } } } } catch (Exception ex) { SaniExceptionHandler.TrackOrThrowException(TruncateLength, SaniType, SaniCore, "MinMax: ", "Error datetime to valid MinMax value: ", dateToClean, ex); } return(tmpResult); }
/// <summary> /// ToValidValueUSDefault - enforce max and min value of a nullable datetime. Default max 1/1/2999 and min 1/1/1753 with US Format and no AM/PM. /// SOURCE: https://stackoverflow.com/questions/3115678/converting-string-to-int-using-c-sharp /// </summary> /// <param name="decimalToClean"></param> /// <returns></returns> public DateTime?ToValidValueUSDefault(string dateToClean, DateUtil.DataType dateDataType, DateUtil.Delim dateDelimiter) { return(SaniCore.MinMax.DateTimeType.ToValidValue(dateToClean, new DateTime(2999, 1, 1), new DateTime(1753, 1, 1), dateDataType, dateDelimiter, MinMax.DateFormat.US, false)); }
/// <summary> /// ToValidValueUSDefault - enforce max and min value of a nullable datetime. Default max 1/1/2999 and min 1/1/1753. /// SOURCE: https://stackoverflow.com/questions/3115678/converting-string-to-int-using-c-sharp /// </summary> /// <param name="decimalToClean"></param> /// <returns></returns> public DateTime?ToValidValueUSDefault(string dateToClean, DateUtil.DataType dateDataType, DateUtil.Delim dateDelimiter, DateFormat dateFormat, bool expectTrailingAMorPM) { return(SaniCore.MinMax.DateTimeType.ToValidValue(dateToClean, new DateTime(2999, 1, 1), new DateTime(1753, 1, 1), dateDataType, dateDelimiter, dateFormat, expectTrailingAMorPM)); }
/// <summary> /// Limit a Unicode string to just the limited subset of ASCII-compatible date times only, aka Latin numbers with date and time delimiters. /// </summary> /// <param name="strToClean"></param> /// <returns></returns> public string ToASCIIDateTimesOnly(string strToClean, DateUtil.Delim delimiter, DateUtil.DataType dateDataType, bool allowAMandPM) { string tmpResult = String.Empty; if (dateDataType == DateUtil.DataType.SQLServerDateTime) { dateDataType = DateUtil.DataType.DateTimeWithMilliseconds; //retain colon and space } try { if (string.IsNullOrWhiteSpace(strToClean)) { tmpResult = null; //Always return null. Protects against a gigabyte of whitespace!!! } else { tmpResult = SaniCore.Truncate.ToValidLength(strToClean, 33); tmpResult = tmpResult.Normalize(NormalizationForm.FormKC);//just to be extra safe //Example 12-8-2015 15:15 if (delimiter == DateUtil.Delim.Dash && !(delimiter == DateUtil.Delim.UTCWithDelimiters || delimiter == DateUtil.Delim.UTCWithoutDelimiters || delimiter == DateUtil.Delim.UTCWithDelimitersAndZone)) { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || //Latin numbers ((int)c == 45) || //45 = dash (allowAMandPM ? (((int)c == 65) || ((int)c == 77) || ((int)c == 80) || ((int)c == 97) || ((int)c == 109) || ((int)c == 112)) : false) || //65 = A , 77 = M, 80 = P, 97 = a, 109 = m, 112 = p ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 32) : false) || //32 = space ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 58) : false) || //58 = colon ((dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 46) : false) //46 = dot )).ToArray())); } //Example 12.8.2015 15:15 if (delimiter == DateUtil.Delim.Dot && !(delimiter == DateUtil.Delim.UTCWithDelimiters || delimiter == DateUtil.Delim.UTCWithoutDelimiters || delimiter == DateUtil.Delim.UTCWithDelimitersAndZone)) { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || ((int)c == 46) || //46 = dot (allowAMandPM ? (((int)c == 65) || ((int)c == 77) || ((int)c == 80) || ((int)c == 97) || ((int)c == 109) || ((int)c == 112)) : false) || //65 = A , 77 = M, 80 = P, 97 = a, 109 = m, 112 = p ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 32) : false) || //32 = space ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 58) : false) //58 = colon )).ToArray())); } //Example 12/8/2015 15:15 if (delimiter == DateUtil.Delim.ForwardSlash && !(delimiter == DateUtil.Delim.UTCWithDelimiters || delimiter == DateUtil.Delim.UTCWithoutDelimiters || delimiter == DateUtil.Delim.UTCWithDelimitersAndZone)) { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || ((int)c == 47) || //47 = forward slash (allowAMandPM ? (((int)c == 65) || ((int)c == 77) || ((int)c == 80) || ((int)c == 97) || ((int)c == 109) || ((int)c == 112)) : false) || //65 = A , 77 = M, 80 = P, 97 = a, 109 = m, 112 = p ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 32) : false) || //32 = space ((dateDataType == DateUtil.DataType.DateTime || dateDataType == DateUtil.DataType.DateTimeWithSeconds || dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 58) : false) || //58 = colon ((dateDataType == DateUtil.DataType.DateTimeWithMilliseconds) ? ((int)c == 46) : false) //46 = dot )).ToArray())); } if (delimiter == DateUtil.Delim.UTCWithoutDelimiters) //yyyyMMdd'T'HHmmss.SSSZ { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || //Latin numbers ((int)c == 46) || //46 = dot (((int)c == 84) || ((int)c == 90) || ((int)c == 32)) //84 = T, 90 = Z, 32 = space )).ToArray())); } if (delimiter == DateUtil.Delim.UTCWithDelimitersAndZone) //yyyy-MM-dd'T'HH:mm:ssK EXAMPLE: 2020-06-10T22:03:15-05:00 { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || //Latin numbers ((int)c == 45) || //45 = dash and minus sign ((int)c == 58) || //58 = colon ((int)c == 43) || //43 = plus sign (((int)c == 84) || ((int)c == 90) || ((int)c == 32)) //84 = T, 90 = Z, 32 = space )).ToArray())); } if (delimiter == DateUtil.Delim.UTCWithDelimiters) //yyyy-MM-dd'T'HH:mm:ss.SSSZZ EXAMPLE: 2014-08-29T06:44:03Z { tmpResult = (new string(tmpResult.ToCharArray().Where(c => ((48 <= (int)c && (int)c <= 57) || //Latin numbers ((int)c == 45) || //45 = dash ((int)c == 58) || //58 = colon (((int)c == 84) || ((int)c == 90) || ((int)c == 32)) //84 = T, 90 = Z, 32 = space )).ToArray())); } //TODO: support 1995-07-14T13:05:00.0000000-03:00 ?!? } } catch (Exception ex) { SaniExceptionHandler.TrackOrThrowException(TruncateLength, SaniType, SaniCore, "NormalizeOrLimit: ", "Error limiting unicode to ASCII DateTimes Only: ", strToClean, ex); } return(tmpResult); }