Working with vague dates (90’s, Sept 2000 etc.)
On a recent project I had to allow users to upload their memories. Memories are vague things and I had to enable users to upload and assign incomplete dates. They needed to be able to enter ‘The 90’s’ or ‘September 2000′ as well as complete dates ‘1st Feb 2009′.
I could have separated all components and build the dates on each read but that would be very db intensive so I created a class that would remember the vague date but build a complete date for storage in the database so we could process them normally.
I created properties for the date components (day, month, year, era) and a normal date. When any of the d/m/y properties were set the class builds a random date based around the completed parts. So If you only supply a month and year the class generates a random day.
/// /// Type for the vague dates /// /// A Vague date is a date that is not complete. Say January 2009, or in the 90's. /// the unfilled date parts so /// that we still have a real date to order/filter by. /// This class is used to support such dates and generate a random date to supply public class VagueDateType { #region Constructors (3) public VagueDateType(int day, int month, int year, int era) { Day = day; Month = month; Year = year; Era = era; } public VagueDateType(DateTime date) { Date = date;} public VagueDateType() { } #endregion Constructors #region Properties (6) public DateTime Date {get;set;} public int Day { get; set; } public int Era { get; set; } /// /// Is the current data valid ///When Day, Month, Year, Era are all zero then we have a full date ///and the VagueDateType.IsVagueDate returns false. /// If Day, Month, Year, Era are larger than zero then the /// VagueDateType.IsVagueDate returns true. public bool IsValid { get { try { BuildDate(); } catch (Exception ex) { //failed due to incorrect date parts return false; } //we have a date if (Date != DateTime.MinValue) return true; //do we have valid date parts if (Era != 0 & Era> 1800 & Era < 2100) return true; if (Year != 0 & Month == 0 & Day == 0) return true; if (Year != 0 & Month != 0 & Day == 0) return true; if (Year != 0 & Month != 0 & Day != 0) return true; return false; } } public bool IsVagueDate { get { if (Day != 0 || Month != 0 || Year != 0 || Era != 0) { return true; } else { return false; } } } public int Month { get; set; } public int Year { get; set; } #endregion Properties #region Methods (5) // Public Methods (5) public DateTime BuildDate() { //If we have a date return that else build a random one with the info we have if (Date == DateTime.MinValue) { DateTime d = DateTime.MinValue; int day = 0, month = 0, year = Year; Random Rnd = new Random(); day = Day == 0 ? Rnd.Next(1, 28) : Day; month = Month == 0 ? Rnd.Next(1, 12) : Month; if (Year == 0) { //No year so must be an era year = Era + Rnd.Next(1, 9); } d = new DateTime(year, month, day); return d; } else { return Date; } } /// /// Builds a date replacing 1st with any unsupplied value /// /// /// The built date and an out param containg the next period public DateTime BuildDate(bool zeroUnsuppliedParts, out DateTime nextPeriod) { int day = 0, month = 0, year = Year; eDatePart largestPopulatedPart = eDatePart.None; if (Era != 0) { //No year so must be an era year = Era; largestPopulatedPart = eDatePart.Era; } if (Year == 0) { year = 1; } else { year = Year; largestPopulatedPart = eDatePart.Year; } if (Month == 0) { month = 1; } else { month = Month; largestPopulatedPart = eDatePart.Month; } if (Day == 0) { day = 1; } else { day = Day; largestPopulatedPart = eDatePart.Day; } DateTime d = new DateTime(year, month, day); nextPeriod = NextPeriod(largestPopulatedPart, d); return d; } /// /// Which is the most detailed part of the vague date /// /// public eDatePart SmallestDatePart() { eDatePart largestPopulatedPart = eDatePart.None; if (Era != 0) { largestPopulatedPart = eDatePart.Era; } if (Year != 0) { largestPopulatedPart = eDatePart.Year; } if (Month != 0) { largestPopulatedPart = eDatePart.Month; } if (Day != 0) { largestPopulatedPart = eDatePart.Day; } return largestPopulatedPart; } public void Clear() { Day = 0; Month = 0; Year = 0; Era = 0; Date = new DateTime(); } public void Populate(int day, int month, int year, int era) { Day = day; Month = month; Year = year; Era = era; } public void PopulateFromDate(DateTime date) { Date = date; } /// /// ToString() /// /// Returns the date formatted for use in rendering /// /// FullDate = "Monday, 13 Febuary 1987" /// Month/Year = "Febuary 1987" /// Year = "1987" /// Era = "1990-1999" /// public override string ToString() { if (Date != DateTime.MinValue) { return Date.ToString("ddd, MMM dd yyyy"); } else { //partial date so build it if(Day == 0 & Month == 0 & Year == 0 & Era != 0){ return Era.ToString() + "-" + (Era + 9).ToString(); } if (Day == 0 & Month == 0 & Year != 0) { return Year.ToString(); } if (Day == 0 & Month != 0) { DateTime d = new DateTime(Year, Month, 1); return d.ToString("MMMM yyyy"); } } return ""; } public DateTime NextPeriod(eDatePart part, DateTime startDate) { switch (part) { case eDatePart.Day: return startDate.AddDays(1); break; case eDatePart.Month: return startDate.AddMonths(1); break; case eDatePart.Year: return startDate.AddYears(1); break; case eDatePart.Era: return startDate.AddYears(10); break; default: return new DateTime(); break; } } #endregion Methods public enum eDatePart { None=0, Day = 1, Month = 2, Year = 3, Era = 4, All = 5 } }