DateUtils throw ArgumentOutOfRangeException on ConvertToDateTime


#1

Hi,

I am trying to implement the RangeSelectorChart sample for Android.
When I try to create a DateRange from 

var range = new DateRange(((DateTimeAxis)m_Chart.XAxis).DataRange.Minimum, DateTime.Now)

where XAxis has just been created (the serie is here, linked to an empty custom data adapter), an ArgumentOutOfRangeException is thrown.
This is due to the 

return DateUtils.ConvertToDateTime(this.MinimumInternal());

done while accessing the DataRange.Minimum property.
I tried to get the MinimumInternal Date by reflection and it looks like a date initialized with 

new Java.Util.Date(Java.Lang.Long.MaxValue)

which of course:

  1. Is not compatible with the range of a System.DateTime
  2. Should probably be initialized with MinValue as it is the minimum value for the XAxis…

Does anybody have already encountered this issue?

Thanks,

Guillaume.


#2

Hi Guillaume,

When an Axis is created (and also when added to a chart that has no data yet) its various ranges need to be initialised, but to something that indicates the range is not valid (it’s infinite in a sense i.e.  it has no range so you can’t draw tick marks for it). We decided to represent this by setting the range’s minimum value to the maximum it could be, and its maximum value to the minimum it could be. While this may seem counter-intuitive at first, doing this means any minimum value given to the range will be less than the default minimum, and similarly any maximum value given to the range will be greater than the default maximum. There are of course other ways to achieve this but we decided to go with this approach. 

Hopefully that addresses your second point. In terms of your first point without data or a default range the axis doesn’t really have a range. If your DateTimeAxis is created with a sensible default range I’m guessing this would prevent the problem from occurring. That does assume knowledge of the range of your data upfront so I appreciate may not be possible. Alternatively can you create the DateRange initially with a System.DateTime that represents some sort of absolute minimum?

Kind regards,

Patrick


#3

Hi Patrick,

I ended up with a solution where I keep a reference to the adapter used by the chart serie to check for its IsEmpty property.

My point was that  as a Xamarin.Android user , I have no way to check if a DateTimeAxis has an uninitialized DataRange without getting an exception crashing my app. Trying to convert  Java.Util.Date(Java.Lang.Long.MaxValue) or Java.Util.Date(Java.Lang.Long.MinValue) to System.DateTime throw the ArgumentOutOfRangeException before I can even check if the data range is valid or the axis contains any value.

As a developer, I do not expect from a third party API to throw an exception and crash my app by simply accessing a property which is intended to reflect a current object state like “empty”.

Regards,

Guillaume.


#4

Hello Guillaume,

Thank you for your feedback. I am glad that you have found a solution to your problem, although I appreciate it is perhaps not ideal. As Patrick has already mentioned, an alternative solution might be to ensure that the axis in question is initialised with a sensible default range.

We do take the feedback of our users seriously and as such I will make sure your feedback is considered when defining future development plans for ShinobiCharts for Android (including its Xamarin bindings). 

In the meantime, please accept my apologies for any inconvenience caused.

Thanks and kind regards,

Kai. 


#5

No problem Kai.

Just to mentioned, you should probably implement some sort of MinValue/MaxValue conversion as Xamarin did in their iOS extensions methods defined here http://developer.xamarin.com/guides/cross-platform/macios/unified/#Converting_DateTime_to_NSDate

This can looks like

public static DateTime ToDateTime(this Java.Util.Date date)
{
    // Java.Util.Date has a wider range than DateTime, so clip
    // the converted date to DateTime.Min|MaxValue.
    long dateInTicks = date.Time * TimeSpan.TicksPerMillisecond;

    if (dateInTicks < MinSupportedTicks)
        return DateTime.MinValue;
    if (dateInTicks > MaxSupportedTicks)
        return DateTime.MaxValue;

    return JavaExtensions.OriginDate.AddTicks(dateInTicks).ToLocalTime();
}

With my MinSupportedTicks/MaxSupportedTicks defined as

OriginDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
MinSupportedTicks = -(OriginDate - DateTime.MinValue).Ticks;
MaxSupportedTicks = (DateTime.MaxValue - OriginDate).Ticks;

That’s what I use when I need to convert between managed/native dates.

Regards,

Guillaume.


#6

Hi Guillaume,

Just to say thanks very much for this; it does look like something we overlooked.

I’ve raised a bug for this so it should get addressed in a future release.

Kind regards,

Patrick