/// <summary> /// Multiplies the duration by a fixed number. /// </summary> /// <param name="duration">The duration to be multiplied.</param> /// <param name="multiplier">The fixed number to multiply the duration.</param> /// <returns>The resulting <see cref="GedcomxDateDuration"/> after multiplying the specified duration by the fixed number.</returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the duration is null /// or /// Thrown if the multiplier is negative or zero. /// </exception> public static GedcomxDateDuration MultiplyDuration(GedcomxDateDuration duration, int multiplier) { if (duration == null) { throw new GedcomxDateException("Invalid Duration"); } if (multiplier <= 0) { throw new GedcomxDateException("Invalid Multiplier"); } StringBuilder newDuration = new StringBuilder("P"); if (duration.Years != null) { newDuration.Append(duration.Years * multiplier).Append('Y'); } if (duration.Months != null) { newDuration.Append(duration.Months * multiplier).Append('M'); } if (duration.Days != null) { newDuration.Append(duration.Days * multiplier).Append('D'); } if (duration.Hours != null || duration.Minutes != null || duration.Seconds != null) { newDuration.Append('T'); if (duration.Hours != null) { newDuration.Append(duration.Hours * multiplier).Append('H'); } if (duration.Minutes != null) { newDuration.Append(duration.Minutes * multiplier).Append('M'); } if (duration.Seconds != null) { newDuration.Append(duration.Seconds * multiplier).Append('S'); } } return(new GedcomxDateDuration(newDuration.ToString())); }
/// <summary> /// Gets the nth instance of this recurring date. /// </summary> /// <param name="count">The nth instance to retrieve.</param> /// <returns>The simple date of the nth instance.</returns> public GedcomxDateSimple GetNth(Int32 count) { GedcomxDateDuration duration = GedcomxDateUtil.MultiplyDuration(range.Duration, count); return(GedcomxDateUtil.AddDuration(range.Start, duration)); }
/// <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> /// Ensures the specified date has matching attributes based off the duration attributes. See remarks for more information. /// </summary> /// <param name="date">The date to evaluate.</param> /// <param name="duration">The duration to use for evaluation.</param> /// <remarks> /// If the specified duration has minutes and the specified date does not, this will initialize the minutes on /// the specified date. /// /// Furthermore, for the level of granularity specified in the duration (e.g., down to the minute but not second), /// this method ensures the specified date has the same level of granularity and greater. So if the specified duration /// has minutes set, but the specified date does not have minutes or some greater unit not set, this will initialize /// the minutes on the specified date and all other levels of granularity greater than minutes. Thus hours would be set /// if not already, and so on. It's important to note, however, that just like <see cref="ZipDates"/>, this method will /// not initialize any unit for which a value is already set. /// /// Note: This method only writes to the specified date, and only reads the duration. /// </remarks> protected static void ZipDuration(Date date, GedcomxDateDuration duration) { bool seconds = false; bool minutes = false; bool hours = false; bool days = false; bool months = false; if (duration.Seconds != null) { seconds = true; minutes = true; hours = true; days = true; months = true; } else if (duration.Minutes != null) { minutes = true; hours = true; days = true; months = true; } else if (duration.Hours != null) { hours = true; days = true; months = true; } else if (duration.Days != null) { days = true; months = true; } else if (duration.Months != null) { months = true; } else { return; } if (seconds && date.seconds == null) { date.seconds = 0; } if (minutes && date.minutes == null) { date.minutes = 0; } if (hours && date.hours == null) { date.hours = 0; } if (days && date.day == null) { date.day = 1; } if (months && date.month == null) { date.month = 1; } }
/// <summary> /// Multiplies the duration by a fixed number. /// </summary> /// <param name="duration">The duration to be multiplied.</param> /// <param name="multiplier">The fixed number to multiply the duration.</param> /// <returns>The resulting <see cref="GedcomxDateDuration"/> after multiplying the specified duration by the fixed number.</returns> /// <exception cref="Gedcomx.Date.GedcomxDateException"> /// Thrown if the duration is null /// or /// Thrown if the multiplier is negative or zero. /// </exception> public static GedcomxDateDuration MultiplyDuration(GedcomxDateDuration duration, int multiplier) { if (duration == null) { throw new GedcomxDateException("Invalid Duration"); } if (multiplier <= 0) { throw new GedcomxDateException("Invalid Multiplier"); } StringBuilder newDuration = new StringBuilder("P"); if (duration.Years != null) { newDuration.Append(duration.Years * multiplier).Append('Y'); } if (duration.Months != null) { newDuration.Append(duration.Months * multiplier).Append('M'); } if (duration.Days != null) { newDuration.Append(duration.Days * multiplier).Append('D'); } if (duration.Hours != null || duration.Minutes != null || duration.Seconds != null) { newDuration.Append('T'); if (duration.Hours != null) { newDuration.Append(duration.Hours * multiplier).Append('H'); } if (duration.Minutes != null) { newDuration.Append(duration.Minutes * multiplier).Append('M'); } if (duration.Seconds != null) { newDuration.Append(duration.Seconds * multiplier).Append('S'); } } return new GedcomxDateDuration(newDuration.ToString()); }
/// <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> /// Ensures the specified date has matching attributes based off the duration attributes. See remarks for more information. /// </summary> /// <param name="date">The date to evaluate.</param> /// <param name="duration">The duration to use for evaluation.</param> /// <remarks> /// If the specified duration has minutes and the specified date does not, this will initialize the minutes on /// the specified date. /// /// Furthermore, for the level of granularity specified in the duration (e.g., down to the minute but not second), /// this method ensures the specified date has the same level of granularity and greater. So if the specified duration /// has minutes set, but the specified date does not have minutes or some greater unit not set, this will initialize /// the minutes on the specified date and all other levels of granularity greater than minutes. Thus hours would be set /// if not already, and so on. It's important to note, however, that just like <see cref="ZipDates"/>, this method will /// not initialize any unit for which a value is already set. /// /// Note: This method only writes to the specified date, and only reads the duration. /// </remarks> protected static void ZipDuration(Date date, GedcomxDateDuration duration) { bool seconds = false; bool minutes = false; bool hours = false; bool days = false; bool months = false; if (duration.Seconds != null) { seconds = true; minutes = true; hours = true; days = true; months = true; } else if (duration.Minutes != null) { minutes = true; hours = true; days = true; months = true; } else if (duration.Hours != null) { hours = true; days = true; months = true; } else if (duration.Days != null) { days = true; months = true; } else if (duration.Months != null) { months = true; } else { return; } if (seconds && date.seconds == null) { date.seconds = 0; } if (minutes && date.minutes == null) { date.minutes = 0; } if (hours && date.hours == null) { date.hours = 0; } if (days && date.day == null) { date.day = 1; } if (months && date.month == null) { date.month = 1; } }
/// <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())); }