Project

Profile

Help

Should new XdmAtomicValue(DateTime.Now) or new XdmAtomicValue(DateTimeOffset.Now) work with Saxon .NET or SaxonCS or throw an exception?

Added by Martin Honnen over 2 years ago

The .NET API has methods https://www.saxonica.com/html/documentation11/dotnetdoc/Saxon/Api/XdmAtomicValue.html#XdmAtomicValue(DateTime) and https://www.saxonica.com/html/documentation11/dotnetdoc/Saxon/Api/XdmAtomicValue.html#XdmAtomicValue(DateTimeOffset) that seem to be offered to pass in a .NET DateTime or DateTimeOffset to the XdmAtomicValue constructor.

The easiest way to construct such values are e.g. DateTime.Now or DateTimeOffset.Now; however, I am kind of confused that passing these values in to XdmAtomicValue gives an exception:

<System.OverflowException: Value was either too large or too small for an unsigned byte.
   at System.Convert.ThrowByteOverflowException()
   at System.Convert.ToByte(UInt64 value)
   at System.Convert.ToByte(Int64 value)
   at Saxon.Api.XdmAtomicValue._init(DateTime dt)
   at Saxon.Api.XdmAtomicValue..ctor(DateTime dt)
<System.OverflowException: Value was either too large or too small for an unsigned byte.
   at System.Convert.ThrowByteOverflowException()
   at System.Convert.ToByte(UInt64 value)
   at System.Convert.ToByte(Int64 value)
   at Saxon.Api.XdmAtomicValue._initOffset(DateTimeOffset offset)
   at Saxon.Api.XdmAtomicValue..ctor(DateTimeOffset offset)

Are these exceptions intended/expected? What is the right DateTime or DateTimeOffset range to pass in to XdmAtomicValue?


Replies (5)

Please register to reply

RE: Should new XdmAtomicValue(DateTime.Now) or new XdmAtomicValue(DateTimeOffset.Now) work with Saxon .NET or SaxonCS or throw an exception? - Added by Martin Honnen over 2 years ago

The Java API for the seventh argument expects an int for nanoseconds https://www.saxonica.com/html/documentation11/javadoc/net/sf/saxon/value/DateTimeValue.html#DateTimeValue-int-byte-byte-byte-byte-byte-int-int- yet the .NET code (https://saxonica.plan.io/projects/saxonmirrorhe/repository/he/revisions/saxon10/entry/src/main/csharp/api/Saxon.Api/Model.cs#L984) seems to try to compute a value but use Convert.ToByte:

       public XdmAtomicValue(DateTime dt)
        {
            if (dt.Kind == DateTimeKind.Local)
            {
                int offsetMinutes = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).Minutes;
                this.value = new net.sf.saxon.value.DateTimeValue(dt.Year, Convert.ToByte(dt.Month), Convert.ToByte(dt.Day), Convert.ToByte(dt.Hour), Convert.ToByte(dt.Minute), Convert.ToByte(dt.Second), Convert.ToByte(dt.Ticks * (1000000000 / TimeSpan.TicksPerSecond)), offsetMinutes);
            }
            else if (dt.Kind == DateTimeKind.Utc)
            {
                this.value = new net.sf.saxon.value.DateTimeValue(dt.Year, Convert.ToByte(dt.Month), Convert.ToByte(dt.Day), Convert.ToByte(dt.Hour), Convert.ToByte(dt.Minute), Convert.ToByte(dt.Second), Convert.ToByte(dt.Ticks * (1000000000 / TimeSpan.TicksPerSecond)), 0);

            }
            else if (dt.Kind == DateTimeKind.Unspecified)
            {
                int offsetMinutes = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).Minutes;
                this.value = new net.sf.saxon.value.DateTimeValue(dt.Year, Convert.ToByte(dt.Month), Convert.ToByte(dt.Day), Convert.ToByte(dt.Hour), Convert.ToByte(dt.Minute), Convert.ToByte(dt.Second), Convert.ToByte(dt.Ticks * (1000000000 / TimeSpan.TicksPerSecond)), net.sf.saxon.value.CalendarValue.NO_TIMEZONE);
            }
        }

        /// <summary>
        /// Construct an atomic value of type <c>xs:dateTime</c> with a specific timezone offset from a DateTimeOffset object.
        /// </summary>
        /// <param name="offset">The DateTimeOffset value</param>
        public XdmAtomicValue(DateTimeOffset offset)
        {
            int offsetMinutes = offset.Offset.Minutes;
            DateTime dt = offset.DateTime;
            this.value = new net.sf.saxon.value.DateTimeValue(dt.Year, Convert.ToByte(dt.Month), Convert.ToByte(dt.Day), Convert.ToByte(dt.Hour), Convert.ToByte(dt.Minute), Convert.ToByte(dt.Second), Convert.ToByte(dt.Ticks * (1000000000 / TimeSpan.TicksPerSecond)), offsetMinutes);
        }

