public ParseResult <Offset> ParsePartial(ValueCursor cursor) { int startIndex = cursor.Index; // TODO: Do better than this. It's horrible, and may well be invalid // for some cultures. Or just remove the NumberPattern from 2.0... int longestPossible = Math.Min(maxLength, cursor.Length - cursor.Index); for (int length = longestPossible; length >= 0; length--) { string candidate = cursor.Value.Substring(cursor.Index, length); int milliseconds; if (Int32.TryParse(candidate, NumberStyles.Integer | NumberStyles.AllowThousands, formatInfo.NumberFormat, out milliseconds)) { if (milliseconds < -NodaConstants.MillisecondsPerStandardDay || NodaConstants.MillisecondsPerStandardDay < milliseconds) { cursor.Move(startIndex); return(ParseResult <Offset> .ValueOutOfRange(cursor, milliseconds)); } cursor.Move(cursor.Index + length); return(ParseResult <Offset> .ForValue(Offset.FromMilliseconds(milliseconds))); } } cursor.Move(startIndex); return(ParseResult <Offset> .CannotParseValue(cursor, "n")); }
public ParseResult <TTarget> Convert <TTarget>([NotNull] Func <T, TTarget> projection) { Preconditions.CheckNotNull(projection, nameof(projection)); return(Success ? ParseResult <TTarget> .ForValue(projection(Value)) : new ParseResult <TTarget>(exceptionProvider, ContinueAfterErrorWithMultipleFormats)); }
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 ParseResult <Offset> ParsePartial(ValueCursor cursor) { if (cursor.Current == 'Z') { cursor.MoveNext(); return(ParseResult <Offset> .ForValue(Offset.Zero)); } return(fullPattern.ParsePartial(cursor)); }
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)); }
protected override ParseResult <Instant> ParseImpl(string value) { long number; if (Int64.TryParse(value, ParsingNumberStyles, FormatInfo.NumberFormat, out number)) { return(ParseResult <Instant> .ForValue(new Instant(number))); } return(ParseResult <Instant> .CannotParseValue(value, patternText)); }
/// <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))); }
public ParseResult <Instant> Parse(string text) { if (text == minLabel) { return(ParseResult <Instant> .ForValue(Instant.MinValue)); } if (text == maxLabel) { return(ParseResult <Instant> .ForValue(Instant.MaxValue)); } return(pattern.Parse(text).Convert(local => new Instant(local.LocalInstant.Ticks))); }
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))); }
/// <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 < 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 <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))); }
public ParseResult <Offset> Parse(string text) { int milliseconds; if (Int32.TryParse(text, NumberStyles.Integer | NumberStyles.AllowThousands, formatInfo.NumberFormat, out milliseconds)) { if (milliseconds < -NodaConstants.MillisecondsPerStandardDay || NodaConstants.MillisecondsPerStandardDay < milliseconds) { return(ParseResult <Offset> .ValueOutOfRange(new ValueCursor(text), milliseconds)); } return(ParseResult <Offset> .ForValue(Offset.FromMilliseconds(milliseconds))); } return(ParseResult <Offset> .CannotParseValue(new ValueCursor(text), "n")); }
/// <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))); }
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)); }
/// <summary> /// Calculates the value from the parsed pieces. /// </summary> internal override ParseResult <Duration> CalculateValue(PatternFields usedFields, string text) { return(ParseResult <Duration> .ForValue(Duration.FromTicks(IsNegative ? NegativeTicks : -NegativeTicks))); }
/// <summary> /// Converts this result to a new target type, either by executing the given projection /// for a success result, or propagating the exception provider for failure. /// </summary> internal ParseResult <TTarget> Convert <TTarget>(NodaFunc <T, TTarget> projection) { return(Success ? ParseResult <TTarget> .ForValue(projection(Value)) : new ParseResult <TTarget>(exceptionProvider, continueWithMultiple)); }
/// <summary> /// Converts this result to a new target type, either by executing the given projection /// for a success result, or propagating the exception provider for failure. /// </summary> internal ParseResult <TTarget> Convert <TTarget>(Func <T, TTarget> projection) => Success ? ParseResult <TTarget> .ForValue(projection(Value)) : new ParseResult <TTarget>(exceptionProvider, ContinueAfterErrorWithMultipleFormats);
// TODO(misc): Tidy this up a *lot*. public ParseResult <Period> Parse(string text) { if (text is null) { return(ParseResult <Period> .ArgumentNull("text")); } if (text.Length == 0) { return(ParseResult <Period> .ValueStringEmpty); } ValueCursor valueCursor = new ValueCursor(text); valueCursor.MoveNext(); if (valueCursor.Current != 'P') { return(ParseResult <Period> .MismatchedCharacter(valueCursor, 'P')); } bool inDate = true; PeriodBuilder builder = new PeriodBuilder(); PeriodUnits unitsSoFar = 0; while (valueCursor.MoveNext()) { if (inDate && valueCursor.Current == 'T') { inDate = false; continue; } bool negative = valueCursor.Current == '-'; var failure = valueCursor.ParseInt64 <Period>(out long unitValue); if (failure != null) { return(failure); } if (valueCursor.Length == valueCursor.Index) { return(ParseResult <Period> .EndOfString(valueCursor)); } // Various failure cases: // - Repeated unit (e.g. P1M2M) // - Time unit is in date part (e.g. P5M) // - Date unit is in time part (e.g. PT1D) // - Unit is in incorrect order (e.g. P5D1Y) // - Unit is invalid (e.g. P5J) // - Unit is missing (e.g. P5) PeriodUnits unit; switch (valueCursor.Current) { case 'Y': unit = PeriodUnits.Years; break; case 'M': unit = inDate ? PeriodUnits.Months : PeriodUnits.Minutes; break; case 'W': unit = PeriodUnits.Weeks; break; case 'D': unit = PeriodUnits.Days; break; case 'H': unit = PeriodUnits.Hours; break; case 'S': unit = PeriodUnits.Seconds; break; case ',': case '.': unit = PeriodUnits.Nanoseconds; break; // Special handling below default: return(InvalidUnit(valueCursor, valueCursor.Current)); } if ((unit & unitsSoFar) != 0) { return(RepeatedUnit(valueCursor, valueCursor.Current)); } // This handles putting months before years, for example. Less significant units // have higher integer representations. if (unit < unitsSoFar) { return(MisplacedUnit(valueCursor, valueCursor.Current)); } // The result of checking "there aren't any time units in this unit" should be // equal to "we're still in the date part". if ((unit & PeriodUnits.AllTimeUnits) == 0 != inDate) { return(MisplacedUnit(valueCursor, valueCursor.Current)); } // Seen a . or , which need special handling. if (unit == PeriodUnits.Nanoseconds) { // Check for already having seen seconds, e.g. PT5S0.5 if ((unitsSoFar & PeriodUnits.Seconds) != 0) { return(MisplacedUnit(valueCursor, valueCursor.Current)); } builder.Seconds = unitValue; if (!valueCursor.MoveNext()) { return(ParseResult <Period> .MissingNumber(valueCursor)); } // Can cope with at most 999999999 nanoseconds if (!valueCursor.ParseFraction(9, 9, out int totalNanoseconds, 1)) { return(ParseResult <Period> .MissingNumber(valueCursor)); } // Use whether or not the seconds value was negative (even if 0) // as the indication of whether this value is negative. if (negative) { totalNanoseconds = -totalNanoseconds; } builder.Milliseconds = (totalNanoseconds / NanosecondsPerMillisecond) % MillisecondsPerSecond; builder.Ticks = (totalNanoseconds / NanosecondsPerTick) % TicksPerMillisecond; builder.Nanoseconds = totalNanoseconds % NanosecondsPerTick; if (valueCursor.Current != 'S') { return(ParseResult <Period> .MismatchedCharacter(valueCursor, 'S')); } if (valueCursor.MoveNext()) { return(ParseResult <Period> .ExpectedEndOfString(valueCursor)); } return(ParseResult <Period> .ForValue(builder.Build())); } builder[unit] = unitValue; unitsSoFar |= unit; } if (unitsSoFar == 0) { return(ParseResult <Period> .ForInvalidValue(valueCursor, TextErrorMessages.EmptyPeriod)); } return(ParseResult <Period> .ForValue(builder.Build())); }
public ParseResult <Period> Parse(string text) { if (text is null) { return(ParseResult <Period> .ArgumentNull("text")); } if (text.Length == 0) { return(ParseResult <Period> .ValueStringEmpty); } ValueCursor valueCursor = new ValueCursor(text); valueCursor.MoveNext(); if (valueCursor.Current != 'P') { return(ParseResult <Period> .MismatchedCharacter(valueCursor, 'P')); } bool inDate = true; PeriodBuilder builder = new PeriodBuilder(); PeriodUnits unitsSoFar = 0; while (valueCursor.MoveNext()) { if (inDate && valueCursor.Current == 'T') { inDate = false; continue; } var failure = valueCursor.ParseInt64 <Period>(out long unitValue); if (failure != null) { return(failure); } if (valueCursor.Length == valueCursor.Index) { return(ParseResult <Period> .EndOfString(valueCursor)); } // Various failure cases: // - Repeated unit (e.g. P1M2M) // - Time unit is in date part (e.g. P5M) // - Date unit is in time part (e.g. PT1D) // - Unit is in incorrect order (e.g. P5D1Y) // - Unit is invalid (e.g. P5J) // - Unit is missing (e.g. P5) PeriodUnits unit; switch (valueCursor.Current) { case 'Y': unit = PeriodUnits.Years; break; case 'M': unit = inDate ? PeriodUnits.Months : PeriodUnits.Minutes; break; case 'W': unit = PeriodUnits.Weeks; break; case 'D': unit = PeriodUnits.Days; break; case 'H': unit = PeriodUnits.Hours; break; case 'S': unit = PeriodUnits.Seconds; break; case 's': unit = PeriodUnits.Milliseconds; break; case 't': unit = PeriodUnits.Ticks; break; case 'n': unit = PeriodUnits.Nanoseconds; break; default: return(InvalidUnit(valueCursor, valueCursor.Current)); } if ((unit & unitsSoFar) != 0) { return(RepeatedUnit(valueCursor, valueCursor.Current)); } // This handles putting months before years, for example. Less significant units // have higher integer representations. if (unit < unitsSoFar) { return(MisplacedUnit(valueCursor, valueCursor.Current)); } // The result of checking "there aren't any time units in this unit" should be // equal to "we're still in the date part". if ((unit & PeriodUnits.AllTimeUnits) == 0 != inDate) { return(MisplacedUnit(valueCursor, valueCursor.Current)); } builder[unit] = unitValue; unitsSoFar |= unit; } return(ParseResult <Period> .ForValue(builder.Build())); }
public ParseResult <Offset> Parse(string text) => text == "Z" ? ParseResult <Offset> .ForValue(Offset.Zero) : fullPattern.Parse(text);