using System; using Csla.Properties; namespace Csla { /// /// Provides a date data type that understands the concept /// of an empty date value. /// /// /// See Chapter 5 for a full discussion of the need for this /// data type and the design choices behind it. /// [Serializable()] public struct SmartDate : IComparable { private DateTime _date; private bool _initialized; private EmptyValue _emptyValue; private string _format; private static string _defaultFormat; #region EmptyValue enum /// /// Indicates the empty value of a /// SmartDate. /// public enum EmptyValue { /// /// Indicates that an empty SmartDate /// is the smallest date. /// MinDate, /// /// Indicates that an empty SmartDate /// is the largest date. /// MaxDate } #endregion #region Constructors static SmartDate() { _defaultFormat = "d"; } /// /// Creates a new SmartDate object. /// /// Indicates whether an empty date is the min or max date value. public SmartDate(bool emptyIsMin) { _emptyValue = GetEmptyValue(emptyIsMin); _format = null; _initialized = false; // provide a dummy value to allow real initialization _date = DateTime.MinValue; SetEmptyDate(_emptyValue); } /// /// Creates a new SmartDate object. /// /// Indicates whether an empty date is the min or max date value. public SmartDate(EmptyValue emptyValue) { _emptyValue = emptyValue; _format = null; _initialized = false; // provide a dummy value to allow real initialization _date = DateTime.MinValue; SetEmptyDate(_emptyValue); } /// /// Creates a new SmartDate object. /// /// /// The SmartDate created will use the min possible /// date to represent an empty date. /// /// The initial value of the object. public SmartDate(DateTime value) { _emptyValue = Csla.SmartDate.EmptyValue.MinDate; _format = null; _initialized = false; _date = DateTime.MinValue; Date = value; } /// /// Creates a new SmartDate object. /// /// The initial value of the object. /// Indicates whether an empty date is the min or max date value. public SmartDate(DateTime value, bool emptyIsMin) { _emptyValue = GetEmptyValue(emptyIsMin); _format = null; _initialized = false; _date = DateTime.MinValue; Date = value; } /// /// Creates a new SmartDate object. /// /// The initial value of the object. /// Indicates whether an empty date is the min or max date value. public SmartDate(DateTime value, EmptyValue emptyValue) { _emptyValue = emptyValue; _format = null; _initialized = false; _date = DateTime.MinValue; Date = value; } /// /// Creates a new SmartDate object. /// /// /// The SmartDate created will use the min possible /// date to represent an empty date. /// /// The initial value of the object (as text). public SmartDate(string value) { _emptyValue = EmptyValue.MinDate; _format = null; _initialized = true; _date = DateTime.MinValue; this.Text = value; } /// /// Creates a new SmartDate object. /// /// The initial value of the object (as text). /// Indicates whether an empty date is the min or max date value. public SmartDate(string value, bool emptyIsMin) { _emptyValue = GetEmptyValue(emptyIsMin); _format = null; _initialized = true; _date = DateTime.MinValue; this.Text = value; } /// /// Creates a new SmartDate object. /// /// The initial value of the object (as text). /// Indicates whether an empty date is the min or max date value. public SmartDate(string value, EmptyValue emptyValue) { _emptyValue = emptyValue; _format = null; _initialized = true; _date = DateTime.MinValue; this.Text = value; } private static EmptyValue GetEmptyValue(bool emptyIsMin) { if (emptyIsMin) return EmptyValue.MinDate; else return EmptyValue.MaxDate; } private void SetEmptyDate(EmptyValue emptyValue) { if (emptyValue == SmartDate.EmptyValue.MinDate) this.Date = DateTime.MinValue; else this.Date = DateTime.MaxValue; } #endregion #region Text Support /// /// Sets the global default format string used by all new /// SmartDate values going forward. /// /// /// The default global format string is "d" unless this /// method is called to change that value. Existing SmartDate /// values are unaffected by this method, only SmartDate /// values created after calling this method are affected. /// /// /// The format string should follow the requirements for the /// .NET System.String.Format statement. /// public static void SetDefaultFormatString(string formatString) { _defaultFormat = formatString; } /// /// Gets or sets the format string used to format a date /// value when it is returned as text. /// /// /// The format string should follow the requirements for the /// .NET System.String.Format statement. /// /// A format string. public string FormatString { get { if (_format == null) _format = _defaultFormat; return _format; } set { _format = value; } } /// /// Gets or sets the date value. /// /// /// /// This property can be used to set the date value by passing a /// text representation of the date. Any text date representation /// that can be parsed by the .NET runtime is valid. /// /// When the date value is retrieved via this property, the text /// is formatted by using the format specified by the /// property. The default is the /// short date format (d). /// /// public string Text { get { return DateToString(this.Date, FormatString, _emptyValue); } set { this.Date = StringToDate(value, _emptyValue); } } #endregion #region Date Support /// /// Gets or sets the date value. /// public DateTime Date { get { if (!_initialized) { _date = DateTime.MinValue; _initialized = true; } return _date; } set { _date = value; _initialized = true; } } #endregion #region System.Object overrides /// /// Returns a text representation of the date value. /// public override string ToString() { return this.Text; } /// /// Returns a text representation of the date value. /// /// /// A standard .NET format string. /// public string ToString(string format) { return DateToString(this.Date, format, _emptyValue); } /// /// Compares this object to another /// for equality. /// /// Object to compare for equality. public override bool Equals(object obj) { if (obj is SmartDate) { SmartDate tmp = (SmartDate)obj; if (this.IsEmpty && tmp.IsEmpty) return true; else return this.Date.Equals(tmp.Date); } else if (obj is DateTime) return this.Date.Equals((DateTime)obj); else if (obj is string) return (this.CompareTo(obj.ToString()) == 0); else return false; } /// /// Returns a hash code for this object. /// public override int GetHashCode() { return this.Date.GetHashCode(); } #endregion #region DBValue /// /// Gets a database-friendly version of the date value. /// /// /// /// If the SmartDate contains an empty date, this returns . /// Otherwise the actual date value is returned as type Date. /// /// This property is very useful when setting parameter values for /// a Command object, since it automatically stores null values into /// the database for empty date values. /// /// When you also use the SafeDataReader and its GetSmartDate method, /// you can easily read a null value from the database back into a /// SmartDate object so it remains considered as an empty date value. /// /// public object DBValue { get { if (this.IsEmpty) return DBNull.Value; else return this.Date; } } #endregion #region Empty Dates /// /// Gets a value indicating whether this object contains an empty date. /// public bool IsEmpty { get { if (_emptyValue == EmptyValue.MinDate) return this.Date.Equals(DateTime.MinValue); else return this.Date.Equals(DateTime.MaxValue); } } /// /// Gets a value indicating whether an empty date is the /// min or max possible date value. /// /// /// Whether an empty date is considered to be the smallest or largest possible /// date is only important for comparison operations. This allows you to /// compare an empty date with a real date and get a meaningful result. /// public bool EmptyIsMin { get { return (_emptyValue == EmptyValue.MinDate); } } #endregion #region Conversion Functions /// /// Converts a string value into a SmartDate. /// /// String containing the date value. /// A new SmartDate containing the date value. /// /// EmptyIsMin will default to . /// public static SmartDate Parse(string value) { return new SmartDate(value); } /// /// Converts a string value into a SmartDate. /// /// String containing the date value. /// Indicates whether an empty date is the min or max date value. /// A new SmartDate containing the date value. public static SmartDate Parse(string value, bool emptyIsMin) { return new SmartDate(value, emptyIsMin); } /// /// Converts a text date representation into a Date value. /// /// /// An empty string is assumed to represent an empty date. An empty date /// is returned as the MinValue of the Date datatype. /// /// The text representation of the date. /// A Date value. public static DateTime StringToDate(string value) { return StringToDate(value, true); } /// /// Converts a text date representation into a Date value. /// /// /// An empty string is assumed to represent an empty date. An empty date /// is returned as the MinValue or MaxValue of the Date datatype depending /// on the EmptyIsMin parameter. /// /// The text representation of the date. /// Indicates whether an empty date is the min or max date value. /// A Date value. public static DateTime StringToDate(string value, bool emptyIsMin) { return StringToDate(value, GetEmptyValue(emptyIsMin)); } /// /// Converts a text date representation into a Date value. /// /// /// An empty string is assumed to represent an empty date. An empty date /// is returned as the MinValue or MaxValue of the Date datatype depending /// on the EmptyIsMin parameter. /// /// The text representation of the date. /// Indicates whether an empty date is the min or max date value. /// A Date value. public static DateTime StringToDate(string value, EmptyValue emptyValue) { DateTime tmp; if (String.IsNullOrEmpty(value)) { if (emptyValue == EmptyValue.MinDate) return DateTime.MinValue; else return DateTime.MaxValue; } else if (DateTime.TryParse(value, out tmp)) return tmp; else { string ldate = value.Trim().ToLower(); if (ldate == Resources.SmartDateT || ldate == Resources.SmartDateToday || ldate == ".") return DateTime.Now; if (ldate == Resources.SmartDateY || ldate == Resources.SmartDateYesterday || ldate == "-") return DateTime.Now.AddDays(-1); if (ldate == Resources.SmartDateTom || ldate == Resources.SmartDateTomorrow || ldate == "+") return DateTime.Now.AddDays(1); throw new ArgumentException(Resources.StringToDateException); } } /// /// Converts a date value into a text representation. /// /// /// The date is considered empty if it matches the min value for /// the Date datatype. If the date is empty, this /// method returns an empty string. Otherwise it returns the date /// value formatted based on the FormatString parameter. /// /// The date value to convert. /// The format string used to format the date into text. /// Text representation of the date value. public static string DateToString( DateTime value, string formatString) { return DateToString(value, formatString, true); } /// /// Converts a date value into a text representation. /// /// /// Whether the date value is considered empty is determined by /// the EmptyIsMin parameter value. If the date is empty, this /// method returns an empty string. Otherwise it returns the date /// value formatted based on the FormatString parameter. /// /// The date value to convert. /// The format string used to format the date into text. /// Indicates whether an empty date is the min or max date value. /// Text representation of the date value. public static string DateToString( DateTime value, string formatString, bool emptyIsMin) { return DateToString(value, formatString, GetEmptyValue(emptyIsMin)); } /// /// Converts a date value into a text representation. /// /// /// Whether the date value is considered empty is determined by /// the EmptyIsMin parameter value. If the date is empty, this /// method returns an empty string. Otherwise it returns the date /// value formatted based on the FormatString parameter. /// /// The date value to convert. /// The format string used to format the date into text. /// Indicates whether an empty date is the min or max date value. /// Text representation of the date value. public static string DateToString( DateTime value, string formatString, EmptyValue emptyValue) { if (emptyValue == EmptyValue.MinDate) { if (value == DateTime.MinValue) return string.Empty; } else { if (value == DateTime.MaxValue) return string.Empty; } return string.Format("{0:" + formatString + "}", value); } #endregion #region Manipulation Functions /// /// Compares one SmartDate to another. /// /// /// This method works the same as the DateTime.CompareTo method /// on the Date datetype, with the exception that it /// understands the concept of empty date values. /// /// The date to which we are being compared. /// A value indicating if the comparison date is less than, equal to or greater than this date. public int CompareTo(SmartDate value) { if (this.IsEmpty && value.IsEmpty) return 0; else return _date.CompareTo(value.Date); } /// /// Compares one SmartDate to another. /// /// /// This method works the same as the DateTime.CompareTo method /// on the Date datetype, with the exception that it /// understands the concept of empty date values. /// /// The date to which we are being compared. /// A value indicating if the comparison date is less than, equal to or greater than this date. int IComparable.CompareTo(object value) { if (value is SmartDate) return CompareTo((SmartDate)value); else throw new ArgumentException(Resources.ValueNotSmartDateException); } /// /// Compares a SmartDate to a text date value. /// /// The date to which we are being compared. /// A value indicating if the comparison date is less than, equal to or greater than this date. public int CompareTo(string value) { return this.Date.CompareTo(StringToDate(value, _emptyValue)); } /// /// Compares a SmartDate to a date value. /// /// The date to which we are being compared. /// A value indicating if the comparison date is less than, equal to or greater than this date. public int CompareTo(DateTime value) { return this.Date.CompareTo(value); } /// /// Adds a TimeSpan onto the object. /// /// Span to add to the date. public DateTime Add(TimeSpan value) { if (IsEmpty) return this.Date; else return this.Date.Add(value); } /// /// Subtracts a TimeSpan from the object. /// /// Span to subtract from the date. public DateTime Subtract(TimeSpan value) { if (IsEmpty) return this.Date; else return this.Date.Subtract(value); } /// /// Subtracts a DateTime from the object. /// /// Date to subtract from the date. public TimeSpan Subtract(DateTime value) { if (IsEmpty) return TimeSpan.Zero; else return this.Date.Subtract(value); } #endregion #region Operators /// /// Equality operator /// /// First object /// Second object /// public static bool operator ==(SmartDate obj1, SmartDate obj2) { return obj1.Equals(obj2); } /// /// Inequality operator /// /// First object /// Second object /// public static bool operator !=(SmartDate obj1, SmartDate obj2) { return !obj1.Equals(obj2); } /// /// Equality operator /// /// First object /// Second object /// public static bool operator ==(SmartDate obj1, DateTime obj2) { return obj1.Equals(obj2); } /// /// Inequality operator /// /// First object /// Second object /// public static bool operator !=(SmartDate obj1, DateTime obj2) { return !obj1.Equals(obj2); } /// /// Equality operator /// /// First object /// Second object /// public static bool operator ==(SmartDate obj1, string obj2) { return obj1.Equals(obj2); } /// /// Inequality operator /// /// First object /// Second object /// public static bool operator !=(SmartDate obj1, string obj2) { return !obj1.Equals(obj2); } /// /// Addition operator /// /// Original date/time /// Span to add /// public static SmartDate operator +(SmartDate start, TimeSpan span) { return new SmartDate(start.Add(span), start.EmptyIsMin); } /// /// Subtraction operator /// /// Original date/time /// Span to subtract /// public static SmartDate operator -(SmartDate start, TimeSpan span) { return new SmartDate(start.Subtract(span), start.EmptyIsMin); } /// /// Subtraction operator /// /// Original date/time /// Second date/time /// public static TimeSpan operator -(SmartDate start, SmartDate finish) { return start.Subtract(finish.Date); } /// /// Greater than operator /// /// First object /// Second object /// public static bool operator >(SmartDate obj1, SmartDate obj2) { return obj1.CompareTo(obj2) > 0; } /// /// Less than operator /// /// First object /// Second object /// public static bool operator <(SmartDate obj1, SmartDate obj2) { return obj1.CompareTo(obj2) < 0; } /// /// Greater than operator /// /// First object /// Second object /// public static bool operator >(SmartDate obj1, DateTime obj2) { return obj1.CompareTo(obj2) > 0; } /// /// Less than operator /// /// First object /// Second object /// public static bool operator <(SmartDate obj1, DateTime obj2) { return obj1.CompareTo(obj2) < 0; } /// /// Greater than operator /// /// First object /// Second object /// public static bool operator >(SmartDate obj1, string obj2) { return obj1.CompareTo(obj2) > 0; } /// /// Less than operator /// /// First object /// Second object /// public static bool operator <(SmartDate obj1, string obj2) { return obj1.CompareTo(obj2) < 0; } /// /// Greater than or equals operator /// /// First object /// Second object /// public static bool operator >=(SmartDate obj1, SmartDate obj2) { return obj1.CompareTo(obj2) >= 0; } /// /// Less than or equals operator /// /// First object /// Second object /// public static bool operator <=(SmartDate obj1, SmartDate obj2) { return obj1.CompareTo(obj2) <= 0; } /// /// Greater than or equals operator /// /// First object /// Second object /// public static bool operator >=(SmartDate obj1, DateTime obj2) { return obj1.CompareTo(obj2) >= 0; } /// /// Less than or equals operator /// /// First object /// Second object /// public static bool operator <=(SmartDate obj1, DateTime obj2) { return obj1.CompareTo(obj2) <= 0; } /// /// Greater than or equals operator /// /// First object /// Second object /// public static bool operator >=(SmartDate obj1, string obj2) { return obj1.CompareTo(obj2) >= 0; } /// /// Less than or equals operator /// /// First object /// Second object /// public static bool operator <=(SmartDate obj1, string obj2) { return obj1.CompareTo(obj2) <= 0; } #endregion } }