private static FiscalQuarter Plus(
            this FiscalQuarter unitOfTime,
            int unitsToAdd)
        {
            var genericQuarter = unitOfTime.ToGenericQuarter().Plus(unitsToAdd);
            var result         = new FiscalQuarter(genericQuarter.Year, genericQuarter.QuarterNumber);

            return(result);
        }
        /// <summary>
        /// Constructs a <see cref="FiscalQuarter"/> from a <see cref="QuarterNumber"/> and a year.
        /// </summary>
        /// <param name="quarterNumber">The quarter number.</param>
        /// <param name="year">The year.</param>
        /// <returns>
        /// The <see cref="FiscalQuarter"/>.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="quarterNumber"/> is <see cref="QuarterNumber.Invalid"/>.</exception>
        public static FiscalQuarter ToFiscal(
            this QuarterNumber quarterNumber,
            int year)
        {
            if (quarterNumber == QuarterNumber.Invalid)
            {
                throw new ArgumentOutOfRangeException(Invariant($"'{nameof(quarterNumber)}' == '{QuarterNumber.Invalid}'"), (Exception)null);
            }

            var result = new FiscalQuarter(year, quarterNumber);

            return(result);
        }
예제 #3
0
        /// <summary>
        /// Constructs a fiscal quarter from a <see cref="QuarterNumber"/> and a year.
        /// </summary>
        /// <param name="quarterNumber">The quarter number.</param>
        /// <param name="year">The year.</param>
        /// <returns>
        /// A fiscal quarter constructed from the specified <see cref="QuarterNumber"/> and year.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="quarterNumber"/> is <see cref="QuarterNumber.Invalid"/>.</exception>
        public static FiscalQuarter ToFiscal(
            this QuarterNumber quarterNumber,
            int year)
        {
            if (quarterNumber == QuarterNumber.Invalid)
            {
                throw new ArgumentException(Invariant($"{nameof(quarterNumber)} is {nameof(QuarterNumber.Invalid)}"));
            }

            var result = new FiscalQuarter(year, quarterNumber);

            return(result);
        }
        /// <summary>
        /// Converts a <see cref="FiscalQuarter"/> to a <see cref="CalendarQuarter"/>.
        /// </summary>
        /// <param name="calendarQuarter">The calendar quarter to convert.</param>
        /// <param name="calendarQuarterThatIsFirstFiscalQuarter">The calendar quarter that is associated with the first fiscal quarter for the company.</param>
        /// <returns>
        /// The fiscal quarter associated with the specified calendar quarter.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="calendarQuarter"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="calendarQuarterThatIsFirstFiscalQuarter"/> is <see cref="QuarterNumber.Invalid"/>.</exception>
        public static FiscalQuarter ToFiscalQuarter(
            this CalendarQuarter calendarQuarter,
            QuarterNumber calendarQuarterThatIsFirstFiscalQuarter)
        {
            if (calendarQuarter == null)
            {
                throw new ArgumentNullException(nameof(calendarQuarter));
            }

            if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Invalid)
            {
                throw new ArgumentOutOfRangeException(Invariant($"'{nameof(calendarQuarterThatIsFirstFiscalQuarter)}' == '{QuarterNumber.Invalid}'"), (Exception)null);
            }

            int offset;

            if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q4)
            {
                offset = 1;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q3)
            {
                offset = 2;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q2)
            {
                offset = 3;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q1)
            {
                offset = 0;
            }
            else
            {
                throw new NotSupportedException("This quarter number is not supported: " + calendarQuarterThatIsFirstFiscalQuarter);
            }

            var result = new FiscalQuarter(calendarQuarter.Year, calendarQuarter.QuarterNumber);

            result = result.Plus(offset);

            return(result);
        }
