diff --git a/s-pipes-core/src/main/java/cz/cvut/spipes/function/time/Duration.java b/s-pipes-core/src/main/java/cz/cvut/spipes/function/time/Duration.java new file mode 100644 index 00000000..76946d4a --- /dev/null +++ b/s-pipes-core/src/main/java/cz/cvut/spipes/function/time/Duration.java @@ -0,0 +1,52 @@ +package cz.cvut.spipes.function.time; + +import cz.cvut.spipes.constants.KBSS_TIMEF; +import cz.cvut.spipes.function.ValueFunction; +import org.apache.jena.datatypes.xsd.XSDDatatype; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.function.FunctionEnv; +import org.topbraid.spin.arq.AbstractFunction2; + +import javax.xml.bind.DatatypeConverter; +import java.util.Calendar; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Computes difference between two xsd:dateTime values in milliseconds and returns xsd:long datatype + */ +public class Duration extends AbstractFunction2 implements ValueFunction { + + private static final String TYPE_IRI = KBSS_TIMEF.getURI() + "add-days"; + + @Override + public String getTypeURI() { + return TYPE_IRI; + } + + @Override + protected NodeValue exec(Node t2, Node t1, FunctionEnv functionEnv) { + Calendar start = parseNodeToCalendar(t1); + Calendar end = parseNodeToCalendar(t2); + + long duration = end.getTimeInMillis()-start.getTimeInMillis(); + Node node = NodeFactory.createLiteralByValue(duration, XSDDatatype.XSDlong); + return NodeValue.makeNode(node); + } + + private Calendar parseNodeToCalendar(Node x){ + return DatatypeConverter.parseDateTime(extractDateTimePart(x.getLiteral().toString())); + } + + private static String extractDateTimePart(String rdfLiteral) { + String dateTimePattern = "^(.*?)(?:\\^\\^.*|$)"; + Pattern pattern = Pattern.compile(dateTimePattern); + Matcher matcher = pattern.matcher(rdfLiteral); + + if (matcher.find())return matcher.group(1); + return null; + } + +} diff --git a/s-pipes-core/src/test/java/cz/cvut/spipes/function/time/DurationTest.java b/s-pipes-core/src/test/java/cz/cvut/spipes/function/time/DurationTest.java new file mode 100644 index 00000000..82dff2f6 --- /dev/null +++ b/s-pipes-core/src/test/java/cz/cvut/spipes/function/time/DurationTest.java @@ -0,0 +1,60 @@ +package cz.cvut.spipes.function.time; + +import org.apache.jena.datatypes.RDFDatatype; +import org.apache.jena.datatypes.xsd.XSDDatatype; +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DurationTest { + + @Test + public void execSimple() { + + Duration duration = new Duration(); + Node t1 = getDateTimeNode("2023-08-21T13:00:00.100").asNode(); + Node t2 = getDateTimeNode("2023-08-21T13:00:00.250").asNode(); + + NodeValue durationInMS = duration.exec(t2,t1,null); + long expected = 150; + assertEquals(expected,durationInMS.getInteger().intValue()); + } + + @Test + public void execT2IsEarlierThenT1() { + + Duration duration = new Duration(); + Node t1 = getDateTimeNode("2023-08-21T13:20:00").asNode(); + Node t2 = getDateTimeNode("2023-08-21T13:00:00").asNode(); + + NodeValue durationInMS = duration.exec(t2,t1,null); + long expected = -1200000; + assertEquals(expected,durationInMS.getInteger().intValue()); + } + + @Test + public void execTimeZoneFormat() { + + Duration duration = new Duration(); + Node t1 = getDateTimeNode("2023-08-21T15:30:00.100-05:00").asNode(); + Node t2 = getDateTimeNode("2023-08-21T14:30:05.600-06:00").asNode(); + + NodeValue durationInMS = duration.exec(t2,t1,null); + long expected = 5500; + assertEquals(expected,durationInMS.getInteger().intValue()); + } + + private NodeValue getDateTimeNode(String dateTime){ + return getNode(dateTime, XSDDatatype.XSDdateTime); + } + + private NodeValue getNode(String time, RDFDatatype datatype) { + return NodeValue.makeNode( + time, + null, + datatype.getURI() + ); + } +} \ No newline at end of file