private ParseResult <AnnualDate>?DetermineMonth(PatternFields usedFields, string text) { switch (usedFields & (PatternFields.MonthOfYearNumeric | PatternFields.MonthOfYearText)) { case PatternFields.MonthOfYearNumeric: // No-op break; case PatternFields.MonthOfYearText: MonthOfYearNumeric = MonthOfYearText; break; case PatternFields.MonthOfYearNumeric | PatternFields.MonthOfYearText: if (MonthOfYearNumeric != MonthOfYearText) { return(ParseResult <AnnualDate> .InconsistentMonthValues(text)); } // No need to change MonthOfYearNumeric - this was just a check break; case 0: MonthOfYearNumeric = TemplateValue.Month; break; } if (MonthOfYearNumeric > CalendarSystem.Iso.GetMonthsInYear(2000)) { return(ParseResult <AnnualDate> .IsoMonthOutOfRange(text, MonthOfYearNumeric)); } return(null); }
private ParseResult <LocalDate> DetermineMonth(PatternFields usedFields) { switch (usedFields & (PatternFields.MonthOfYearNumeric | PatternFields.MonthOfYearText)) { case PatternFields.MonthOfYearNumeric: // No-op break; case PatternFields.MonthOfYearText: MonthOfYearNumeric = MonthOfYearText; break; case PatternFields.MonthOfYearNumeric | PatternFields.MonthOfYearText: if (MonthOfYearNumeric != MonthOfYearText) { return(ParseResult <LocalDate> .InconsistentMonthValues); } // No need to change MonthOfYearNumeric - this was just a check break; case 0: MonthOfYearNumeric = templateValue.Month; break; } if (MonthOfYearNumeric > Calendar.GetMaxMonth(Year)) { return(ParseResult <LocalDate> .MonthOutOfRange(MonthOfYearNumeric, Year)); } return(null); }
internal override ParseResult <LocalDate> CalculateValue(PatternFields usedFields) { // This will set Year if necessary ParseResult <LocalDate> failure = DetermineYear(usedFields); if (failure != null) { return(failure); } // This will set MonthOfYearNumeric if necessary failure = DetermineMonth(usedFields); if (failure != null) { return(failure); } int day = IsFieldUsed(usedFields, PatternFields.DayOfMonth) ? DayOfMonth : templateValue.Day; if (day > Calendar.GetDaysInMonth(Year, MonthOfYearNumeric)) { return(ParseResult <LocalDate> .DayOfMonthOutOfRange(day, MonthOfYearNumeric, Year)); } LocalDate value = new LocalDate(Year, MonthOfYearNumeric, day, Calendar); if (IsFieldUsed(usedFields, PatternFields.DayOfWeek) && DayOfWeek != value.DayOfWeek) { return(ParseResult <LocalDate> .InconsistentDayOfWeekTextValue); } return(ParseResult <LocalDate> .ForValue(value)); }
public SteppedPattern(NodaAction <TResult, StringBuilder> formatActions, ParseAction[] parseActions, NodaFunc <TBucket> bucketProvider, PatternFields usedFields) { this.formatActions = formatActions; this.parseActions = parseActions; this.bucketProvider = bucketProvider; this.usedFields = usedFields; }
public Action <TResult, StringBuilder> BuildFormatAction(PatternFields finalFields) { bool genitive = (finalFields & PatternFields.DayOfMonth) != 0; IList <string> textValues = count == 3 ? (genitive ? formatInfo.ShortMonthGenitiveNames : formatInfo.ShortMonthNames) : (genitive ? formatInfo.LongMonthGenitiveNames : formatInfo.LongMonthNames); return((value, sb) => sb.Append(textValues[getter(value)])); }
internal override ParseResult <ZonedDateTime> CalculateValue(PatternFields usedFields, string text) { var localResult = LocalDateTimePatternParser.LocalDateTimeParseBucket.CombineBuckets(usedFields, Date, Time, text); if (!localResult.Success) { return(localResult.ConvertError <ZonedDateTime>()); } var localDateTime = localResult.Value; // No offset - so just use the resolver if ((usedFields & PatternFields.EmbeddedOffset) == 0) { try { return(ParseResult <ZonedDateTime> .ForValue(Zone.ResolveLocal(localDateTime, resolver))); } catch (SkippedTimeException) { return(ParseResult <ZonedDateTime> .SkippedLocalTime(text)); } catch (AmbiguousTimeException) { return(ParseResult <ZonedDateTime> .AmbiguousLocalTime(text)); } } // We were given an offset, so we can resolve and validate using that var mapping = Zone.MapLocal(localDateTime); ZonedDateTime result; switch (mapping.Count) { // If the local time was skipped, the offset has to be invalid. case 0: return(ParseResult <ZonedDateTime> .InvalidOffset(text)); case 1: result = mapping.First(); // We'll validate in a minute break; case 2: result = mapping.First().Offset == Offset?mapping.First() : mapping.Last(); break; default: throw new InvalidOperationException("Mapping has count outside range 0-2; should not happen."); } if (result.Offset != Offset) { return(ParseResult <ZonedDateTime> .InvalidOffset(text)); } return(ParseResult <ZonedDateTime> .ForValue(result)); }
/// <summary> /// Registers that a pattern field has been used in this pattern, and throws a suitable error /// result if it's already been used. /// </summary> internal void AddField(PatternFields field, char characterInPattern) { PatternFields newUsedFields = usedFields | field; if (newUsedFields == usedFields) { throw new InvalidPatternException(TextErrorMessages.RepeatedFieldInPattern, characterInPattern); } usedFields = newUsedFields; }
internal override ParseResult <OffsetDate> CalculateValue(PatternFields usedFields, string text) { ParseResult <LocalDate> dateResult = Date.CalculateValue(usedFields & PatternFields.AllDateFields, text); if (!dateResult.Success) { return(dateResult.ConvertError <OffsetDate>()); } LocalDate date = dateResult.Value; return(ParseResult <OffsetDate> .ForValue(date.WithOffset(Offset))); }
private static CharacterHandler <Duration, DurationParseBucket> CreatePartialHandler (PatternFields field, long ticksPerUnit, int unitsPerContainer) { return((pattern, builder) => { int count = pattern.GetRepeatCount(2); builder.AddField(field, pattern.Current); builder.AddParseValueAction(count, 2, pattern.Current, 0, unitsPerContainer - 1, (bucket, value) => bucket.NegativeTicks -= value * ticksPerUnit); builder.AddFormatLeftPad(count, duration => (int)((GetPositiveTicks(duration) / (ulong)ticksPerUnit) % (uint)unitsPerContainer)); }); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <Offset> CalculateValue(PatternFields usedFields, string text) { int seconds = Hours * NodaConstants.SecondsPerHour + Minutes * NodaConstants.SecondsPerMinute + Seconds; if (IsNegative) { seconds = -seconds; } return(ParseResult <Offset> .ForValue(Offset.FromSeconds(seconds))); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <Duration> CalculateValue(PatternFields usedFields, string text) { if (IsNegative) { currentNanos = -currentNanos; } if (currentNanos < Duration.MinNanoseconds || currentNanos > Duration.MaxNanoseconds) { return(ParseResult <Duration> .ForInvalidValuePostParse(text, TextErrorMessages.OverallValueOutOfRange, typeof(Duration))); } return(ParseResult <Duration> .ForValue(Duration.FromNanoseconds(currentNanos))); }
internal override ParseResult <OffsetDateTime> CalculateValue(PatternFields usedFields, string text) { var localResult = LocalDateTimePatternParser.LocalDateTimeParseBucket.CombineBuckets(usedFields, Date, Time, text); if (!localResult.Success) { return(localResult.ConvertError <OffsetDateTime>()); } var localDateTime = localResult.Value; return(ParseResult <OffsetDateTime> .ForValue(localDateTime.WithOffset(Offset))); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <Offset> CalculateValue(PatternFields usedFields) { int milliseconds = Hours * NodaConstants.MillisecondsPerHour + Minutes * NodaConstants.MillisecondsPerMinute + Seconds * NodaConstants.MillisecondsPerSecond + Milliseconds; if (IsNegative) { milliseconds = -milliseconds; } return(ParseResult <Offset> .ForValue(Offset.FromMilliseconds(milliseconds))); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <Duration> CalculateValue(PatternFields usedFields, string text) { if (IsNegative) { currentNanos = -currentNanos; } if (currentNanos < MinNanos || currentNanos > MaxNanos) { // TODO: Work out whether this is really the best message. (Created a new one...) return(ParseResult <Duration> .ForInvalidValuePostParse(text, Messages.Parse_OverallValueOutOfRange, typeof(Duration))); } return(ParseResult <Duration> .ForValue(Duration.FromNanoseconds(currentNanos))); }
private static CharacterHandler <Duration, DurationParseBucket> CreatePartialHandler (PatternFields field, long nanosecondsPerUnit, int unitsPerContainer) { return((pattern, builder) => { int count = pattern.GetRepeatCount(2); builder.AddField(field, pattern.Current); builder.AddParseValueAction(count, 2, pattern.Current, 0, unitsPerContainer - 1, (bucket, value) => bucket.AddUnits(value, nanosecondsPerUnit)); // This is never used for anything larger than a day, so the day part is irrelevant. builder.AddFormatLeftPad(count, duration => (int)(((Math.Abs(duration.NanosecondOfDay) / nanosecondsPerUnit)) % unitsPerContainer), assumeNonNegative: true, assumeFitsInCount: count == 2); }); }
private static CharacterHandler <Duration, DurationParseBucket> CreateTotalHandler (PatternFields field, long ticksPerUnit) { return((pattern, builder) => { int count = pattern.GetRepeatCount(10); // AddField would throw an inappropriate exception here, so handle it specially. if ((builder.UsedFields & PatternFields.TotalDuration) != 0) { throw new InvalidPatternException(Messages.Parse_MultipleCapitalDurationFields); } builder.AddField(field, pattern.Current); builder.AddField(PatternFields.TotalDuration, pattern.Current); builder.AddParseValueAction(count, 10, pattern.Current, 0, int.MaxValue, (bucket, value) => bucket.NegativeTicks -= value * ticksPerUnit); builder.AddFormatLeftPad(count, duration => (int)(GetPositiveTicks(duration) / (ulong)ticksPerUnit)); }); }
/// <summary> /// Validates the combination of fields used. /// </summary> internal void ValidateUsedFields() { // We assume invalid combinations are global across all parsers. The way that // the patterns are parsed ensures we never end up with any invalid individual fields // (e.g. time fields within a date pattern). if ((usedFields & (PatternFields.Era | PatternFields.YearOfEra)) == PatternFields.Era) { throw new InvalidPatternException(TextErrorMessages.EraWithoutYearOfEra); } const PatternFields calendarAndEra = PatternFields.Era | PatternFields.Calendar; if ((usedFields & calendarAndEra) == calendarAndEra) { throw new InvalidPatternException(TextErrorMessages.CalendarAndEra); } }
private ParseResult <LocalTime> DetermineHour(PatternFields usedFields, string text, out int hour) { hour = 0; if (usedFields.HasAny(PatternFields.Hours24)) { if (usedFields.HasAll(PatternFields.Hours12 | PatternFields.Hours24)) { if (Hours12 % 12 != Hours24 % 12) { return(ParseResult <LocalTime> .InconsistentValues(text, 'H', 'h')); } } if (usedFields.HasAny(PatternFields.AmPm)) { if (Hours24 / 12 != AmPm) { return(ParseResult <LocalTime> .InconsistentValues(text, 'H', 't')); } } hour = Hours24; return(null); } // Okay, it's definitely valid - but we've still got 8 possibilities for what's been specified. switch (usedFields & (PatternFields.Hours12 | PatternFields.AmPm)) { case PatternFields.Hours12 | PatternFields.AmPm: hour = (Hours12 % 12) + AmPm * 12; break; case PatternFields.Hours12: // Preserve AM/PM from template value hour = (Hours12 % 12) + (TemplateValue.Hour / 12) * 12; break; case PatternFields.AmPm: // Preserve 12-hour hour of day from template value, use specified AM/PM hour = (TemplateValue.Hour % 12) + AmPm * 12; break; case 0: hour = TemplateValue.Hour; break; } return(null); }
private static CharacterHandler <Duration, DurationParseBucket> CreateTotalHandler (PatternFields field, long nanosecondsPerUnit, int unitsPerDay, long maxValue) { return((pattern, builder) => { // Needs to be big enough for 1449551462400 seconds int count = pattern.GetRepeatCount(13); // AddField would throw an inappropriate exception here, so handle it specially. if ((builder.UsedFields & PatternFields.TotalDuration) != 0) { throw new InvalidPatternException(TextErrorMessages.MultipleCapitalDurationFields); } builder.AddField(field, pattern.Current); builder.AddField(PatternFields.TotalDuration, pattern.Current); builder.AddParseInt64ValueAction(count, 13, pattern.Current, 0, maxValue, (bucket, value) => bucket.AddUnits(value, nanosecondsPerUnit)); builder.AddFormatAction((value, sb) => FormatHelper.LeftPadNonNegativeInt64(GetPositiveNanosecondUnits(value, nanosecondsPerUnit, unitsPerDay), count, sb)); }); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <LocalTime> CalculateValue(PatternFields usedFields, string text) { if (AmPm == 2) { AmPm = templateValue.Hour / 12; } int hour; ParseResult <LocalTime> failure = DetermineHour(usedFields, text, out hour); if (failure != null) { return(failure); } int minutes = IsFieldUsed(usedFields, PatternFields.Minutes) ? Minutes : templateValue.Minute; int seconds = IsFieldUsed(usedFields, PatternFields.Seconds) ? Seconds : templateValue.Second; int fraction = IsFieldUsed(usedFields, PatternFields.FractionalSeconds) ? FractionalSeconds : templateValue.TickOfSecond; return(ParseResult <LocalTime> .ForValue(LocalTime.FromHourMinuteSecondTick(hour, minutes, seconds, fraction))); }
public SteppedPattern(Action <TResult, StringBuilder> formatActions, ParseAction[] parseActions, Func <TBucket> bucketProvider, PatternFields usedFields, TResult sample) { this.formatActions = formatActions; this.parseActions = parseActions; this.bucketProvider = bucketProvider; this.usedFields = usedFields; // Format the sample value to work out the expected length, so we // can use that when creating a StringBuilder. This will definitely not always // be appropriate, but it's a start. StringBuilder builder = new StringBuilder(); formatActions(sample, builder); expectedLength = builder.Length; }
internal override ParseResult <AnnualDate> CalculateValue(PatternFields usedFields, string text) { // This will set MonthOfYearNumeric if necessary var failure = DetermineMonth(usedFields, text); if (failure != null) { return(failure); } int day = usedFields.HasAny(PatternFields.DayOfMonth) ? DayOfMonth : TemplateValue.Day; // Validate for the year 2000, just like the AnnualDate constructor does. if (day > CalendarSystem.Iso.GetDaysInMonth(2000, MonthOfYearNumeric)) { return(ParseResult <AnnualDate> .DayOfMonthOutOfRangeNoYear(text, day, MonthOfYearNumeric)); } return(ParseResult <AnnualDate> .ForValue(new AnnualDate(MonthOfYearNumeric, day))); }
/// <summary> /// Combines the values in a date bucket with the values in a time bucket. /// </summary> /// <remarks> /// This would normally be the <see cref="CalculateValue"/> method, but we want /// to be able to use the same logic when parsing an <see cref="OffsetDateTime"/> /// and <see cref="ZonedDateTime"/>. /// </remarks> internal static ParseResult <LocalDateTime> CombineBuckets( PatternFields usedFields, LocalDatePatternParser.LocalDateParseBucket dateBucket, LocalTimePatternParser.LocalTimeParseBucket timeBucket, string text) { // Handle special case of hour = 24 bool hour24 = false; if (timeBucket.Hours24 == 24) { timeBucket.Hours24 = 0; hour24 = true; } ParseResult <LocalDate> dateResult = dateBucket.CalculateValue(usedFields & PatternFields.AllDateFields, text); if (!dateResult.Success) { return(dateResult.ConvertError <LocalDateTime>()); } ParseResult <LocalTime> timeResult = timeBucket.CalculateValue(usedFields & PatternFields.AllTimeFields, text); if (!timeResult.Success) { return(timeResult.ConvertError <LocalDateTime>()); } LocalDate date = dateResult.Value; LocalTime time = timeResult.Value; if (hour24) { if (time != LocalTime.Midnight) { return(ParseResult <LocalDateTime> .InvalidHour24(text)); } date = date.PlusDays(1); } return(ParseResult <LocalDateTime> .ForValue(date + time)); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <LocalTime> CalculateValue(PatternFields usedFields, string text) { if (usedFields.HasAny(PatternFields.EmbeddedTime)) { return(ParseResult <LocalTime> .ForValue(LocalTime.FromHourMinuteSecondNanosecond(Hours24, Minutes, Seconds, FractionalSeconds))); } if (AmPm == 2) { AmPm = TemplateValue.Hour / 12; } ParseResult <LocalTime> failure = DetermineHour(usedFields, text, out int hour); if (failure != null) { return(failure); } int minutes = usedFields.HasAny(PatternFields.Minutes) ? Minutes : TemplateValue.Minute; int seconds = usedFields.HasAny(PatternFields.Seconds) ? Seconds : TemplateValue.Second; int fraction = usedFields.HasAny(PatternFields.FractionalSeconds) ? FractionalSeconds : TemplateValue.NanosecondOfSecond; return(ParseResult <LocalTime> .ForValue(LocalTime.FromHourMinuteSecondNanosecond(hour, minutes, seconds, fraction))); }
internal override ParseResult <LocalDate> CalculateValue(PatternFields usedFields, string text) { if (usedFields.HasAny(PatternFields.EmbeddedDate)) { return(ParseResult <LocalDate> .ForValue(new LocalDate(Year, MonthOfYearNumeric, DayOfMonth, Calendar))); } // This will set Year if necessary ParseResult <LocalDate> failure = DetermineYear(usedFields, text); if (failure != null) { return(failure); } // This will set MonthOfYearNumeric if necessary failure = DetermineMonth(usedFields, text); if (failure != null) { return(failure); } int day = usedFields.HasAny(PatternFields.DayOfMonth) ? DayOfMonth : TemplateValue.Day; if (day > Calendar.GetDaysInMonth(Year, MonthOfYearNumeric)) { return(ParseResult <LocalDate> .DayOfMonthOutOfRange(text, day, MonthOfYearNumeric, Year)); } LocalDate value = new LocalDate(Year, MonthOfYearNumeric, day, Calendar); if (usedFields.HasAny(PatternFields.DayOfWeek) && DayOfWeek != value.DayOfWeek) { return(ParseResult <LocalDate> .InconsistentDayOfWeekTextValue(text)); } // FIXME: If we got an era, check that the resulting date really lies within that era. return(ParseResult <LocalDate> .ForValue(value)); }
internal override ParseResult <LocalDateTime> CalculateValue(PatternFields usedFields, string text) => CombineBuckets(usedFields, Date, Time, text);
private ParseResult <LocalDate> DetermineYear(PatternFields usedFields) { int yearFromEra = 0; if (IsFieldUsed(usedFields, PatternFields.YearOfEra)) { // Odd to have a year-of-era without era, but it's valid... if (!IsFieldUsed(usedFields, PatternFields.Era)) { EraIndex = Calendar.Eras.IndexOf(templateValue.Era); } // Find the absolute year from the year-of-era and era if (YearOfEra < Calendar.GetMinYearOfEra(EraIndex) || YearOfEra > Calendar.GetMaxYearOfEra(EraIndex)) { return(ParseResult <LocalDate> .YearOfEraOutOfRange(YearOfEra, EraIndex, Calendar)); } yearFromEra = Calendar.GetAbsoluteYear(YearOfEra, EraIndex); } // Note: we can't have YearTwoDigits without Year, hence there are only 6 options here rather than 8. switch (usedFields & (PatternFields.Year | PatternFields.YearOfEra | PatternFields.YearTwoDigits)) { case PatternFields.Year: // Fine, we'll just use the Year value we've been provided break; case PatternFields.Year | PatternFields.YearTwoDigits: Year = GetAbsoluteYearFromTwoDigits(templateValue.Year, Year); break; case PatternFields.YearOfEra: Year = yearFromEra; break; case PatternFields.YearOfEra | PatternFields.Year | PatternFields.YearTwoDigits: // We've been given a year of era, but only a two digit year. The year of era // takes precedence, so we just check that the two digits are correct. // This is a pretty bizarre situation... if ((Math.Abs(yearFromEra) % 100) != Year) { return(ParseResult <LocalDate> .InconsistentValues('y', 'Y')); } Year = yearFromEra; break; case PatternFields.YearOfEra | PatternFields.Year: if (Year != yearFromEra) { return(ParseResult <LocalDate> .InconsistentValues('y', 'Y')); } Year = yearFromEra; break; case 0: Year = templateValue.Year; break; // No default: it would be impossible. } if (Year > Calendar.MaxYear || Year < Calendar.MinYear) { // The field can't be YearOfEra, as we've already validated that earlier. return(ParseResult <LocalDate> .FieldValueOutOfRange(Year, 'y')); } return(null); }
internal override ParseResult <LocalDateTime> CalculateValue(PatternFields usedFields) { return(CombineBuckets(usedFields, Date, Time)); }
/// <summary> /// Returns a handler for a zero-padded purely-numeric field specifier, such as "seconds", "minutes", "24-hour", "12-hour" etc. /// </summary> /// <param name="maxCount">Maximum permissable count (usually two)</param> /// <param name="field">Field to remember that we've seen</param> /// <param name="minValue">Minimum valid value for the field (inclusive)</param> /// <param name="maxValue">Maximum value value for the field (inclusive)</param> /// <param name="getter">Delegate to retrieve the field value when formatting</param> /// <param name="setter">Delegate to set the field value into a bucket when parsing</param> /// <returns>The pattern parsing failure, or null on success.</returns> internal static CharacterHandler <TResult, TBucket> HandlePaddedField(int maxCount, PatternFields field, int minValue, int maxValue, Func <TResult, int> getter, Action <TBucket, int> setter) { return((pattern, builder) => { int count = pattern.GetRepeatCount(maxCount); builder.AddField(field, pattern.Current); builder.AddParseValueAction(count, maxCount, pattern.Current, minValue, maxValue, setter); builder.AddFormatLeftPad(count, getter, assumeNonNegative: minValue >= 0, assumeFitsInCount: count == maxCount); }); }
/// <summary> /// Returns true if the given set of fields contains all of the target fields. /// </summary> internal static bool HasAll(this PatternFields fields, PatternFields target) => (fields & target) == target;
internal override ParseResult <LocalDate> CalculateValue(PatternFields usedFields, string value) { throw new NotImplementedException(); }
/// <summary> /// Returns true if the given set of fields contains any of the target fields. /// </summary> internal static bool HasAny(this PatternFields fields, PatternFields target) => (fields & target) != 0;