예제 #5
0
        /// <summary>
        /// Converts a <see cref="FiscalQuarter"/> to a <see cref="CalendarQuarter"/>.
        /// </summary>
        /// <param name="calendarQuarter">The calendar quarter to convert.</param>
        /// <param name="calendarQuarterThatIsFirstFiscalQuarter">The calendar quarter that is associated with the first fiscal quarter for the company.</param>
        /// <returns>
        /// The fiscal quarter associated with the specified calendar quarter.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="calendarQuarter"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="calendarQuarterThatIsFirstFiscalQuarter"/> is <see cref="QuarterNumber.Invalid"/>.</exception>
        public static FiscalQuarter ToFiscalQuarter(
            this CalendarQuarter calendarQuarter,
            QuarterNumber calendarQuarterThatIsFirstFiscalQuarter)
        {
            if (calendarQuarter == null)
            {
                throw new ArgumentNullException(nameof(calendarQuarter));
            }

            if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Invalid)
            {
                throw new ArgumentException("calendar quarter that is first fiscal quarter is Invalid.", nameof(calendarQuarterThatIsFirstFiscalQuarter));
            }

            int offset;

            if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q4)
            {
                offset = 1;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q3)
            {
                offset = 2;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q2)
            {
                offset = 3;
            }
            else if (calendarQuarterThatIsFirstFiscalQuarter == QuarterNumber.Q1)
            {
                offset = 0;
            }
            else
            {
                throw new NotSupportedException("This quarter number is not supported: " + calendarQuarterThatIsFirstFiscalQuarter);
            }

            var fiscalQuarter = new FiscalQuarter(calendarQuarter.Year, calendarQuarter.QuarterNumber);

            fiscalQuarter = fiscalQuarter.Plus(offset);
            return(fiscalQuarter);
        }