RE: Should new XdmAtomicValue(DateTime.Now) or new XdmAtomicValue(DateTimeOffset.Now) work with Saxon .NET or SaxonCS or throw an exception? - Added by Martin Honnen over 2 years ago

Indeed only a DateTime with very small Ticks property can be passed in, in the following all but the first test fail:

using System;
using NUnit.Framework;
using Saxon.Api;

namespace SaxonCSXdmAtomicValueFromDateTime
{
    public class Tests
    {
        private static Processor? processor;
        [SetUp]
        public void Setup()
        {
            processor = new Processor(true);
        }

        [Test(Description = "Test new XdmAtomicValue(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc))")]
        public void TestXdmAtomicValueFromNewDateTime()
        {
            var dateTime = new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var xdmDateTime = new XdmAtomicValue(dateTime);
            Assert.IsInstanceOf<XdmAtomicValue>(xdmDateTime);
        }

        [Test(Description = "Test new XdmAtomicValue(DateTime.Parse)")]
        public void XdmAtomicValueDateTimeParse() => Assert.DoesNotThrow(() => { var value = new XdmAtomicValue(DateTime.Parse("2022-05-05T00:00:00Z", null, System.Globalization.DateTimeStyles.RoundtripKind)); }, "new XdmAtomicValue(DateTime.Now) does not throw");

        [Test(Description = "Test new XdmAtomicValue(new DateTime(2022, 5, 6))")]
        public void XdmAtomicValueNewDateTime() => Assert.DoesNotThrow(() => { var value = new XdmAtomicValue(new DateTime(2022, 5, 6)); }, "new XdmAtomicValue(new DateTime()) does not throw");

        [Test(Description = "Test new XdmAtomicValue(DateTime.Now)")]
        public void XdmAtomicValueDateTimeNow() => Assert.DoesNotThrow(() => { var value = new XdmAtomicValue(DateTime.Now); } , "new XdmAtomicValue(DateTime.Now) does not throw");

        [Test(Description = "Test new XdmAtomicValue(DateTimeOffset.Now)")]
        public void XdmAtomicValueDateTimeOffsetNow() => Assert.DoesNotThrow(() => { var value = new XdmAtomicValue(DateTimeOffset.Now); }, "new XdmAtomicValue(DateTimeOffset.Now) does not throw");
    }
}

RE: Should new XdmAtomicValue(DateTime.Now) or new XdmAtomicValue(DateTimeOffset.Now) work with Saxon .NET or SaxonCS or throw an exception? - Added by Martin Honnen over 2 years ago

But it kind of looks as if the attempt to use dt.Ticks * (1000000000 / TimeSpan.TicksPerSecond is not the right value for the nanoseconds part of the DateTime, that value is rather the whole time since 0001-01-01T00:00:00 in nanoseconds, I think.

RE: Should new XdmAtomicValue(DateTime.Now) or new XdmAtomicValue(DateTimeOffset.Now) work with Saxon .NET or SaxonCS or throw an exception? - Added by Martin Honnen over 2 years ago

I wonder whether that argument should be Convert.ToInt(dt.Ticks % TimeSpan.TicksPerSecond * 100).

    (1-5/5)

    Please register to reply