VistA-Scheduling/cs/bsdx0200GUISourceCode/FMDateTime.cs

1187 lines
29 KiB
C#

/*
* Copyright (C) 2004-2009 Medsphere Systems Corporation
* All rights reserved.
*
* This source code contains the intellectual property
* of its copyright holder(s), and is made available
* under a license. If you do not know the terms of
* the license, please stop and do not read further.
*
* Please read LICENSES for detailed information about
* the license this source code file is available under.
* Questions should be directed to legal@medsphere.com
*
*
* Licensed under AGPL v3.
*
*
Modified by Sam Habiel to use in Scheduling GUI. Modified class licensed under LGPL.
*/
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace IndianHealthService.ClinicalScheduling
{
public delegate void FMDateTimeHandler (FMDateTime time);
public delegate string FMDateStringHandler (string s);
[Serializable]
[System.Xml.Serialization.SoapType (Namespace="http://ws.medsphere.com")]
public class FMDateTime : IComparable, ICloneable
{
/* public properties */
[System.Xml.Serialization.SoapAttribute (Namespace="http://ws.medsphere.com")]
public DateTime DateTime {
get {
switch (Precision) {
case FMDateTimePrecision.YearOnly:
return new DateTime (Year, 1, 1);
case FMDateTimePrecision.YearMonthOnly:
return new DateTime (Year, Month, 1);
case FMDateTimePrecision.DateOnly:
return new DateTime (Year, Month, Day);
default:
return new DateTime (Year, Month, Day, Hour, Minute, Second);
}
}
}
public int Year {
get { return year; }
set { year = value; }
}
public int Month {
get { return month; }
set { month = value; }
}
public int Day {
get { return day; }
set { day = value; }
}
public int Hour {
get { return hour; }
set {
if (value > 24) {
throw new ArgumentException ("Hour cannot be greater than 24");
}
hour = value % 24;
}
}
public int Minute {
get { return minute; }
set {
if (value > 59) {
throw new ArgumentException ("Minute cannot be greater than 59");
}
minute = value;
}
}
public int Second {
get { return second; }
set {
if (value > 59) {
throw new ArgumentException ("Second cannot be greater than 59");
}
second = value;
}
}
public static FMDateTime ServerNow {
get { return FMDateTime.Create (DateTime.Now - server_offset); }
}
public string RelativeFMDateString {
get { return Raw; }
}
public string ShortHandString {
get {
if (Raw != null && date_format.IsMatch (Raw.Replace (" ", ""))) {
return Raw;
}
return ToString ();
}
}
public FMDateTime AtZero {
get {
FMDateTime d = (FMDateTime)this.Clone ();
d.Precision = FMDateTimePrecision.DateAndTime;
d.Hour = 0;
d.Minute = 0;
d.Second = 0;
return d;
}
}
public FMDateTime AtMidnight {
get {
FMDateTime d = (FMDateTime)this.Clone ();
d.Precision = FMDateTimePrecision.DateAndTime;
d.Hour = 23;
d.Minute = 59;
d.Second = 59;
return d;
}
}
public FMDateTime DateOnly {
get {
FMDateTime d = (FMDateTime)this.Clone ();
if (Precision != FMDateTimePrecision.DateAndTime) {
return d;
}
d.Precision = FMDateTimePrecision.DateOnly;
d.Hour = d.Minute = d.Second = 0;
return d;
}
}
public string FMDateString {
get {
switch (Precision) {
case FMDateTimePrecision.YearOnly:
return String.Format ("{0:000}", year - 1700);
case FMDateTimePrecision.YearMonthOnly:
return String.Format ("{0:000}{1:00}", year - 1700, month);
case FMDateTimePrecision.DateOnly:
return String.Format ("{0:000}{1:00}{2:00}", year - 1700, month, day);
case FMDateTimePrecision.DateAndTime:
default:
if (second > 0) {
return String.Format ("{0:000}{1:00}{2:00}.{3:00}{4:00}{5:00}",
year - 1700, month, day, hour, minute, second);
} else {
return String.Format ("{0:000}{1:00}{2:00}.{3:00}{4:00}",
year - 1700, month, day, hour, minute);
}
}
}
}
/* public fields */
public FMDateTimePrecision Precision;
[NonSerialized]
public static FMDateStringHandler ValidationMethod;
public static FMDateTime MinValue; // 1/1/1700
public static FMDateTime MaxValue; // 12/31/2699 12:59 PM
/* public methods */
static FMDateTime ()
{
// This is the equivalent of the FMDateTime string '0000101'
// We do this manually to avoid parsing overhead here.
MinValue = new FMDateTime (); // 1/1/1700
MinValue.Hour = MinValue.Minute = MinValue.Second = 0;
MinValue.Day = MinValue.Month = 1;
MinValue.Year = 1700;
MinValue.Precision = FMDateTimePrecision.DateOnly;
// This is the equivalent of the FMDateTime string '9991231.235959'
// We do this manually to avoid parsing overhead here.
MaxValue = new FMDateTime (); // 12/31/2699 12:59 PM
MaxValue.Hour = 23;
MaxValue.Minute = 59;
MaxValue.Second = 59;
MaxValue.Day = 31;
MaxValue.Month = 12;
MaxValue.Year = 2699;
MaxValue.Precision = FMDateTimePrecision.DateAndTime;
}
public FMDateTime ()
{
}
public FMDateTime(DateTime d)
: this(d, FMDateTimePrecision.DateAndTime)
{
}
public FMDateTime(DateTime d, FMDateTimePrecision precision)
{
if (d > MaxValue.DateTime || d < MinValue.DateTime)
return;
this.Precision = precision;
this.Year = d.Year;
this.Month = d.Month;
this.Day = d.Day;
this.Hour = d.Hour;
this.Minute = d.Minute;
this.Second = d.Second;
}
public static FMDateTime Create (DateTime d, FMDateTimePrecision precision)
{
if (d > MaxValue.DateTime || d < MinValue.DateTime) {
return null;
}
FMDateTime f = new FMDateTime ();
f.Precision = precision;
f.Year = d.Year;
f.Month = d.Month;
f.Day = d.Day;
f.Hour = d.Hour;
f.Minute = d.Minute;
f.Second = d.Second;
return f;
}
public static FMDateTime Create (DateTime d)
{
return Create (d, FMDateTimePrecision.DateAndTime);
}
/*public static string FMDate (this DateTime d)
{
return Create(d, FMDateTimePrecision.DateAndTime).FMDateString;
}*/
public static FMDateTime Parse (string str)
{
return Parse (str, FMDateTime.ValidationMethod);
}
public static FMDateTime Parse (string str,
FMDateStringHandler validation_method)
{
if (validation_method == null) {
throw new ArgumentNullException ("You must pass in a valid validation_method");
}
if (str == null) {
return null;
}
FMDateTime date = null;
str = str.Trim ();
if (str == "0" || str == String.Empty) {
return null;
}
if (str.IndexOf ("@") != -1) {
date = new FMDateTime ();
// string has a date and time part
string[] tokens = str.Split (new char[] {'@'}, 2);
if (ParseDatePart (tokens[0], ref date)
|| ParseUsingDateTime (tokens[0], ref date)
|| (validation_method != null
&& ParseInternalFormat (validation_method (tokens[0]), ref date))) {
// Its been decided that if you have an
// invalid time part, that the entire
// string is invalid
if (!ParseTimePart (tokens[1], true, ref date)) {
return null;
}
date.Raw = str;
return date;
} else {
// Account for @0600
date = FMDateTime.ServerNow;
if (!ParseTimePart (tokens[1], true, ref date)) {
return null;
}
return date;
}
}
if (ParseDatePart (str, ref date)) {
date.Raw = str;
return date;
}
if (ParseTimePart (str, false, ref date)) {
FMDateTime now = ServerNow;
date.Year = now.Year;
date.Month = now.Month;
date.Day = now.Day;
return date;
}
if (ParseUsingDateTime (str, ref date)) {
return date;
}
if (ParseInternalFormat (str, ref date)) {
return date;
}
if (validation_method != null) {
if (ParseInternalFormat (validation_method (str), ref date)) {
return date;
}
return null;
}
if (date == null) {
Console.WriteLine ("WARNING: FMDateTime failed parsing '{0}'", str);
}
return date;
}
public static FMDateTime Parse (string str, FMDateTimePrecision precision)
{
FMDateTime date = FMDateTime.Parse (str);
if (date != null) {
date.Precision = precision;
}
return date;
}
public void PopulateFrom12HrTime (int hour, int minute, int second, bool is_pm)
{
if (hour < 12 && is_pm) {
hour += 12;
} else if (hour == 12 && !is_pm) {
hour = 0;
}
Hour = hour;
Minute = minute;
Second = second;
}
public bool IsFutureDate
{
get {
return (CompareTo (Precision, FMDateTime.ServerNow, FMDateTime.ServerNow.Precision) > 0);
}
}
public bool IsPastDate
{
get {
return (CompareTo (Precision, FMDateTime.ServerNow, FMDateTime.ServerNow.Precision) < 0);
}
}
public static void UpdateServerNow (FMDateTime server_now)
{
if (server_now != null) {
server_offset = (DateTime.Now - server_now.DateTime);
}
}
public override string ToString ()
{
switch (Precision) {
case FMDateTimePrecision.YearOnly:
return DateTime.ToString ("yyyy");
case FMDateTimePrecision.YearMonthOnly:
return DateTime.ToString ("Y");
case FMDateTimePrecision.DateOnly:
return DateTime.ToString ("d");
default:
return DateTime.ToString ("G");
}
}
public static string ToString (FMDateTime date)
{
if (date != null) {
return date.ToString ();
}
return String.Empty;
}
public string ToString (string format)
{
return DateTime.ToString (format);
}
public static string ToString (FMDateTime date, string format)
{
if (date != null) {
return date.ToString (format);
}
return String.Empty;
}
public string ToDateString ()
{
return DateTime.ToString ("d");
}
public static string ToDateString (FMDateTime date)
{
if (date != null) {
return date.ToDateString ();
}
return String.Empty;
}
public string ToTimeString ()
{
return DateTime.ToString ("t");
}
public static string ToTimeString (FMDateTime date)
{
if (date != null) {
return date.ToTimeString ();
}
return String.Empty;
}
public static string ToFMDateString (FMDateTime date)
{
if (date != null) {
return date.FMDateString;
}
return String.Empty;
}
/**
* Compares this FMDateTime instance with given FMDateTimePrecision this_p to dt
* using the given FMDateTimePrecision p.
*/
public int CompareTo (FMDateTimePrecision this_p, FMDateTime dt, FMDateTimePrecision dt_p)
{
int r;
FMDateTimePrecision save_this_p = Precision;
FMDateTimePrecision save_dt_p = dt.Precision;
Precision = this_p;
dt.Precision = dt_p;
r = DateTime.CompareTo (dt.DateTime);
Precision = save_this_p;
dt.Precision = save_dt_p;
return r;
}
/**
* Implementation of IComparable interface.
*/
public int CompareTo (object o)
{
if (o == null) {
return 1;
} else if (o is FMDateTime) {
FMDateTime f = (FMDateTime)o;
int r = DateTime.CompareTo (f.DateTime);
if (r == 0) {
// special cases of DateTime comparison:
// 1900 and January,1900 and 01/01/1900 are all
// represented as 01/01/1900
// TODAY@0 and TODAY are both represented as TODAY@0
// these are the cases where precision has the last word
if (Precision < f.Precision) {
r = -1;
} else if (Precision > f.Precision) {
r = 1;
}
}
return r;
} else if (o is DateTime) {
DateTime d = (DateTime)o;
return DateTime.CompareTo (d);
}
throw new ArgumentException ("Value is not a DateTime or FMDateTime.");
}
public static int Compare (FMDateTime a, FMDateTime b)
{
if (a == null && b == null) {
return 0;
} else if (a != null && b != null) {
return a.CompareTo (b);
/* We sort the non-null item before the null one for the mixed case */
} else if (a == null) {
return -1;
} else if (b == null) {
return 1;
}
// This code path really has no way of being hit.
return 0;
}
public override bool Equals (object o)
{
if (o == null) {
return false;
} else if (o is FMDateTime) {
FMDateTime f = (FMDateTime)o;
if (f.Precision != Precision) {
return false;
}
switch (Precision) {
case FMDateTimePrecision.YearOnly:
return Year == f.Year;
case FMDateTimePrecision.YearMonthOnly:
return Year == f.Year && Month == f.Month;
case FMDateTimePrecision.DateOnly:
return Year == f.Year && Month == f.Month && Day == f.Day;
case FMDateTimePrecision.DateAndTime:
default:
return Year == f.Year && Month == f.Month && Day == f.Day
&& Hour == f.Hour && Minute == f.Minute && Second == f.Second;
}
}
throw new ArgumentException ("Value is not a FMDateTime.");
}
public override int GetHashCode ()
{
return (int)Precision + year + month + day + hour + minute + second;
}
/**
* This gets a hash code based upon the FMDateTime precision, so that
* an object can be stored based on DateOnly, for example, and if you
* try to look it up later using a different FMDateTime object that
* has the same date, but may have different time. Seconds are
* intentionally never factored into this hash code, even for DateAndTime
* cases. If you want to factor in seconds as well, just use GetHashCode().
*
* @return An integer hash code.
*/
public int GetPrecisionAwareHashCode ()
{
int hash_code = (int)Precision;
switch (Precision)
{
case FMDateTimePrecision.YearOnly:
hash_code += year;
break;
case FMDateTimePrecision.YearMonthOnly:
hash_code += year;
hash_code += month;
break;
case FMDateTimePrecision.DateOnly:
hash_code += year;
hash_code += month;
hash_code += day;
break;
case FMDateTimePrecision.DateAndTime:
default:
hash_code += year;
hash_code += month;
hash_code += day;
hash_code += hour;
hash_code += minute;
break;
}
return hash_code;
}
public object Clone ()
{
FMDateTime d = new FMDateTime ();
d.Precision = Precision;
d.Year = year;
d.Month = month;
d.Day = day;
d.Hour = hour;
d.Minute = minute;
d.Second = second;
return d;
}
public static bool operator == (FMDateTime a, FMDateTime b)
{
object obj_a = (object)a;
object obj_b = (object)b;
if (obj_a == null && obj_b == null) {
return true;
} else if (obj_a != null && obj_b != null) {
return a.Equals (b);
} else {
return false;
}
}
public static bool operator != (FMDateTime a, FMDateTime b)
{
return !(a == b);
}
public static bool operator > (FMDateTime a, FMDateTime b)
{
if (a == null) {
throw new ArgumentException ("Left hand argument to comparison cannot be null.");
}
return (a.CompareTo (b) > 0);
}
public static bool operator >= (FMDateTime a, FMDateTime b)
{
if (a == null) {
throw new ArgumentException ("Left hand argument to comparison cannot be null.");
}
return (a.CompareTo (b) >= 0);
}
public static bool operator < (FMDateTime a, FMDateTime b)
{
if (a == null) {
throw new ArgumentException ("Left hand argument to comparison cannot be null.");
}
return (a.CompareTo (b) < 0);
}
public static bool operator <= (FMDateTime a, FMDateTime b)
{
if (a == null) {
throw new ArgumentException ("Left hand argument to comparison cannot be null.");
}
return (a.CompareTo (b) <= 0);
}
public static implicit operator FMDateTime (DateTime d)
{
return FMDateTime.Create (d);
}
/* protected properties */
protected string Raw;
/* private properties */
private static Calendar CurrentCalendar {
get { return CultureInfo.CurrentCulture.Calendar; }
}
/* private fields */
private int year, month, day, hour, minute, second;
// We do this here so we can lazy load the compiled regexes.
private static Regex internal_format {
get {
if (priv_internal_format == null) {
priv_internal_format = new Regex (@"^(\d{3})(\d{2})?(\d{2})?(\.(\d{1,2})?(\d{1,2})?(\d{1,2})?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
return priv_internal_format;
}
}
private static Regex priv_internal_format;
private static Regex date_format {
get {
if (priv_date_format == null) {
priv_date_format = new Regex (@"^(t(oday)?|n(ow)?)(([+-])(\d+)(w)?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
return priv_date_format;
}
}
private static Regex priv_date_format;
private static Regex time_format {
get {
if (priv_time_format == null) {
priv_time_format = new Regex (@"^(\d{1,2})(:(\d{2}))?(:(\d{2}))?(\s*)(AM|PM)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
return priv_time_format;
}
}
private static Regex priv_time_format;
private static TimeSpan server_offset = new TimeSpan ();
/* private methods */
private static bool ParseDatePart (string str, ref FMDateTime date)
{
FMDateTime orig_date = date;
string clean = str.Replace (" ", "");
// see if it matches (t|today|now) +/- some number
if (!date_format.IsMatch (clean)) {
return false;
}
Match m = date_format.Match (clean);
if (m != null) {
if (date == null) {
date = new FMDateTime ();
}
// be safe about dates like T-99999999
try {
int modifier = 0;
if (m.Groups[5].Value != String.Empty) {
int sign_bit = 1;
if (m.Groups[5].Value == "-") {
sign_bit = -1;
}
// Convert will bomb if the modifier
// won't fit into an int (it's invalid
// anyway)
modifier = sign_bit * Convert.ToInt32 (m.Groups[6].Value);
}
DateTime dt = ServerNow.DateTime;
if (m.Groups[7].Value.ToLower () == "w") {
dt = CurrentCalendar.AddWeeks (dt, modifier);
} else {
dt = CurrentCalendar.AddDays (dt, modifier);
}
date.Day = dt.Day;
date.Month = dt.Month;
date.Year = dt.Year;
if (m.Groups[1].Value.ToLower ().StartsWith ("n")) {
date.Precision = FMDateTimePrecision.DateAndTime;
date.Hour = dt.Hour;
date.Minute = dt.Minute;
date.Second = dt.Second;
} else {
date.Precision = FMDateTimePrecision.DateOnly;
date.Hour = date.Minute = date.Second = 0;
}
} catch {
date = orig_date;
return false;
}
return true;
}
date = orig_date;
return false;
}
private static bool ParseTimePart (string str, bool try_int_parse, ref FMDateTime date)
{
int time;
str = str.ToUpper ();
if (str == "NOON") {
if (date == null) {
date = new FMDateTime ();
}
date.Hour = 12;
date.Minute = date.Second = 0;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} else if (str == "MID" || str == "MIDNIGHT") {
if (date == null) {
date = new FMDateTime ();
}
date.Hour = 23;
date.Minute = 59;
date.Second = 59;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} else if (time_format.IsMatch (str)) {
Match m = time_format.Match (str);
if (m == null) {
return false;
}
int hour, minute, second;
int.TryParse(m.Groups[1].Value, out hour);
int.TryParse(m.Groups[3].Value, out minute);
int.TryParse(m.Groups[5].Value, out second);
if (m.Groups[7].Value == "PM") {
hour += 12;
}
if (hour == 24 && minute == 0 && second == 0) {
hour = 23;
minute = second = 59;
}
if (!ValidateTime (hour, minute, second)) {
return false;
}
if (date == null) {
date = new FMDateTime ();
}
date.Hour = hour;
date.Minute = minute;
date.Second = second;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} else if (try_int_parse && int.TryParse(str, out time)) {
int hour, minute, second;
if (time <= 2359) {
hour = time / 100;
minute = time - (hour * 100);
second = 0;
} else if (time <= 235959) {
hour = time / 10000;
minute = (time - (hour * 10000)) / 100;
second = time - ((time / 100) * 100);
} else {
return false;
}
if (hour == 24 && minute == 0 && second == 0) {
hour = 23;
minute = second = 59;
}
if (!ValidateTime (hour, minute, second)) {
return false;
}
if (date == null) {
date = new FMDateTime ();
}
date.Hour = hour;
date.Minute = minute;
date.Second = second;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
}
return false;
}
private static bool ParseUsingDateTime (string str, ref FMDateTime date)
{
// we need to use DateTime.Parse to allow
// roundtripping of our ToString () methods
// LAMESPEC: There isn't any way to find out whether a
// DateTime contains a time part, or just a date part
// after calling Parse, so we have to specifically call
// ParseExact on a few known formats
try {
string[] formats = new string[] {
"yyyy"
};
DateTime d = DateTime.ParseExact (str, formats, null,
DateTimeStyles.AllowWhiteSpaces);
if (date == null) {
date = new FMDateTime ();
}
date.Year = d.Year;
date.Precision = FMDateTimePrecision.YearOnly;
return true;
} catch (FormatException) {
}
try {
string[] formats = new string[] {
"Y"
};
DateTime d = DateTime.ParseExact (str, formats, null,
DateTimeStyles.AllowWhiteSpaces);
if (date == null) {
date = new FMDateTime ();
}
date.Year = d.Year;
date.Month = d.Month;
date.Precision = FMDateTimePrecision.YearMonthOnly;
return true;
} catch (FormatException) {
}
try {
string[] formats = new string[] {
"d", "MM/dd/yy"
};
DateTime d = DateTime.ParseExact (str, formats, null,
DateTimeStyles.AllowWhiteSpaces);
if (date == null) {
date = new FMDateTime ();
}
date.Year = d.Year;
date.Month = d.Month;
date.Day = d.Day;
date.Precision = FMDateTimePrecision.DateOnly;
return true;
} catch (FormatException) {
}
try {
string[] formats = new string[] {
"g", "G", "MM/dd/yy hh:mm tt",
"MM/dd/yy h:mm tt"
};
DateTime d = DateTime.ParseExact (str, formats, null,
DateTimeStyles.AllowWhiteSpaces);
if (date == null) {
date = new FMDateTime ();
}
date.Year = d.Year;
date.Month = d.Month;
date.Day = d.Day;
date.Hour = d.Hour;
date.Minute = d.Minute;
date.Second = d.Second;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} catch (FormatException) {
}
/* XXX: Disabiling this for now, since it sucks incredibly
// first try parsing date & time
try {
string[] date_time_formats = new string[] {
"dddd*, MMMM* dd, yyyy HH*:mm* tt*", "f",
"dddd*, MMMM* dd, yyyy HH*:mm*:ss* tt*", "F",
"MM/dd/yyyy HH*:mm* tt*", "g",
"MM/dd/yyyy HH*:mm*:ss* tt*", "G",
"dddd*, MMMM* dd, yyyy HH*:mm*:ss* tt*", "U"
};
DateTime d = DateTime.ParseExact (str, date_time_formats, null,
DateTimeStyles.AllowWhiteSpaces);
date.Year = d.Year;
date.Month = d.Month;
date.Day = d.Day;
date.Hour = d.Hour;
date.Minute = d.Minute;
date.Second = d.Second;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} catch { }
// fall back on just parsing a date
try {
string[] date_formats = new string[] {
"d", "D", "m", "M", "y", "Y"
};
DateTime d = DateTime.ParseExact (str, date_formats, null,
DateTimeStyles.AllowWhiteSpaces);
date.Year = d.Year;
date.Month = d.Month;
date.Day = d.Day;
date.Precision = FMDateTimePrecision.DateOnly;
return true;
} catch { }
// if nothing else, try a couple of time formats
try {
string[] time_formats = new string[] {
"HH*:mm* tt*", "t",
"HH*:mm*:ss* tt*", "T"
};
DateTime d = DateTime.ParseExact (str, time_formats, null,
DateTimeStyles.AllowWhiteSpaces);
date = FMDateTime.ServerNow;
date.Hour = d.Hour;
date.Minute = d.Minute;
date.Second = d.Second;
date.Precision = FMDateTimePrecision.DateAndTime;
return true;
} catch { }
*/
return false;
}
private static bool ParseInternalFormat (string str, ref FMDateTime date)
{
FMDateTime orig_date = date;
if (internal_format.IsMatch (str)) {
Match m = internal_format.Match (str);
if (m != null && m.Groups.Count == 8) {
int year, month, day, hour, minute, second;
int.TryParse(m.Groups[1].Value, out year);
year += 1700;
int.TryParse(m.Groups[2].Value, out month);
int.TryParse(m.Groups[3].Value, out day);
int.TryParse(m.Groups[5].Value, out hour);
int.TryParse(m.Groups[6].Value, out minute);
int.TryParse(m.Groups[7].Value, out second);
// 1 digit hours apparently have just
// had the trailing 0 dropped. Go figure.
if (m.Groups[5].Value.Length == 1) {
hour *= 10;
}
// 1 digit minutes do too
if (m.Groups[6].Value.Length == 1) {
minute *= 10;
}
// 1 digit seconds aren't to be left out
if (m.Groups[7].Value.Length == 1) {
second *= 10;
}
if (!ValidateYear (year)) {
return false;
}
if (date == null) {
date = new FMDateTime ();
}
date.Year = year;
date.Precision = FMDateTimePrecision.YearOnly;
if (m.Groups[5].Value != String.Empty
&& month > 0 && day > 0 && hour > 0) {
if (!ValidateDate (year, month, day)
|| !ValidateTime (hour, minute, second)) {
date = orig_date;
return false;
}
date.Month = month;
date.Day = day;
date.Hour = hour;
date.Minute = minute;
date.Second = second;
date.Precision = FMDateTimePrecision.DateAndTime;
} else if (m.Groups[3].Value != String.Empty
&& month > 0 && day > 0) {
if (!ValidateDate (year, month, day)) {
date = orig_date;
return false;
}
date.Month = month;
date.Day = day;
date.Precision = FMDateTimePrecision.DateOnly;
} else if (m.Groups[2].Value != String.Empty
&& month > 0) {
if (!ValidateYearMonth (year, month)) {
date = orig_date;
return false;
}
date.Month = month;
date.Precision = FMDateTimePrecision.YearMonthOnly;
}
return true;
}
}
return false;
}
private static bool ValidateYear (int year)
{
// Sadly, we can't use MaxValue and MinValue due to
// this function being used in the
// parsing and initialization of those values
if (year < 1700 || year > 2699) {
return false;
}
return true;
}
private static bool ValidateYearMonth (int year, int month)
{
if (!ValidateYear (year)) {
return false;
}
int num_months = CurrentCalendar.GetMonthsInYear (year);
if (month < 1 || month > num_months) {
return false;
}
return true;
}
private static bool ValidateDate (int year, int month, int day)
{
if (!ValidateYearMonth (year, month)) {
return false;
}
int num_days = CurrentCalendar.GetDaysInMonth (year, month);
if (day < 1 || day > num_days) {
return false;
}
return true;
}
private static bool ValidateTime (int hour, int minute, int second)
{
if (hour < 0 || hour > 24) {
return false;
}
if (minute < 0 || minute > 59) {
return false;
}
if (second < 0 || second > 59) {
return false;
}
if (hour == 24 && (minute > 0 || second > 0)) {
return false;
}
return true;
}
}
public enum FMDateTimePrecision {
YearOnly,
YearMonthOnly,
DateOnly,
DateAndTime
}
}