예제 #6
0
        public static UnitOfTime DeserializeFromSortableString(
            this string unitOfTime,
            Type type)
        {
            if (!type.IsUnitOfTimeType())
            {
                throw new NotSupportedException(Invariant($"Unsupported type {type?.FullName ?? "<NULL TYPE>"}, expected an implmenter {nameof(UnitOfTime)}"));
            }

            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (unitOfTime == null)
            {
                throw new ArgumentNullException(nameof(unitOfTime));
            }

            if (string.IsNullOrWhiteSpace(unitOfTime))
            {
                throw new ArgumentException(Invariant($"'{nameof(unitOfTime)}' is white space"));
            }

            var serializationFormatMatch = SerializationFormatByType.Select(_ => new { Match = _.Regex.Match(unitOfTime), SerializationFormat = _ }).SingleOrDefault(_ => _.Match.Success);

            if (serializationFormatMatch == null)
            {
                throw new InvalidOperationException("Cannot deserialize string; it is not valid unit-of-time.");
            }

            var serializedType = serializationFormatMatch.SerializationFormat.Type;

            if (!type.IsAssignableFrom(serializedType))
            {
                throw new InvalidOperationException(Invariant($"The unit-of-time appears to be a {serializedType.Name} which cannot be casted to a {type.Name}."));
            }

            string errorMessage = Invariant($"Cannot deserialize string;  it appears to be a {serializedType.Name} but it is malformed.");
            var    tokens       = serializationFormatMatch.SerializationFormat.Regex.GetGroupNames().Skip(1).Select(_ => serializationFormatMatch.Match.Groups[_].Value).ToArray();

            if (serializedType == typeof(CalendarDay))
            {
                var year  = ParseIntOrThrow(tokens[0], errorMessage);
                var month = ParseEnumOrThrow <MonthOfYear>(tokens[1], errorMessage);
                var day   = ParseEnumOrThrow <DayOfMonth>(tokens[2], errorMessage);

                try
                {
                    var result = new CalendarDay(year, month, day);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(CalendarMonth))
            {
                var year  = ParseIntOrThrow(tokens[0], errorMessage);
                var month = ParseEnumOrThrow <MonthOfYear>(tokens[1], errorMessage);

                try
                {
                    var result = new CalendarMonth(year, month);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(CalendarQuarter))
            {
                var year    = ParseIntOrThrow(tokens[0], errorMessage);
                var quarter = ParseEnumOrThrow <QuarterNumber>(tokens[1], errorMessage);

                try
                {
                    var result = new CalendarQuarter(year, quarter);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(CalendarYear))
            {
                var year = ParseIntOrThrow(tokens[0], errorMessage);

                try
                {
                    var result = new CalendarYear(year);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(CalendarUnbounded))
            {
                var result = new CalendarUnbounded();

                return(result);
            }

            if (serializedType == typeof(FiscalMonth))
            {
                var year  = ParseIntOrThrow(tokens[0], errorMessage);
                var month = ParseEnumOrThrow <MonthNumber>(tokens[1], errorMessage);

                try
                {
                    var result = new FiscalMonth(year, month);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(FiscalQuarter))
            {
                var year    = ParseIntOrThrow(tokens[0], errorMessage);
                var quarter = ParseEnumOrThrow <QuarterNumber>(tokens[1], errorMessage);

                try
                {
                    var result = new FiscalQuarter(year, quarter);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(FiscalYear))
            {
                var year = ParseIntOrThrow(tokens[0], errorMessage);

                try
                {
                    var result = new FiscalYear(year);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(FiscalUnbounded))
            {
                var result = new FiscalUnbounded();

                return(result);
            }

            if (serializedType == typeof(GenericMonth))
            {
                var year  = ParseIntOrThrow(tokens[0], errorMessage);
                var month = ParseEnumOrThrow <MonthNumber>(tokens[1], errorMessage);

                try
                {
                    var result = new GenericMonth(year, month);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(GenericQuarter))
            {
                var year    = ParseIntOrThrow(tokens[0], errorMessage);
                var quarter = ParseEnumOrThrow <QuarterNumber>(tokens[1], errorMessage);

                try
                {
                    var result = new GenericQuarter(year, quarter);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(GenericYear))
            {
                var year = ParseIntOrThrow(tokens[0], errorMessage);

                try
                {
                    var result = new GenericYear(year);

                    return(result);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            if (serializedType == typeof(GenericUnbounded))
            {
                var result = new GenericUnbounded();

                return(result);
            }

            throw new NotSupportedException("this type of unit-of-time is not supported: " + serializedType);
        }
예제 #7
0
        private static IReportingPeriod <UnitOfTime> MakeLessGranular(
            this IReportingPeriod <UnitOfTime> reportingPeriod,
            UnitOfTimeGranularity granularity)
        {
            if (reportingPeriod == null)
            {
                throw new ArgumentNullException(nameof(reportingPeriod));
            }

            if (reportingPeriod.HasComponentWithUnboundedGranularity())
            {
                throw new ArgumentException(Invariant($"{nameof(reportingPeriod)} has an {nameof(UnitOfTimeGranularity.Unbounded)} component"));
            }

            if (granularity == UnitOfTimeGranularity.Invalid)
            {
                throw new ArgumentException(Invariant($"{nameof(granularity)} is {nameof(UnitOfTimeGranularity.Invalid)}"));
            }

            if (granularity == UnitOfTimeGranularity.Unbounded)
            {
                throw new ArgumentException(Invariant($"{nameof(granularity)} is {nameof(UnitOfTimeGranularity.Unbounded)}"));
            }

            var reportingPeriodGranularity = reportingPeriod.GetUnitOfTimeGranularity();

            if (reportingPeriodGranularity.IsAsGranularOrLessGranularThan(granularity))
            {
                throw new ArgumentException(Invariant($"{nameof(reportingPeriod)} is as granular or less granular than {nameof(granularity)}"));
            }

            IReportingPeriod <UnitOfTime> lessGranularReportingPeriod;
            var unitOfTimeKind = reportingPeriod.GetUnitOfTimeKind();

            if (reportingPeriodGranularity == UnitOfTimeGranularity.Day)
            {
                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startAsCalendarDay = reportingPeriod.Start as CalendarDay;

                    // ReSharper disable once PossibleNullReferenceException
                    var startMonth = new CalendarMonth(startAsCalendarDay.Year, startAsCalendarDay.MonthOfYear);
                    if (startMonth.GetFirstCalendarDay() != startAsCalendarDay)
                    {
                        throw new InvalidOperationException("Cannot convert a calendar day reporting period to a calendar month reporting period when the reporting period start time is not the first day of a month.");
                    }

                    var endAsCalendarDay = reportingPeriod.End as CalendarDay;

                    // ReSharper disable once PossibleNullReferenceException
                    var endMonth = new CalendarMonth(endAsCalendarDay.Year, endAsCalendarDay.MonthOfYear);
                    if (endMonth.GetLastCalendarDay() != endAsCalendarDay)
                    {
                        throw new InvalidOperationException("Cannot convert a calendar day reporting period to a calendar month reporting period when the reporting period end time is not the last day of a month.");
                    }

                    lessGranularReportingPeriod = new ReportingPeriod <CalendarMonth>(startMonth, endMonth);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else if (reportingPeriodGranularity == UnitOfTimeGranularity.Month)
            {
                var startAsMonth = reportingPeriod.Start as IHaveAMonth;
                var endAsMonth   = reportingPeriod.End as IHaveAMonth;

                var quarterByStartMonth = new Dictionary <int, QuarterNumber>
                {
                    { 1, QuarterNumber.Q1 },
                    { 4, QuarterNumber.Q2 },
                    { 7, QuarterNumber.Q3 },
                    { 10, QuarterNumber.Q4 },
                };

                var quarterByEndMonth = new Dictionary <int, QuarterNumber>
                {
                    { 3, QuarterNumber.Q1 },
                    { 6, QuarterNumber.Q2 },
                    { 9, QuarterNumber.Q3 },
                    { 12, QuarterNumber.Q4 },
                };

                // ReSharper disable once PossibleNullReferenceException
                if (!quarterByStartMonth.ContainsKey((int)startAsMonth.MonthNumber))
                {
                    throw new InvalidOperationException("Cannot convert a monthly reporting period to a quarterly reporting period when the reporting period start time is not the first month of a recognized quarter.");
                }

                // ReSharper disable once PossibleNullReferenceException
                if (!quarterByEndMonth.ContainsKey((int)endAsMonth.MonthNumber))
                {
                    throw new InvalidOperationException("Cannot convert a monthly reporting period to a quarterly reporting period when the reporting period end time is not the last month of a recognized quarter.");
                }

                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startQuarter = new CalendarQuarter(startAsMonth.Year, quarterByStartMonth[(int)startAsMonth.MonthNumber]);
                    var endQuarter   = new CalendarQuarter(endAsMonth.Year, quarterByEndMonth[(int)endAsMonth.MonthNumber]);
                    lessGranularReportingPeriod = new ReportingPeriod <CalendarQuarter>(startQuarter, endQuarter);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Fiscal)
                {
                    var startQuarter = new FiscalQuarter(startAsMonth.Year, quarterByStartMonth[(int)startAsMonth.MonthNumber]);
                    var endQuarter   = new FiscalQuarter(endAsMonth.Year, quarterByEndMonth[(int)endAsMonth.MonthNumber]);
                    lessGranularReportingPeriod = new ReportingPeriod <FiscalQuarter>(startQuarter, endQuarter);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Generic)
                {
                    var startQuarter = new GenericQuarter(startAsMonth.Year, quarterByStartMonth[(int)startAsMonth.MonthNumber]);
                    var endQuarter   = new GenericQuarter(endAsMonth.Year, quarterByEndMonth[(int)endAsMonth.MonthNumber]);
                    lessGranularReportingPeriod = new ReportingPeriod <GenericQuarter>(startQuarter, endQuarter);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else if (reportingPeriodGranularity == UnitOfTimeGranularity.Quarter)
            {
                var startAsQuarter = reportingPeriod.Start as IHaveAQuarter;
                var endAsQuarter   = reportingPeriod.End as IHaveAQuarter;

                // ReSharper disable once PossibleNullReferenceException
                if (startAsQuarter.QuarterNumber != QuarterNumber.Q1)
                {
                    throw new InvalidOperationException(
                              "Cannot convert a quarterly reporting period to a yearly reporting period when the reporting period start time is not Q1.");
                }

                // ReSharper disable once PossibleNullReferenceException
                if (endAsQuarter.QuarterNumber != QuarterNumber.Q4)
                {
                    throw new InvalidOperationException(
                              "Cannot convert a quarterly reporting period to a yearly reporting period when the reporting period end is not Q4.");
                }

                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startYear = new CalendarYear(startAsQuarter.Year);
                    var endYear   = new CalendarYear(endAsQuarter.Year);
                    lessGranularReportingPeriod = new ReportingPeriod <CalendarYear>(startYear, endYear);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Fiscal)
                {
                    var startYear = new FiscalYear(startAsQuarter.Year);
                    var endYear   = new FiscalYear(endAsQuarter.Year);
                    lessGranularReportingPeriod = new ReportingPeriod <FiscalYear>(startYear, endYear);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Generic)
                {
                    var startYear = new GenericYear(startAsQuarter.Year);
                    var endYear   = new GenericYear(endAsQuarter.Year);
                    lessGranularReportingPeriod = new ReportingPeriod <GenericYear>(startYear, endYear);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else
            {
                throw new NotSupportedException("This granularity is not supported: " + reportingPeriodGranularity);
            }

            if (lessGranularReportingPeriod.GetUnitOfTimeGranularity() == granularity)
            {
                return(lessGranularReportingPeriod);
            }

            var result = MakeLessGranular(lessGranularReportingPeriod, granularity);

            return(result);
        }
        private static ReportingPeriod MakeLessGranular(
            this ReportingPeriod reportingPeriod,
            UnitOfTimeGranularity granularity,
            bool throwOnMisalignment = true)
        {
            if (reportingPeriod == null)
            {
                throw new ArgumentNullException(nameof(reportingPeriod));
            }

            if (reportingPeriod.HasComponentWithUnboundedGranularity())
            {
                throw new ArgumentException(Invariant($"{nameof(reportingPeriod)} has an {nameof(UnitOfTimeGranularity.Unbounded)} component."));
            }

            if (granularity == UnitOfTimeGranularity.Invalid)
            {
                throw new ArgumentOutOfRangeException(Invariant($"'{nameof(granularity)}' == '{UnitOfTimeGranularity.Invalid}'"), (Exception)null);
            }

            if (granularity == UnitOfTimeGranularity.Unbounded)
            {
                throw new ArgumentOutOfRangeException(Invariant($"'{nameof(granularity)}' == '{UnitOfTimeGranularity.Unbounded}'"), (Exception)null);
            }

            var reportingPeriodGranularity = reportingPeriod.GetUnitOfTimeGranularity();

            if (reportingPeriodGranularity.IsAsGranularOrLessGranularThan(granularity))
            {
                throw new ArgumentException(Invariant($"{nameof(reportingPeriod)} is as granular or less granular than {nameof(granularity)}."));
            }

            ReportingPeriod lessGranularReportingPeriod;
            var             unitOfTimeKind = reportingPeriod.GetUnitOfTimeKind();

            if (reportingPeriodGranularity == UnitOfTimeGranularity.Day)
            {
                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startAsCalendarDay = reportingPeriod.Start as CalendarDay;

                    // ReSharper disable once PossibleNullReferenceException
                    var startMonth = new CalendarMonth(startAsCalendarDay.Year, startAsCalendarDay.MonthOfYear);
                    if ((startMonth.GetFirstCalendarDay() != startAsCalendarDay) && throwOnMisalignment)
                    {
                        throw new InvalidOperationException("Cannot convert a calendar day reporting period to a calendar month reporting period when the reporting period start time is not the first day of a month.");
                    }

                    var endAsCalendarDay = reportingPeriod.End as CalendarDay;

                    // ReSharper disable once PossibleNullReferenceException
                    var endMonth = new CalendarMonth(endAsCalendarDay.Year, endAsCalendarDay.MonthOfYear);
                    if ((endMonth.GetLastCalendarDay() != endAsCalendarDay) && throwOnMisalignment)
                    {
                        throw new InvalidOperationException("Cannot convert a calendar day reporting period to a calendar month reporting period when the reporting period end time is not the last day of a month.");
                    }

                    lessGranularReportingPeriod = new ReportingPeriod(startMonth, endMonth);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else if (reportingPeriodGranularity == UnitOfTimeGranularity.Month)
            {
                var startAsMonth = reportingPeriod.Start as IHaveAMonth;
                var endAsMonth   = reportingPeriod.End as IHaveAMonth;

                var validStartMonths = new HashSet <int> {
                    1, 4, 7, 10
                };

                // ReSharper disable once PossibleNullReferenceException
                if ((!validStartMonths.Contains((int)startAsMonth.MonthNumber)) && throwOnMisalignment)
                {
                    throw new InvalidOperationException("Cannot convert a monthly reporting period to a quarterly reporting period when the reporting period start time is not the first month of a recognized quarter.");
                }

                var validEndMonths = new HashSet <int> {
                    3, 6, 9, 12
                };

                // ReSharper disable once PossibleNullReferenceException
                if ((!validEndMonths.Contains((int)endAsMonth.MonthNumber)) && throwOnMisalignment)
                {
                    throw new InvalidOperationException("Cannot convert a monthly reporting period to a quarterly reporting period when the reporting period end time is not the last month of a recognized quarter.");
                }

                var monthNumberToQuarterMap = new Dictionary <int, QuarterNumber>
                {
                    { 1, QuarterNumber.Q1 },
                    { 2, QuarterNumber.Q1 },
                    { 3, QuarterNumber.Q1 },
                    { 4, QuarterNumber.Q2 },
                    { 5, QuarterNumber.Q2 },
                    { 6, QuarterNumber.Q2 },
                    { 7, QuarterNumber.Q3 },
                    { 8, QuarterNumber.Q3 },
                    { 9, QuarterNumber.Q3 },
                    { 10, QuarterNumber.Q4 },
                    { 11, QuarterNumber.Q4 },
                    { 12, QuarterNumber.Q4 },
                };

                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startQuarter = new CalendarQuarter(startAsMonth.Year, monthNumberToQuarterMap[(int)startAsMonth.MonthNumber]);

                    var endQuarter = new CalendarQuarter(endAsMonth.Year, monthNumberToQuarterMap[(int)endAsMonth.MonthNumber]);

                    lessGranularReportingPeriod = new ReportingPeriod(startQuarter, endQuarter);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Fiscal)
                {
                    var startQuarter = new FiscalQuarter(startAsMonth.Year, monthNumberToQuarterMap[(int)startAsMonth.MonthNumber]);

                    var endQuarter = new FiscalQuarter(endAsMonth.Year, monthNumberToQuarterMap[(int)endAsMonth.MonthNumber]);

                    lessGranularReportingPeriod = new ReportingPeriod(startQuarter, endQuarter);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Generic)
                {
                    var startQuarter = new GenericQuarter(startAsMonth.Year, monthNumberToQuarterMap[(int)startAsMonth.MonthNumber]);

                    var endQuarter = new GenericQuarter(endAsMonth.Year, monthNumberToQuarterMap[(int)endAsMonth.MonthNumber]);

                    lessGranularReportingPeriod = new ReportingPeriod(startQuarter, endQuarter);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else if (reportingPeriodGranularity == UnitOfTimeGranularity.Quarter)
            {
                var startAsQuarter = reportingPeriod.Start as IHaveAQuarter;
                var endAsQuarter   = reportingPeriod.End as IHaveAQuarter;

                // ReSharper disable once PossibleNullReferenceException
                if ((startAsQuarter.QuarterNumber != QuarterNumber.Q1) && throwOnMisalignment)
                {
                    throw new InvalidOperationException("Cannot convert a quarterly reporting period to a yearly reporting period when the reporting period start time is not Q1.");
                }

                // ReSharper disable once PossibleNullReferenceException
                if ((endAsQuarter.QuarterNumber != QuarterNumber.Q4) && throwOnMisalignment)
                {
                    throw new InvalidOperationException("Cannot convert a quarterly reporting period to a yearly reporting period when the reporting period end is not Q4.");
                }

                if (unitOfTimeKind == UnitOfTimeKind.Calendar)
                {
                    var startYear = new CalendarYear(startAsQuarter.Year);

                    var endYear = new CalendarYear(endAsQuarter.Year);

                    lessGranularReportingPeriod = new ReportingPeriod(startYear, endYear);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Fiscal)
                {
                    var startYear = new FiscalYear(startAsQuarter.Year);

                    var endYear = new FiscalYear(endAsQuarter.Year);

                    lessGranularReportingPeriod = new ReportingPeriod(startYear, endYear);
                }
                else if (unitOfTimeKind == UnitOfTimeKind.Generic)
                {
                    var startYear = new GenericYear(startAsQuarter.Year);

                    var endYear = new GenericYear(endAsQuarter.Year);

                    lessGranularReportingPeriod = new ReportingPeriod(startYear, endYear);
                }
                else
                {
                    throw new NotSupportedException("This kind of unit-of-time is not supported: " + unitOfTimeKind);
                }
            }
            else
            {
                throw new NotSupportedException("This granularity is not supported: " + reportingPeriodGranularity);
            }

            if (lessGranularReportingPeriod.GetUnitOfTimeGranularity() == granularity)
            {
                return(lessGranularReportingPeriod);
            }

            var result = MakeLessGranular(lessGranularReportingPeriod, granularity, throwOnMisalignment);

            return(result);
        }