Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LocalDateTime.ToString() throws exception near end of time #215

Closed
GoogleCodeExporter opened this issue Mar 15, 2015 · 13 comments
Closed

LocalDateTime.ToString() throws exception near end of time #215

GoogleCodeExporter opened this issue Mar 15, 2015 · 13 comments

Comments

@GoogleCodeExporter
Copy link

What steps will reproduce the problem?

var zone = DateTimeZoneProviders.Tzdb["America/Phoenix"];
var interval = zone.GetZoneInterval(SystemClock.Instance.Now);
Console.WriteLine(interval.End);
Console.WriteLine(interval.IsoLocalEnd);

interval.End outputs EOT, which is expected since this zone does not use 
daylight savings time. But I cannot output the interval.IsoLocalEnd value. It 
throws an exception:

System.ArgumentOutOfRangeException was unhandled
  HResult=-2146233086
  Message=Value should be in range [-27255-31195]
Parameter name: year
Actual value was 31197.
  Source=NodaTime
  ParamName=year
  StackTrace:
       at NodaTime.Utility.Preconditions.CheckArgumentRange(String paramName, Int32 value, Int32 minInclusive, Int32 maxInclusive)
       at NodaTime.Calendars.BasicCalendarSystem.GetYearTicks(Int32 year)
       at NodaTime.Calendars.GregorianCalendarSystem.GetYearTicks(Int32 year)
       at NodaTime.Calendars.BasicGJCalendarSystem.GetMonthOfYear(LocalInstant localInstant, Int32 year)
       at NodaTime.Calendars.BasicCalendarSystem.GetMonthOfYear(LocalInstant localInstant)
       at NodaTime.Fields.BasicMonthOfYearDateTimeField.GetValue(LocalInstant localInstant)
       at NodaTime.LocalDateTime.get_Month()
       at NodaTime.Text.LocalDateTimePatternParser.<.cctor>b__8(LocalDateTime value)
       at NodaTime.Text.Patterns.DatePatternHelper.<>c__DisplayClassd`2.<>c__DisplayClass10.<CreateMonthOfYearHandler>b__c(TResult value, StringBuilder sb)
       at NodaTime.NodaAction`2.Invoke(TArg1 arg1, TArg2 arg2)
       at NodaTime.Text.Patterns.SteppedPatternBuilder`2.SteppedPattern.Format(TResult value)
       at NodaTime.Text.Patterns.PatternBclSupport`1.Format(T value, String patternText, NodaFormatInfo formatInfo)
       at NodaTime.LocalDateTime.ToString(String patternText, IFormatProvider formatProvider)
       at System.IO.TextWriter.WriteLine(Object value)
       at System.IO.TextWriter.SyncTextWriter.WriteLine(Object value)


What version of the product are you using? On what operating system?
NodatTime 1.1.0

Please provide any additional information below.
http://stackoverflow.com/q/16098076/634824

Original issue reported on code.google.com by mj1856 on 19 Apr 2013 at 10:04

@GoogleCodeExporter
Copy link
Author

Yes, we definitely have problems at the end of time - and even a bit before 
then. Issue 113 covers this a little, and I thought we'd got another one 
somewhere, but apparently not.

The tricky thing is working out what it *should* do. What would you like it to 
do in this case? It's not like it's *really* whatever year/month/day it would 
end up as with long.MaxValue. Should we have an "end of time LocalDateTime" as 
well, perhaps? Any thoughts here would be appreciated... including any context 
as to what you were trying to do at the time. (Would it make sense for you to 
detect EOT yourself, for example?)

Original comment by jonathan.skeet on 20 Apr 2013 at 6:49

@GoogleCodeExporter
Copy link
Author

This does appear to be a regression against 1.0.x, probably because of 
r7a65b5f5f560 (issue 197).

In fact, I think you can reproduce essentially the same problem with simply:

Console.WriteLine(Instant.MaxValue.InUtc());

Under 1.0.1, this produces:

Local: 01/01/31197 02:48:05 Offset: +00 Zone: UTC

Under 1.1.0, I get:

