/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateApproximate"/> class. /// </summary> /// <param name="date">The formal duration string that describes a GEDCOM X date approximation.</param> /// <exception cref="GedcomxDateException">Thrown if the specified date is null, empty, or does not begin with 'A' (as required by a formal date string).</exception> public GedcomxDateApproximate(String date) { if (date == null || date.Length < 1 || date[0] != 'A') { throw new GedcomxDateException("Invalid Approximate Date: Must start with A"); } simpleDate = new GedcomxDateSimple(date.Substring(1)); }
/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateApproximate"/> class. /// </summary> /// <param name="date">The formal duration string that describes a GEDCOM X date approximation.</param> /// <exception cref="GedcomxDateException">Thrown if the specified date is null, empty, or does not begin with 'A' (as required by a formal date string).</exception> public GedcomxDateApproximate(String date) { if (date == null || date.Length < 1 || date[0] != 'A') { throw new GedcomxDateException("Invalid Approximate Date: Must start with A"); } simpleDate = new GedcomxDateSimple(date.Substring(1)); }
/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateRecurring"/> class. /// </summary> /// <param name="date">The recurring date string that describes a recurring GEDCOM X date.</param> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the formal date string is null, empty, or fails to meet the expected format. The specific reason will be given at runtime. /// </exception> public GedcomxDateRecurring(String date) { if (date == null || date.Length < 3) { throw new GedcomxDateException("Invalid Recurring Date"); } if (date[0] != 'R') { throw new GedcomxDateException("Invalid Recurring Date: Must start with R"); } String[] parts = date.Split('/'); if (parts.Length != 3) { throw new GedcomxDateException("Invalid Recurring Date: Must contain 3 parts"); } // We must have a start and end if (parts[1].Equals("") || parts[2].Equals("")) { throw new GedcomxDateException("Invalid Recurring Date: Range must have a start and an end"); } String countNum = parts[0].Substring(1); char[] countNumChars = parts[0].Substring(1).ToCharArray(); if (countNumChars.Length > 0) { foreach (char c in countNumChars) { if (!Char.IsDigit(c)) { throw new GedcomxDateException("Invalid Recurring Date: Malformed Count"); } } count = Int32.Parse(countNum); } try { range = new GedcomxDateRange(parts[1] + "/" + parts[2]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Recurring Range"); } // If we have a count set end if (count != null) { end = GetNth(count.Value); } }
/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateRecurring"/> class. /// </summary> /// <param name="date">The recurring date string that describes a recurring GEDCOM X date.</param> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the formal date string is null, empty, or fails to meet the expected format. The specific reason will be given at runtime. /// </exception> public GedcomxDateRecurring(String date) { if (date == null || date.Length < 3) { throw new GedcomxDateException("Invalid Recurring Date"); } if (date[0] != 'R') { throw new GedcomxDateException("Invalid Recurring Date: Must start with R"); } String[] parts = date.Split('/'); if (parts.Length != 3) { throw new GedcomxDateException("Invalid Recurring Date: Must contain 3 parts"); } // We must have a start and end if (parts[1].Equals("") || parts[2].Equals("")) { throw new GedcomxDateException("Invalid Recurring Date: Range must have a start and an end"); } String countNum = parts[0].Substring(1); char[] countNumChars = parts[0].Substring(1).ToCharArray(); if (countNumChars.Length > 0) { foreach (char c in countNumChars) { if (!Char.IsDigit(c)) { throw new GedcomxDateException("Invalid Recurring Date: Malformed Count"); } } count = Int32.Parse(countNum); } try { range = new GedcomxDateRange(parts[1] + "/" + parts[2]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Recurring Range"); } // If we have a count set end if (count != null) { end = GetNth(count.Value); } }
/// <summary> /// Initializes a new instance of the <see cref="Date"/> class. /// </summary> /// <param name="simple">The simple GEDCOM X date.</param> /// <param name="adjustTimezone"> /// If set to <c>true</c> the resulting date hours and minutes will have the timezone hours and minutes added. See remarks for more information. /// </param> /// <remarks> /// The timezone adjustment is only applied when the adjustTimezone parameter is <c>true</c> and the incoming simple date hours and minutes are set. Thus, /// if the simple date hours are null, no adjustment will be made to the hours. Likewise, if the simple date minutes are null, no adjustment will be made /// to minutes. /// </remarks> public Date(GedcomxDateSimple simple, bool adjustTimezone) { year = simple.Year; month = simple.Month; day = simple.Day; hours = simple.Hours; minutes = simple.Minutes; seconds = simple.Seconds; if (adjustTimezone) { if (hours != null && simple.TzHours != null) { hours += simple.TzHours; } if (minutes != null && simple.TzMinutes != null) { minutes += simple.TzMinutes; } } }
/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateRange"/> class. /// </summary> /// <param name="date">The formal date string that describes a GEDCOM X date range.</param> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the formal date is null, empty, or does not meet the expected format. /// </exception> public GedcomxDateRange(String date) { if (date == null || date.Length < 1) { throw new GedcomxDateException("Invalid Range"); } String range = date; // If range starts with A it is recurring if (date[0] == 'A') { approximate = true; range = date.Substring(1); } // / is required if (!date.Contains("/")) { throw new GedcomxDateException("Invalid Range: / is required"); } /* * range -> parts * / -> [] * +1000/ -> ["+1000"] * /+1000 -> ["","+1000"] * +1000/+2000 -> ["+1000","+2000"] */ String[] parts = range.Split('/'); if (parts.Length < 1 || parts.Length > 2) { throw new GedcomxDateException("Invalid Range: One or two parts are required"); } if (!parts[0].Equals("")) { try { start = new GedcomxDateSimple(parts[0]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range Start Date"); } } if (parts.Length == 2) { if (parts[1][0] == 'P') { if (start == null) { throw new GedcomxDateException("Invalid Range: A range may not end with a duration if missing a start date"); } try { duration = new GedcomxDateDuration(parts[1]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range End Duration"); } // Use the duration to calculate the end date end = GedcomxDateUtil.AddDuration(start, duration); } else { try { end = new GedcomxDateSimple(parts[1]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range End Date"); } if (start != null) { duration = GedcomxDateUtil.GetDuration(start, end); } } } }
/// <summary> /// Initializes a new instance of the <see cref="GedcomxDateRange"/> class. /// </summary> /// <param name="date">The formal date string that describes a GEDCOM X date range.</param> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the formal date is null, empty, or does not meet the expected format. /// </exception> public GedcomxDateRange(String date) { if (date == null || date.Length < 1) { throw new GedcomxDateException("Invalid Range"); } String range = date; // If range starts with A it is recurring if (date[0] == 'A') { approximate = true; range = date.Substring(1); } // / is required if (!date.Contains("/")) { throw new GedcomxDateException("Invalid Range: / is required"); } /* * range -> parts * / -> [] * +1000/ -> ["+1000"] * /+1000 -> ["","+1000"] * +1000/+2000 -> ["+1000","+2000"] */ String[] parts = range.Split('/'); if (parts.Length < 1 || parts.Length > 2) { throw new GedcomxDateException("Invalid Range: One or two parts are required"); } if (!parts[0].Equals("")) { try { start = new GedcomxDateSimple(parts[0]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range Start Date"); } } if (parts.Length == 2) { if (parts[1][0] == 'P') { if (start == null) { throw new GedcomxDateException("Invalid Range: A range may not end with a duration if missing a start date"); } try { duration = new GedcomxDateDuration(parts[1]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range End Duration"); } // Use the duration to calculate the end date end = GedcomxDateUtil.AddDuration(start, duration); } else { try { end = new GedcomxDateSimple(parts[1]); } catch (GedcomxDateException e) { throw new GedcomxDateException(e.Message + " in Range End Date"); } if (start != null) { duration = GedcomxDateUtil.GetDuration(start, end); } } } }
/// <summary> /// Gets the duration between the two specified GEDCOM X dates. /// </summary> /// <param name="startDate">The simple start date.</param> /// <param name="endDate">The simple end date.</param> /// <returns>A <see cref="GedcomxDateDuration"/> representing the duration between the two specified dates.</returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if one of the input dates is null /// or /// Thrown if the start date occurs after the end date, or is equal to the end time. /// </exception> public static GedcomxDateDuration GetDuration(GedcomxDateSimple startDate, GedcomxDateSimple endDate) { if (startDate == null || endDate == null) { throw new GedcomxDateException("Start and End must be simple dates"); } Date start = new Date(startDate, true); Date end = new Date(endDate, true); bool hasTime = false; StringBuilder duration = new StringBuilder(); ZipDates(start, end); // Build the duration backwards so we can grab the correct diff // Also we need to roll everything up so we don't generate an invalid max year if (end.seconds != null) { while (end.seconds - start.seconds < 0) { end.minutes -= 1; end.seconds += 60; } if (end.seconds - start.seconds > 0) { hasTime = true; duration.Insert(0, 'S').Insert(0, String.Format("{0:00}", end.seconds - start.seconds)); } } if (end.minutes != null) { while (end.minutes - start.minutes < 0) { end.hours -= 1; end.minutes += 60; } if (end.minutes - start.minutes > 0) { hasTime = true; duration.Insert(0, 'M').Insert(0, String.Format("{0:00}", end.minutes - start.minutes)); } } if (end.hours != null) { while (end.hours - start.hours < 0) { end.day -= 1; end.hours += 24; } if (end.hours - start.hours > 0) { hasTime = true; duration.Insert(0, 'H').Insert(0, String.Format("{0:00}", end.hours - start.hours)); } } if (hasTime) { duration.Insert(0, 'T'); } if (end.day != null) { while (end.day - start.day < 0) { end.day += DateTime.DaysInMonth(end.year.Value, end.month.Value); end.month -= 1; if (end.month < 1) { end.year -= 1; end.month += 12; } } if (end.day - start.day > 0) { duration.Insert(0, 'D').Insert(0, String.Format("{0:00}", end.day - start.day)); } } if (end.month != null) { while (end.month - start.month < 0) { end.year -= 1; end.month += 12; } if (end.month - start.month > 0) { duration.Insert(0, 'M').Insert(0, String.Format("{0:00}", end.month - start.month)); } } if (end.year - start.year > 0) { duration.Insert(0, 'Y').Insert(0, String.Format("%04d", end.year - start.year)); } String finalDuration = duration.ToString(); if (end.year - start.year < 0 || duration.Equals("")) { throw new GedcomxDateException("Start Date must be less than End Date"); } return new GedcomxDateDuration("P" + finalDuration); }
/// <summary> /// Initializes a new instance of the <see cref="Date"/> class. /// </summary> /// <param name="simple">The simple GEDCOM X date.</param> /// <param name="adjustTimezone"> /// If set to <c>true</c> the resulting date hours and minutes will have the timezone hours and minutes added. See remarks for more information. /// </param> /// <remarks> /// The timezone adjustment is only applied when the adjustTimezone parameter is <c>true</c> and the incoming simple date hours and minutes are set. Thus, /// if the simple date hours are null, no adjustment will be made to the hours. Likewise, if the simple date minutes are null, no adjustment will be made /// to minutes. /// </remarks> public Date(GedcomxDateSimple simple, bool adjustTimezone) { year = simple.Year; month = simple.Month; day = simple.Day; hours = simple.Hours; minutes = simple.Minutes; seconds = simple.Seconds; if (adjustTimezone) { if (hours != null && simple.TzHours != null) { hours += simple.TzHours; } if (minutes != null && simple.TzMinutes != null) { minutes += simple.TzMinutes; } } }
/// <summary> /// Adds a duration to the specified simple date and returns the resulting simple date. /// </summary> /// <param name="startDate">The start date that will have the specified duration added.</param> /// <param name="duration">The duration to add to the specified simple date.</param> /// <returns>The <see cref="GedcomxDateSimple"/> date resulting from adding the duration to the specified date. </returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Throw if the start date is null /// or /// Thrown if the duration is null /// or /// Thrown if the resulting end year is beyond 9999. /// </exception> public static GedcomxDateSimple AddDuration(GedcomxDateSimple startDate, GedcomxDateDuration duration) { if (startDate == null) { throw new GedcomxDateException("Invalid Start Date"); } if (duration == null) { throw new GedcomxDateException("Invalid Duration"); } Date end = new Date(startDate, false); StringBuilder endString = new StringBuilder(); // Initialize all the values we need in end based on the duration ZipDuration(end, duration); // Add Timezone offset to endString if (startDate.TzHours != null) { endString.Append(startDate.TzHours >= 0 ? "+" : "-").Append(String.Format("{0:00}", Math.Abs(startDate.TzHours.Value))); endString.Append(":").Append(String.Format("{0:00}", startDate.TzMinutes)); } if (end.seconds != null) { if (duration.Seconds != null) { end.seconds += duration.Seconds; } while (end.seconds >= 60) { end.seconds -= 60; end.minutes += 1; } endString.Insert(0, String.Format("{0:00}", end.seconds)).Insert(0, ":"); } if (end.minutes != null) { if (duration.Minutes != null) { end.minutes += duration.Minutes; } while (end.minutes >= 60) { end.minutes -= 60; end.hours += 1; } endString.Insert(0, String.Format("{0:00}", end.minutes)).Insert(0, ":"); } if (end.hours != null) { if (duration.Hours != null) { end.hours += duration.Hours; } while (end.hours >= 24) { end.hours -= 24; end.day += 1; } endString.Insert(0, String.Format("{0:00}", end.hours)).Insert(0, "T"); } if (end.day != null) { if (duration.Days != null) { end.day += duration.Days; } while (end.day >= DateTime.DaysInMonth(end.year.Value, end.month.Value)) { end.month += 1; if (end.month > 12) { end.month -= 12; end.year += 1; } end.day -= DateTime.DaysInMonth(end.year.Value, end.month.Value); } endString.Insert(0, String.Format("{0:00}", end.day)).Insert(0, "-"); } if (end.month != null) { if (duration.Months != null) { end.month += duration.Months; } while (end.month > 12) { end.month -= 12; end.year += 1; } endString.Insert(0, String.Format("{0:00}", end.month)).Insert(0, "-"); } if (duration.Years != null) { end.year += duration.Years; } // After adding months to this year we could have bumped into or out of a non leap year // TODO fix this if (end.year > 9999) { throw new GedcomxDateException("New date out of range"); } if (end.year != null) { endString.Insert(0, String.Format("%04d", Math.Abs(end.year.Value))).Insert(0, end.year >= 0 ? "+" : "-"); } return new GedcomxDateSimple(endString.ToString()); }
/// <summary> /// Gets the duration between the two specified GEDCOM X dates. /// </summary> /// <param name="startDate">The simple start date.</param> /// <param name="endDate">The simple end date.</param> /// <returns>A <see cref="GedcomxDateDuration"/> representing the duration between the two specified dates.</returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if one of the input dates is null /// or /// Thrown if the start date occurs after the end date, or is equal to the end time. /// </exception> public static GedcomxDateDuration GetDuration(GedcomxDateSimple startDate, GedcomxDateSimple endDate) { if (startDate == null || endDate == null) { throw new GedcomxDateException("Start and End must be simple dates"); } Date start = new Date(startDate, true); Date end = new Date(endDate, true); bool hasTime = false; StringBuilder duration = new StringBuilder(); ZipDates(start, end); // Build the duration backwards so we can grab the correct diff // Also we need to roll everything up so we don't generate an invalid max year if (end.seconds != null) { while (end.seconds - start.seconds < 0) { end.minutes -= 1; end.seconds += 60; } if (end.seconds - start.seconds > 0) { hasTime = true; duration.Insert(0, 'S').Insert(0, String.Format("{0:00}", end.seconds - start.seconds)); } } if (end.minutes != null) { while (end.minutes - start.minutes < 0) { end.hours -= 1; end.minutes += 60; } if (end.minutes - start.minutes > 0) { hasTime = true; duration.Insert(0, 'M').Insert(0, String.Format("{0:00}", end.minutes - start.minutes)); } } if (end.hours != null) { while (end.hours - start.hours < 0) { end.day -= 1; end.hours += 24; } if (end.hours - start.hours > 0) { hasTime = true; duration.Insert(0, 'H').Insert(0, String.Format("{0:00}", end.hours - start.hours)); } } if (hasTime) { duration.Insert(0, 'T'); } if (end.day != null) { while (end.day - start.day < 0) { end.day += DateTime.DaysInMonth(end.year.Value, end.month.Value); end.month -= 1; if (end.month < 1) { end.year -= 1; end.month += 12; } } if (end.day - start.day > 0) { duration.Insert(0, 'D').Insert(0, String.Format("{0:00}", end.day - start.day)); } } if (end.month != null) { while (end.month - start.month < 0) { end.year -= 1; end.month += 12; } if (end.month - start.month > 0) { duration.Insert(0, 'M').Insert(0, String.Format("{0:00}", end.month - start.month)); } } if (end.year - start.year > 0) { duration.Insert(0, 'Y').Insert(0, String.Format("%04d", end.year - start.year)); } String finalDuration = duration.ToString(); if (end.year - start.year < 0 || duration.Equals("")) { throw new GedcomxDateException("Start Date must be less than End Date"); } return(new GedcomxDateDuration("P" + finalDuration)); }
/// <summary> /// Adds a duration to the specified simple date and returns the resulting simple date. /// </summary> /// <param name="startDate">The start date that will have the specified duration added.</param> /// <param name="duration">The duration to add to the specified simple date.</param> /// <returns>The <see cref="GedcomxDateSimple"/> date resulting from adding the duration to the specified date. </returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Throw if the start date is null /// or /// Thrown if the duration is null /// or /// Thrown if the resulting end year is beyond 9999. /// </exception> public static GedcomxDateSimple AddDuration(GedcomxDateSimple startDate, GedcomxDateDuration duration) { if (startDate == null) { throw new GedcomxDateException("Invalid Start Date"); } if (duration == null) { throw new GedcomxDateException("Invalid Duration"); } Date end = new Date(startDate, false); StringBuilder endString = new StringBuilder(); // Initialize all the values we need in end based on the duration ZipDuration(end, duration); // Add Timezone offset to endString if (startDate.TzHours != null) { endString.Append(startDate.TzHours >= 0 ? "+" : "-").Append(String.Format("{0:00}", Math.Abs(startDate.TzHours.Value))); endString.Append(":").Append(String.Format("{0:00}", startDate.TzMinutes)); } if (end.seconds != null) { if (duration.Seconds != null) { end.seconds += duration.Seconds; } while (end.seconds >= 60) { end.seconds -= 60; end.minutes += 1; } endString.Insert(0, String.Format("{0:00}", end.seconds)).Insert(0, ":"); } if (end.minutes != null) { if (duration.Minutes != null) { end.minutes += duration.Minutes; } while (end.minutes >= 60) { end.minutes -= 60; end.hours += 1; } endString.Insert(0, String.Format("{0:00}", end.minutes)).Insert(0, ":"); } if (end.hours != null) { if (duration.Hours != null) { end.hours += duration.Hours; } while (end.hours >= 24) { end.hours -= 24; end.day += 1; } endString.Insert(0, String.Format("{0:00}", end.hours)).Insert(0, "T"); } if (end.day != null) { if (duration.Days != null) { end.day += duration.Days; } while (end.day >= DateTime.DaysInMonth(end.year.Value, end.month.Value)) { end.month += 1; if (end.month > 12) { end.month -= 12; end.year += 1; } end.day -= DateTime.DaysInMonth(end.year.Value, end.month.Value); } endString.Insert(0, String.Format("{0:00}", end.day)).Insert(0, "-"); } if (end.month != null) { if (duration.Months != null) { end.month += duration.Months; } while (end.month > 12) { end.month -= 12; end.year += 1; } endString.Insert(0, String.Format("{0:00}", end.month)).Insert(0, "-"); } if (duration.Years != null) { end.year += duration.Years; } // After adding months to this year we could have bumped into or out of a non leap year // TODO fix this if (end.year > 9999) { throw new GedcomxDateException("New date out of range"); } if (end.year != null) { endString.Insert(0, String.Format("%04d", Math.Abs(end.year.Value))).Insert(0, end.year >= 0 ? "+" : "-"); } return(new GedcomxDateSimple(endString.ToString())); }