private OffsetDateTime CreateUpdatedOffsetDateTimeFromTokens(OffsetDateTime existingOffsetDateTime, DateToken dateToken, TimeToken timeToken)
        {
            var existingLocalDateTime = existingOffsetDateTime.LocalDateTime;
            var day = existingLocalDateTime.Day;
            var month = existingLocalDateTime.Month;
            var year = existingLocalDateTime.Year;
            if (dateToken != null)
            {
                if (dateToken.Day.HasValue) day = dateToken.Day.Value;
                if (dateToken.Month.HasValue) month = dateToken.Month.Value;
                if (dateToken.Year.HasValue) year = dateToken.Year.Value;
            }
            var hour = 12;
            var minute = 0;
            if (timeToken != null)
            {
                hour = timeToken.Hour;
                if (timeToken.Meridiem == Meridiem.AM && hour == 12) hour = 0;
                if (timeToken.Meridiem == Meridiem.PM && hour < 12) hour += 12;
                minute = timeToken.Minute ?? 0;
            }

            try
            {
                return new LocalDateTime(year, month, day, hour, minute).WithOffset(existingOffsetDateTime.Offset);
            }
            catch (ArgumentOutOfRangeException)
            {
                throw new InvalidTokenValueException(ErrorMessages.InvalidDateTime);
            }
        }
        private Answer GetAnswerToTimeConversionQuestion(Question question, DateToken knownDateToken, TimeToken knownTimeToken, CityOrTimezoneToken  knownCityOrTimezone, CityOrTimezoneToken  unknownCityOrTimezone)
        {
            var knownEntityOffsetDateTime = CreateUpdatedOffsetDateTimeFromTokens(knownCityOrTimezone.GetCurrentTimeAsOffsetDateTime(), knownDateToken, knownTimeToken);
            Instant knownCityOrTimezoneInstant;
            String note = null;
            if (knownCityOrTimezone.GetType() == typeof(CityToken))
            {
                var cityToken = (CityToken)knownCityOrTimezone;
                ZoneLocalMapping knownCityOrTimezoneZoneLocalMapping = DateTimeZoneProviders.Tzdb[cityToken.City.Timezone].MapLocal(knownEntityOffsetDateTime.LocalDateTime);
                if (knownCityOrTimezoneZoneLocalMapping.Count == 1)
                {
                    knownCityOrTimezoneInstant = knownCityOrTimezoneZoneLocalMapping.Single().ToInstant();
                }
                else if (knownCityOrTimezoneZoneLocalMapping.Count > 1)
                {
                    knownCityOrTimezoneInstant = knownCityOrTimezoneZoneLocalMapping.First().ToInstant();
                    note = String.Format("In {0} the time {1} occurs twice due to daylight saving time changes when clocks are put back, we are using the first occurance of that time.",
                        cityToken.City.FormattedName,
                        knownEntityOffsetDateTime.LocalDateTime.GetFormattedTimeAndDate());
                }
                else
                {
                    knownCityOrTimezoneInstant = knownCityOrTimezoneZoneLocalMapping.LateInterval.Start.WithOffset(knownCityOrTimezoneZoneLocalMapping.LateInterval.WallOffset).ToInstant();
                    note = String.Format("In {0} the time {1} does not occur due to daylight saving time changes when clocks are put forward, we are using the next valid time.",
                        cityToken.City.FormattedName,
                        knownEntityOffsetDateTime.LocalDateTime.GetFormattedTimeAndDate());
                }
            }
            else if (knownCityOrTimezone.GetType() == typeof(TimezoneToken))
            {
                var timezoneToken = (TimezoneToken)knownCityOrTimezone;
                knownCityOrTimezoneInstant = knownEntityOffsetDateTime.LocalDateTime.WithOffset(timezoneToken.Timezone.Offset).ToInstant();
            }
            else
            {
                throw new NotImplementedException();
            }

            var answerText = String.Format("It is {0} in {1} when it is {2} in {3}",
                                            unknownCityOrTimezone.GetLocalDateTime(knownCityOrTimezoneInstant).GetFormattedTimeAndDate(),
                                            unknownCityOrTimezone.GetFormattedNameAndTimezone(knownCityOrTimezoneInstant),
                                            knownEntityOffsetDateTime.LocalDateTime.GetFormattedTimeAndDate(),
                                            knownCityOrTimezone.GetFormattedNameAndTimezone(knownEntityOffsetDateTime.ToInstant()));

            var answer = new Answer(question, true, true, answerText);
            answer.Note = note;
            return answer;
        }
        private IList<Token> GetDayMonthYearFormattedDates(Question question)
        {
            var tokens = new List<Token>();
            var monthNamesRegex = "(jan(uary)?|febr(uary)?|mar(ch)?|apr(il)?|may|jun(e)?|jul(y)?|aug(ust)?|sep(tember)?|oct(ober)?|nov(ember)?|dec(ember)?)";
            var matches = Regex.Matches(question.QuestionText, @"(?<day>\d{1,2})(th|st|nd|rd)?(\sof|/|-)?\s?(?<month_name>" + monthNamesRegex + @")((/|-|\s)(?<year>\d{2,4}))?", RegexOptions.IgnoreCase);
            foreach (Match match in matches)
            {
                Group group = match.Groups[0];
                var monthNameMatch = match.Groups["month_name"];
                var dayMatch = match.Groups["day"];
                var yearMatch = match.Groups["year"];

                var month = GetMonth(monthNameMatch.Value);
                var day = int.Parse(dayMatch.Value);
                int? year = null;
                if(!string.IsNullOrEmpty(yearMatch.Value)) year = int.Parse(yearMatch.Value);
                year = MakeYearInThisCenturyIfOnly2Digits(year);
                var tokenResult = new DateToken(group.Value, group.Index, day, month, year);
                tokens.Add(tokenResult);
            }
            return tokens;
        }
        private IList<Token> GetMonthDayYearFormattedDates(Question question)
        {
            var tokens = new List<Token>();
            var monthNamesRegex = "(jan(uary)?|feb(ruary)?|mar(ch)?|apr(il)?|may|jun(e)?|jul(y)?|aug(ust)?|sep(tember)?|oct(ober)?|nov(ember)?|dec(ember)?)";
            var matches = Regex.Matches(question.QuestionText, @"(?<month_name>" + monthNamesRegex + @")(\sthe\s|,\s|,|\s)(?<first_number>\d{1,4})(th|st|nd|rd)?(?<second_number>\s?\d{4})?", RegexOptions.IgnoreCase);
            foreach (Match match in matches)
            {
                Group group = match.Groups[0];
                var monthNameMatch = match.Groups["month_name"];
                var firstNumberMatch = match.Groups["first_number"];
                var secondNumberMatch = match.Groups["second_number"];

                var month = GetMonth(monthNameMatch.Value);
                int? firstNumber = int.Parse (firstNumberMatch.Value);
                int? secondNumber = null;
                if(!string.IsNullOrEmpty (secondNumberMatch.Value)) secondNumber = int.Parse(secondNumberMatch.Value);
                int? day = null;
                int? year = null;
                if(secondNumber.HasValue)
                {
                    day = firstNumber;
                    year = secondNumber;
                }
                else
                {
                    if(firstNumber <= 31){
                        day = firstNumber;
                    }
                    else{
                        year = firstNumber;
                    }
                }
                year = MakeYearInThisCenturyIfOnly2Digits(year);
                var tokenResult = new DateToken(group.Value, monthNameMatch.Index, day, month, year);
                tokens.Add(tokenResult);
            }
            return tokens;
        }
        private IList<Token> GetHyphenAndSlashFormattedDates(Question question)
        {
            var tokens = new List<Token>();
            var matches = Regex.Matches(question.QuestionText, @"(?<first_number>\d{1,2})(-|/)(?<second_number>\d{1,2})(-|/)(?<year>\d{2,4})", RegexOptions.IgnoreCase);
            foreach (Match match in matches)
            {
                Group group = match.Groups[0];
                var yearMatch = match.Groups["year"];
                var firstNumberMatch = match.Groups["first_number"];
                var secondNumberMatch = match.Groups["second_number"];

                int? year = int.Parse(yearMatch.Value);
                var firstNumber = int.Parse(firstNumberMatch.Value);
                var secondNumber = int.Parse(secondNumberMatch.Value);
                int day = secondNumber;
                int month = firstNumber;
                if(firstNumber > 12 && secondNumber <= 12){
                    day = firstNumber;
                    month = secondNumber;
                }
                year = MakeYearInThisCenturyIfOnly2Digits(year);
                var tokenResult = new DateToken(group.Value, yearMatch.Index, day, month, year);
                tokens.Add(tokenResult);
            }
            return tokens;
        }
 private IList<Token> GetYearOnlyDates(Question question)
 {
     var tokens = new List<Token>();
     var matches = Regex.Matches(question.QuestionText, @"\s(?<year>\d{4})(\s|$)", RegexOptions.IgnoreCase);
     foreach (Match match in matches)
     {
         Group yearMatch = match.Groups["year"];
         var year = int.Parse (yearMatch.Value);
         var tokenResult = new DateToken(yearMatch.Value, yearMatch.Index, null, null, year);
         tokens.Add(tokenResult);
     }
     return tokens;
 }