Unhandled Exception: System.ArgumentOutOfRangeException: Value should be in 
range [-27255-31195]
Parameter name: year
31197
  at NodaTime.Utility.Preconditions.CheckArgumentRange (System.String paramName, Int32 value, Int32 minInclusive, Int32 maxInclusive) [0x00000] in <filename unknown>:0 
  at NodaTime.Calendars.BasicCalendarSystem.GetYearTicks (Int32 year) [0x00000] in <filename unknown>:0 
  at NodaTime.Calendars.GregorianCalendarSystem.GetYearTicks (Int32 year) [0x00000] in <filename unknown>:0 
  at NodaTime.Calendars.BasicGJCalendarSystem.GetMonthOfYear (LocalInstant localInstant, Int32 year) [0x00000] in <filename unknown>:0 
  at NodaTime.Calendars.BasicCalendarSystem.GetDayOfMonth (LocalInstant localInstant) [0x00000] in <filename unknown>:0 
  at NodaTime.Fields.BasicDayOfMonthDateTimeField.GetValue (LocalInstant localInstant) [0x00000] in <filename unknown>:0 
  at NodaTime.LocalDateTime.get_Day () [0x00000] in <filename unknown>:0 
  at NodaTime.Text.LocalDateTimePatternParser.<.cctor>b__b (LocalDateTime value) [0x00000] in <filename unknown>:0 
  at (wrapper delegate-invoke) <Module>:invoke_int__this___LocalDateTime (NodaTime.LocalDateTime)
  at NodaTime.Text.Patterns.DatePatternHelper+<>c__DisplayClass15`2+<>c__DisplayClass18[NodaTime.LocalDateTime,NodaTime.Text.LocalDateTimePatternParser+LocalDateTimeParseBucket].<CreateDayHandler>b__13 (LocalDateTime value, System.Text.StringBuilder sb) [0x00000] in <filename unknown>:0 
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at (wrapper delegate-invoke) <Module>:invoke_void__this___LocalDateTime_StringBuilder (NodaTime.LocalDateTime,System.Text.StringBuilder)
  at NodaTime.Text.Patterns.SteppedPatternBuilder`2+SteppedPattern[NodaTime.LocalDateTime,NodaTime.Text.LocalDateTimePatternParser+LocalDateTimeParseBucket].Format (LocalDateTime value) [0x00000] in <filename unknown>:0 
  at NodaTime.Text.Patterns.PatternBclSupport`1[NodaTime.LocalDateTime].Format (LocalDateTime value, System.String patternText, NodaTime.Globalization.NodaFormatInfo formatInfo) [0x00000] in <filename unknown>:0 
  at NodaTime.LocalDateTime.ToString () [0x00000] in <filename unknown>:0 
  at System.String.Concat (System.Object[] args) [0x00039] in /home/malcolm/noda-time/mono-2.10.9/mcs/class/corlib/System/String.cs:2134 
  at NodaTime.ZonedDateTime.ToString () [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.Write (System.Object value) [0x00006] in /home/malcolm/noda-time/mono-2.10.9/mcs/class/corlib/System.IO/TextWriter.cs:163 
  at System.IO.TextWriter.WriteLine (System.Object value) [0x00000] in /home/malcolm/noda-time/mono-2.10.9/mcs/class/corlib/System.IO/TextWriter.cs:273 
  at System.IO.SynchronizedWriter.WriteLine (System.Object value) [0x00008] in /home/malcolm/noda-time/mono-2.10.9/mcs/class/corlib/System.IO/TextWriter.cs:563 
  at System.Console.WriteLine (System.Object value) [0x00000] in /home/malcolm/noda-time/mono-2.10.9/mcs/class/corlib/System/Console.cs:423 
  at Program.Main (System.String[] args) [0x00000] in <filename unknown>:0 


Note that it appears to be the ToString() that causes this: simply computing 
the LocalDateTime is fine.

Original comment by malcolm.rowe on 20 Apr 2013 at 9:00

  • Changed title: LocalDateTime.ToString() throws exception near end of time
  • Changed state: Accepted
  • Added labels: Milestone-1.1-consider

@GoogleCodeExporter
Copy link
Author

Original comment by malcolm.rowe on 20 Apr 2013 at 9:03

  • Added labels: Regression

@GoogleCodeExporter
Copy link
Author

In some ways I see this as *not* a regression. I think I'd prefer to give an 
exception than a misleading string representation - it's not like there 
actually *is* a transition at 01/01/31197 02:48:05.

After the refactoring at NDC, I'm hoping to prevent LocalDate/LocalDateTime 
values which are outside the range of the calendar they're associated with from 
ever escaping - at which point we'd throw a more meaningful exception earlier.

Original comment by jonathan.skeet on 20 Apr 2013 at 10:15

@GoogleCodeExporter
Copy link
Author

The regression I was flagging was that Instant.MaxValue.InUtc().ToString() 
(arguably) DTRT in 1.0.x and fails completely in 1.1.0.

If you're saying that our proleptic calendars are not, in fact, intended to be 
proleptic, then it's probably not a regression.

Original comment by malcolm.rowe on 20 Apr 2013 at 8:06

@GoogleCodeExporter
Copy link
Author

I think we may disagree about the meaning of proleptic :)

I'm saying that Instant.MaxValue is meant to be "the end of time" - and the end 
of time isn't 31197 AD. I think we probably want to special-case 
Instant.MaxValue => LocalTime (any calendar) to give LocalInstant.MaxValue in 
that calendar, and special-case that value to give "EOT" as a ToString value. 
Maybe. Either that, or throw an exception.

Fuel for further discussion though.

I suspect that pretty much *any* sensible change (even restoring the old 
behaviour) could be tricky in the 1.1 branch.

Original comment by jonathan.skeet on 21 Apr 2013 at 7:07

@GoogleCodeExporter
Copy link
Author

The only real reason I am concerned with EOT values is to denote that something 
is intended to go on forever.  While I understand that it's hard to pin a date 
on it, it still does exist on the timeline.  Just like +/- infinity are still 
on the line of Real numbers.

Perhaps someone doing astronomical doomsday calculations cares about years that 
large, but I guess I'm one of those that would stop at 9999 and call that good 
enough.  From my perspective, anything > 9999 is close enough to infinity. :)

So what would I expect?  If End equals EOT, then IsoLocalEnd also equals EOT.  
EOT plus or minus anthing is still EOT. Same for beginning of time values (BOT)

I suppose the alternative would be for some of these properties to be nullable. 
 There would then be no concept of EOT, but anywhere that might go on forever 
would be a Nullable<Instant> or Nullable<LocalDateTime>.  Intervals being a 
good example.  The only problem there is that Null means either BOT or EOT 
depending on where it is used, so comparisons can be problematic.

Original comment by mj1856 on 22 Apr 2013 at 7:21

@GoogleCodeExporter
Copy link
Author

Sorry for the duplicate before i did no found this post :s

My concern about MinValue and MaxValue are more about finding a convention 
saying that the field is actually empty without using a nullable or as mj1856 
to have a date that will always fit some tests.

the thing is, outside these cases using a date that is close to EOT or BOT 
would most certainly be a bug. 
To my opinion it is too tempting to use those values available in Instant to 
let the serialisation crash. I don't think i am the only one who is using this 
kind of coding convention.

To me the best would be to crash on any value above calendar capacity except 
for EOT and BOT which should give the max and the min of the considered 
calendar.

I understand the problem is bigger than that and this may not be the reliant 
comportment you want but i think this is a really tricky problem so good luck 
with it :)

Anyways thanks again for your API and your time :)  

Original comment by qcastel...@gmail.com on 6 Jun 2013 at 1:04

@GoogleCodeExporter
Copy link
Author

Your description of EOT and BOT being valid, but nearby ones not being valid is 
where I'd like to go too. Just don't expect it to happen any time soon - 
especially when I need to balance performance requirements around it too.

I would like to discourage the use of BOT/EOT as substitutes for null values, 
mind you - they're meant to be meaningful values for cases like "an interval 
which stretches from the beginning of time until X". But they should definitely 
be serializable... (Both XML and JSON.)

Original comment by jonathan.skeet on 6 Jun 2013 at 1:13

@GoogleCodeExporter
Copy link
Author

We've fixed Instant.MinValue and Instant.MaxValue for release 1.2, but *only* 
for Instant. A lot more work is needed to handle this sort of thing in general, 
and I'm hoping to address that in 1.3 - so I'm punting this to then for now.

Original comment by jonathan.skeet on 26 Jul 2013 at 6:31

  • Added labels: Milestone-1.3-consider
  • Removed labels: Milestone-1.1-consider

@GoogleCodeExporter
Copy link
Author

Original comment by malcolm.rowe on 30 May 2014 at 8:34

  • Added labels: Milestone-unscheduled
  • Removed labels: Milestone-1.3-consider

@GoogleCodeExporter
Copy link
Author

Noda Time 2.0 fixes all of this. Any value you can create, you should be able 
to format.

(There are some internal values which are unprintable, but that's a different 
matter.)

Original comment by jonathan.skeet on 2 Aug 2014 at 8:09

  • Changed state: Fixed

@GoogleCodeExporter
Copy link
Author

Original comment by malcolm.rowe on 2 Aug 2014 at 8:47

  • Added labels: Milestone-2.0.0
  • Removed labels: Milestone-unscheduled

@malcolmr malcolmr added the bug label Mar 15, 2015
@malcolmr malcolmr modified the milestone: 2.0.0 Mar 15, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants