[ 
https://issues.apache.org/jira/browse/JENA-1402?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16448024#comment-16448024
 ] 

Bruno P. Kinoshita commented on JENA-1402:
------------------------------------------

At work we migrated a forecast system using Joda to Java 8 time API. Pretty 
much everything worked, except some classes that were missing (two IIRC) that 
were available in [three-thirteen|http://www.threeten.org/threeten-extra/]. 310 
is the JSR number.

Perhaps there could be a third temporary/phase-out alternative... though it 
could look a bit too hacky? One could create an adapter layer for Jena users to 
experiment the Java 8 time package and see if that works OK before moving.

Add the following JVM argument when running the next class: 
-Djavax.xml.datatype.DatatypeFactory=org.apache.jena.atlas.data.WrappedDurationDatatypeFactory.

{code:java}
// random module that I was reading the code...
package org.apache.jena.atlas.data;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;

public class TestingXmlJava8Durations {

    public static void main(String... args) throws 
DatatypeConfigurationException {
        String lex1 = "PT1M3.123S";
        String lex2 = "PT0M10.123S";
        DatatypeFactory factory = DatatypeFactory.newInstance();
        System.out.println(factory.getClass()); // to get the factory class 
name...
        Duration dt1 = factory.newDuration(lex1);
        Duration dt2 = factory.newDuration(lex2);
        System.out.println(dt1);
        System.out.println(dt2);
        System.out.println();

        Duration dt3 = dt1.subtract(dt2);
        System.out.println(dt3);
        System.out.println();
    }

}
{code}

{code:java}
package org.apache.jena.atlas.data;

import java.math.BigDecimal;
import java.util.Calendar;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants.Field;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;

public class WrappedXmlDuration extends Duration {

    private java.time.Duration javaDuration;
    private DatatypeFactory factory;

    public WrappedXmlDuration(Duration xmlDuration) throws 
DatatypeConfigurationException {
        javaDuration = 
java.time.Duration.ofMillis(xmlDuration.getTimeInMillis(Calendar.getInstance()));
        factory = 
DatatypeFactory.newInstance("org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl",
                TestingXmlJava8Durations.class.getClassLoader());
    }

    public java.time.Duration getWrapped() {
        return javaDuration;
    }

    @Override
    public int getSign() {
        if (javaDuration.isZero())
            return 0;
        return javaDuration.isNegative() ? -1 : 1;
    }

    @Override
    public Number getField(Field field) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public boolean isSet(Field field) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public Duration add(Duration rhs) {
        java.time.Duration otherDuration = 
java.time.Duration.ofMillis(rhs.getTimeInMillis(Calendar.getInstance()));
        java.time.Duration added = javaDuration.plus(otherDuration);
        return factory.newDuration(added.toMillis());
    }

    @Override
    public void addTo(Calendar calendar) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public Duration multiply(BigDecimal factor) {
        java.time.Duration multiplied = 
javaDuration.multipliedBy(factor.longValue());
        return factory.newDuration(multiplied.toMillis());
    }

    @Override
    public Duration negate() {
        java.time.Duration negated = javaDuration.negated();
        return factory.newDuration(negated.toMillis());
    }

    @Override
    public Duration normalizeWith(Calendar startTimeInstant) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public int compare(Duration duration) {
        java.time.Duration otherDuration = java.time.Duration
                .ofMillis(duration.getTimeInMillis(Calendar.getInstance()));
        return javaDuration.compareTo(otherDuration);
    }

    @Override
    public int hashCode() {
        return javaDuration.hashCode();
    }

    @Override
    public String toString() {
        return javaDuration.toString();
    }
}
{code}

{code:java}
package org.apache.jena.atlas.data;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.GregorianCalendar;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

public class WrappedDurationDatatypeFactory extends DatatypeFactory {

    private DatatypeFactory factory;

    public WrappedDurationDatatypeFactory() throws 
DatatypeConfigurationException {
        factory = 
DatatypeFactory.newInstance("org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl",
                TestingXmlJava8Durations.class.getClassLoader());
    }

    @Override
    public Duration newDuration(String lexicalRepresentation) {
        Duration wrappedDuration = factory.newDuration(lexicalRepresentation);
        WrappedXmlDuration myDuration = null;
        try {
            myDuration = new WrappedXmlDuration(wrappedDuration);
        } catch (DatatypeConfigurationException e) {
            e.printStackTrace();
        }
        return myDuration;
    }

    @Override
    public Duration newDuration(long durationInMilliSeconds) {
        Duration wrappedDuration = factory.newDuration(durationInMilliSeconds);
        WrappedXmlDuration myDuration = null;
        try {
            myDuration = new WrappedXmlDuration(wrappedDuration);
        } catch (DatatypeConfigurationException e) {
            e.printStackTrace();
        }
        return myDuration;
    }

    @Override
    public Duration newDuration(boolean isPositive, BigInteger years, 
BigInteger months, BigInteger days,
            BigInteger hours, BigInteger minutes, BigDecimal seconds) {
        Duration wrappedDuration = factory.newDuration(isPositive, years, 
months, days, hours, minutes, seconds);
        WrappedXmlDuration myDuration = null;
        try {
            myDuration = new WrappedXmlDuration(wrappedDuration);
        } catch (DatatypeConfigurationException e) {
            e.printStackTrace();
        }
        return myDuration;
    }

    @Override
    public XMLGregorianCalendar newXMLGregorianCalendar() {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public XMLGregorianCalendar newXMLGregorianCalendar(String 
lexicalRepresentation) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public XMLGregorianCalendar newXMLGregorianCalendar(GregorianCalendar cal) {
        throw new RuntimeException("Not implemented");
    }

    @Override
    public XMLGregorianCalendar newXMLGregorianCalendar(BigInteger year, int 
month, int day, int hour, int minute,
            int second, BigDecimal fractionalSecond, int timezone) {
        throw new RuntimeException("Not implemented");
    }

}
{code}

I read this ticket just before leaving $work, and kept wondering if that would 
work... so had to have a crack at and see what would happen.

If you have the class in that package, or anywhere else in Eclipse, and start 
Fuseki with the same JVM argument to replace the DatatypeFactory, you will get:

{noformat}
{
  "head": {
    "vars": [ "res" , "op1" , "op2" ]
  } ,
  "results": {
    "bindings": [
      {
        "res": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT53.000S" } ,
        "op1": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT2M3S" } ,
        "op2": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1M10S" }
      } ,
      {
        "res": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1M2.000S" } ,
        "op1": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT2M3.123S" } ,
        "op2": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1M1.123S" }
      } ,
      {
        "res": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "-PT1M7.000S" } ,
        "op1": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT0M3.123S" } ,
        "op2": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1M10.123S" }
      } ,
      {
        "res": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT53.000S" } ,
        "op1": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT2M3.123S" } ,
        "op2": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1M10.123S" }
      } ,
      {
        "res": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1H3M53.000S" } ,
        "op1": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT1H4M3.123S" } ,
        "op2": { "type": "literal" , "datatype": 
"http://www.w3.org/2001/XMLSchema#duration"; , "value": "PT0M10.123S" }
      }
    ]
  }
}
{noformat}

Probably we would need the other methods implemented... but looks... doable? 
Though not sure if a good idea...

Bruno

> Subtracting two xsd:Duration gives incorrect results in SPARQL query
> --------------------------------------------------------------------
>
>                 Key: JENA-1402
>                 URL: https://issues.apache.org/jira/browse/JENA-1402
>             Project: Apache Jena
>          Issue Type: Bug
>          Components: ARQ
>    Affects Versions: Jena 3.4.0
>            Reporter: Greg Albiston
>            Priority: Major
>
> There is an issue when subtracting two xsd:durations that include:
> * decimal seconds
> * non-zero minutes
> * second operand has a greater number of seconds than the first operand, i.e. 
> the minutes are reduced. 
> The result is a large number of minutes and incorrect seconds.
> For example:
> Integer, Larger: "PT2M3S" - "PT1M10S"  = "PT0M53S" CORRECT
> Decimal, Smaller: "PT2M3.123S" - "PT1M1.123S" = "PT1M2.000S" CORRECT
> Decimal, Larger, Seconds: "PT0M3.123S" - "PT1M10.123S"  = "-PT1M7.000S" 
> CORRECT
> Decimal, Larger, Minutes: "PT2M3.123S" - "PT1M10.123S"  = "PT883M0.020S" 
> INCORRECT
> Decimal, Larger, Hours: "PT1H4M3.123S" - "PT0M10.123S" = "PT1H3883M0.020S" 
> INCORRECT
> Example SPARQL:
> {code:sparql}
> SELECT ?res ?op1 ?op2
> WHERE{
>    VALUES (?op1 ?op2) {
>         ("PT2M3S"^^<http://www.w3.org/2001/XMLSchema#duration> 
> "PT1M10S"^^<http://www.w3.org/2001/XMLSchema#duration>)
>         ("PT2M3.123S"^^<http://www.w3.org/2001/XMLSchema#duration> 
> "PT1M1.123S"^^<http://www.w3.org/2001/XMLSchema#duration>)
>         ("PT0M3.123S"^^<http://www.w3.org/2001/XMLSchema#duration> 
> "PT1M10.123S"^^<http://www.w3.org/2001/XMLSchema#duration>)
>         ("PT2M3.123S"^^<http://www.w3.org/2001/XMLSchema#duration> 
> "PT1M10.123S"^^<http://www.w3.org/2001/XMLSchema#duration>)
>         ("PT1H4M3.123S"^^<http://www.w3.org/2001/XMLSchema#duration> 
> "PT0M10.123S"^^<http://www.w3.org/2001/XMLSchema#duration>)
>     }
>     BIND(?op1 - ?op2 AS ?res)
> }
> {code}



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to