You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemds.apache.org by ss...@apache.org on 2022/02/12 22:40:30 UTC
[systemds] branch main updated: [SYSTEMDS-3175] Builtin for date processing - This commit supports the date processing via map() function. It provides methods to - convert date to number - find the dominating pattern in the date column and format the whole column accordingly - addition and subtraction on the date column (return results in date-time format)
This is an automated email from the ASF dual-hosted git repository.
ssiddiqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git
The following commit(s) were added to refs/heads/main by this push:
new b65d934 [SYSTEMDS-3175] Builtin for date processing - This commit supports the date processing via map() function. It provides methods to - convert date to number - find the dominating pattern in the date column and format the whole column accordingly - addition and subtraction on the date column (return results in date-time format)
b65d934 is described below
commit b65d9344648f741bfa471a00dc2af74e2c42da56
Author: Phillip Stranger <ph...@student.tugraz.at>
AuthorDate: Sat Feb 12 23:15:52 2022 +0100
[SYSTEMDS-3175] Builtin for date processing
- This commit supports the date processing via map() function. It provides methods to
- convert date to number
- find the dominating pattern in the date column and format the whole column accordingly
- addition and subtraction on the date column (return results in date-time format)
Closes #1514.
---
.../apache/sysds/runtime/util/UtilFunctions.java | 142 +++++++++++
.../builtin/part1/BuiltinDateProcessingTest.java | 263 +++++++++++++++++++++
.../scripts/functions/builtin/date_processing.dml | 63 +++++
3 files changed, 468 insertions(+)
diff --git a/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java b/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
index 4376fa9..6ca83be 100644
--- a/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
+++ b/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
@@ -24,17 +24,21 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.math3.random.RandomDataGenerator;
import org.apache.sysds.common.Types.ValueType;
import org.apache.sysds.runtime.DMLRuntimeException;
@@ -874,6 +878,144 @@ public class UtilFunctions {
.map(DATE_FORMATS::get).orElseThrow(() -> new NullPointerException("Unknown date format."));
}
+ private static int findDateCol (FrameBlock block) {
+ int cols = block.getNumColumns();
+ int[] match_counter = new int[cols];
+ int dateCol = -1;
+
+ for (int i = 0; i < cols; i++) {
+ String[] values = (String[]) block.getColumnData(i);
+ int matchCount = 0;
+ for (int j = 0; j < values.length; j++) {
+ //skip null/blank entries
+ if (values[j] == null || values[j].trim().isEmpty() || values[j].toUpperCase().equals("NULL") ||
+ values[j].equals("0")) continue;
+ String tmp = values[j];
+ //check if value matches any date pattern
+ if(DATE_FORMATS.keySet().parallelStream().anyMatch(e -> tmp.toLowerCase().matches(e))) matchCount++;
+ }
+ match_counter[i] = matchCount;
+ }
+
+ int maxMatches = Integer.MIN_VALUE;
+ //get column with most matches -> date column
+ for (int i = 0; i < match_counter.length; i++) {
+ if (match_counter[i] > maxMatches) {
+ maxMatches = match_counter[i];
+ dateCol = i;
+ }
+ }
+
+ if (maxMatches <= 0 || dateCol < 0){
+ //ERROR - no date column found
+ throw new DMLRuntimeException("No date column found.");
+ }
+ return dateCol;
+ }
+
+ public static String[] getDominantDateFormat (String[] values) {
+ String[] output = new String[values.length];
+ Map<String, String> date_formats = DATE_FORMATS;
+
+ Map<String, Integer> format_matches = new HashMap<String, Integer>();
+ for (Map.Entry<String,String> entry : date_formats.entrySet()) {
+ format_matches.put(entry.getValue(), 0);
+ }
+
+ for (int i = 0; i < values.length; i++) {
+ //skip null/blank entries
+ if (values[i] == null || values[i].trim().isEmpty() || values[i].equalsIgnoreCase("NULL")
+ || values[i].equals("0")) continue;
+ String tmp = values[i];
+ System.out.println("tmp "+tmp);
+ String dateFormat = getDateFormat(tmp);
+ //find pattern which matches values[i] -> increase count for this pattern
+ format_matches.put(dateFormat, format_matches.get(dateFormat) + 1);
+ }
+ //find format with the most occurences in values -> dominant format
+ String dominantFormat = format_matches.entrySet().stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey();
+ for (int i = 0; i< values.length; i++){
+ //skip null/blank entries
+ if (values[i] == null || values[i].trim().isEmpty() ||
+ values[i].equalsIgnoreCase("NULL") || values[i].equals("0")) continue;
+ String currentFormat = getDateFormat(values[i]);
+ //Locale.US needs to be used as otherwise dateformat like "dd MMM yyyy HH:mm" are not parsable
+ SimpleDateFormat curr = new SimpleDateFormat(currentFormat, Locale.US);
+ try {
+ Date date = curr.parse(values[i]); //parse date string
+ if (!currentFormat.equals(dominantFormat)){
+ curr.applyPattern(dominantFormat);
+ }
+ String newDate = curr.format(date); //convert date to dominant date format
+ output[i] = curr.format(date); //convert back to datestring
+ } catch (ParseException e) {
+ throw new DMLRuntimeException(e);
+ }
+ }
+
+ return output;
+ }
+
+ public static String addTimeToDate (String dateString, int amountToAdd, String timeformat) {
+ String currentFormat = getDateFormat(dateString);
+ Date date = null;
+ SimpleDateFormat curr = new SimpleDateFormat(currentFormat, Locale.US);
+ try {
+ date = curr.parse(dateString);
+ }
+ catch(Exception e) { e.getMessage();}
+
+ Date newDate;
+ switch (timeformat) {
+ case "ms": //milliseconds
+ newDate = DateUtils.addMilliseconds(date, amountToAdd);
+ break;
+ case "m": //minutes
+ newDate = DateUtils.addMinutes(date, amountToAdd);
+ break;
+ case "H": //hours
+ newDate = DateUtils.addHours(date, amountToAdd);
+ break;
+ case "d": //days
+ newDate = DateUtils.addDays(date, amountToAdd);
+ break;
+ case "w": //weeks
+ newDate = DateUtils.addWeeks(date, amountToAdd);
+ break;
+ case "M": //months
+ newDate = DateUtils.addMonths(date, amountToAdd);
+ break;
+ case "y": //years
+ newDate = DateUtils.addYears(date, amountToAdd);
+ break;
+ default: //seconds
+ newDate = DateUtils.addSeconds(date, amountToAdd);
+ break;
+ }
+ return curr.format(newDate);
+ }
+
+ public static String[] getTimestamp(String[] values)
+ {
+ String output[] = new String[values.length];
+ for (int i = 0; i< values.length; i++){
+ //skip null/blank entries
+ if (values[i] == null || values[i].trim().isEmpty() || values[i].toUpperCase().equals("NULL")
+ || values[i].equals("0")) continue;
+ String currentFormat = getDateFormat(values[i]);
+ //Locale.US needs to be used as otherwise dateformat like "dd MMM yyyy HH:mm" are not parsable
+ SimpleDateFormat curr = new SimpleDateFormat(currentFormat, Locale.US);
+ curr.setTimeZone(TimeZone.getTimeZone("UTC"));
+ try {
+ Date date = curr.parse(values[i]); //parse date string
+ output[i] = String.valueOf(date.getTime()); //get timestamp in milliseconds
+ } catch (ParseException e) {
+ throw new DMLRuntimeException(e);
+ }
+ }
+ return output;
+ }
+
public static String[] getSplittedStringAsArray (String input) {
//Frame f = new Frame();
String[] string_array = input.split("'[ ]*,[ ]*'");
diff --git a/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinDateProcessingTest.java b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinDateProcessingTest.java
new file mode 100644
index 0000000..7ce709e
--- /dev/null
+++ b/src/test/java/org/apache/sysds/test/functions/builtin/part1/BuiltinDateProcessingTest.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.builtin.part1;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.sysds.common.Types;
+import org.apache.sysds.common.Types.ExecType;
+import org.apache.sysds.runtime.io.FrameWriterFactory;
+import org.apache.sysds.runtime.matrix.data.FrameBlock;
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class BuiltinDateProcessingTest extends AutomatedTestBase {
+ private final static String TEST_NAME = "date_processing";
+ private final static String TEST_DIR = "functions/builtin/";
+ private static final String TEST_CLASS_DIR = TEST_DIR + BuiltinDateProcessingTest.class.getSimpleName() + "/";
+
+ @BeforeClass
+ public static void init() {
+ TestUtils.clearDirectory(TEST_DATA_DIR + TEST_CLASS_DIR);
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ if (TEST_CACHE_ENABLED) {
+ TestUtils.clearDirectory(TEST_DATA_DIR + TEST_CLASS_DIR);
+ }
+ }
+
+ @Override
+ public void setUp() {
+ TestUtils.clearAssertionInformation();
+ addTestConfiguration(TEST_NAME,new TestConfiguration(TEST_CLASS_DIR, TEST_NAME,new String[]{"B"}));
+ if (TEST_CACHE_ENABLED) {
+ setOutAndExpectedDeletionDisabled(true);
+ }
+ }
+
+ static enum TestType {
+ DOMINANT,
+ DOMINANT_DAY,
+ TIMESTAMP,
+ ADD_HOURS,
+ SUB_MON
+ }
+
+ @Test //simple conversion to dominant format
+ public void DateProcessingTest0() {
+
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003", "04-07-2003", "05-08-2004", "06-06-2006"}
+ };
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003", "04-07-2003", "05-08-2004", "06-06-2006 06:16"}
+ };
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = False, convertToDominant = True, valToAdd = 0
+ runDateProcessingTest(TestType.DOMINANT, f, ExecType.CP, 0, ref);
+ }
+
+ @Test //simple conversion to dominant format + 1 day added
+ public void DateProcessingTest1() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003", "04-07-2003", "05-08-2004", "06-06-2006 06:16"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"04-06-2003", "05-07-2003", "06-08-2004", "07-06-2006"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = False, convertToDominant = True, valToAdd = 1, timeformatToAdd = d
+ runDateProcessingTest(TestType.DOMINANT_DAY, f, ExecType.CP, 1, ref);
+ }
+//
+ @Test //simple conversion to number (timestamp in milliseconds)
+ public void DateProcessingTest2() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003", "04-07-2004 04:04:04", "05-08-2004", "06-06-2006 06:16"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"1054598400000", "1088913844000", "1091664000000", "1149574560000"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = True, convertToDominant = False, valToAdd = 0
+ runDateProcessingTest(TestType.TIMESTAMP, f, ExecType.CP, 0, ref);
+ }
+
+ @Test //simple conversion to number (timestamp in milliseconds)
+ public void DateProcessingTest2SP() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003", "04-07-2004 04:04:04", "05-08-2004", "06-06-2006 06:16"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"1054598400000", "1088913844000", "1091664000000", "1149574560000"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = True, convertToDominant = False, valToAdd = 0
+ runDateProcessingTest(TestType.TIMESTAMP, f, ExecType.SPARK, 0, ref);
+ }
+
+ @Test //no conversion only time added (3 hours)
+ public void DateProcessingTest3() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003 23:04:04", "04-07-2003 04:04:04", "05-08-2004 10:04:04", "06-06-2006 06:16"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"04-06-2003 02:04:04", "04-07-2003 07:04:04", "05-08-2004 13:04:04", "06-06-2006 09:16"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = False, convertToDominant = False, valToAdd = 3, timeformatToAdd = H
+ runDateProcessingTest(TestType.ADD_HOURS, f, ExecType.CP, 3, ref);
+ }
+
+ @Test //no conversion only time added (3 hours)
+ public void DateProcessingTest3SPARK() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"03-06-2003 23:04:04", "04-07-2003 04:04:04", "05-08-2004 10:04:04", "06-06-2006 06:16"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4"},
+ {"1", "2", "3", "4"},
+ {"04-06-2003 02:04:04", "04-07-2003 07:04:04", "05-08-2004 13:04:04", "06-06-2006 09:16"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(4, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(4, 3, testtable_ref);
+ //convertToNumber = False, convertToDominant = False, valToAdd = 3, timeformatToAdd = H
+ runDateProcessingTest(TestType.ADD_HOURS, f, ExecType.SPARK, 3, ref);
+ }
+
+ @Test //no conversion, only substraction of 3 months
+ public void DateProcessingTest12() {
+ String[][] testtable = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4", "pseudo_value5", "08-09-2013 22:12", "05-08-2004"},
+ {"03-06-2003", "04 Sep 2006 09:06", "20040805121518", "15-06-2012 11:12","10 Oct 2010 10:10",
+ "06/07/2006 06:16:44", "06 Mar 2010 12:20"},
+ {"1", "2", "3", "4", "5", "6", "05-08-2004"}
+ };
+ String[][] testtable_ref = new String[][]{
+ {"pseudo_value1", "pseudo_value2", "pseudo_value3", "pseudo_value4", "pseudo_value5", "08-09-2013 22:12", "05-08-2004"},
+ {"03-03-2003", "04 Jun 2006 09:06", "20040505121518", "15-03-2012 11:12","10 Jul 2010 10:10",
+ "03/07/2006 06:16:44", "06 Dec 2009 12:20"},
+ {"1", "2", "3", "4", "5", "6", "05-08-2004"}
+ };
+
+ FrameBlock f = generateRandomFrameBlock(7, 3, testtable);
+ FrameBlock ref = generateRandomFrameBlock(7, 3, testtable_ref);
+ //convertToNumber = False, convertToDominant = False, valToAdd = -3 , timeformatToAdd = M
+ runDateProcessingTest(TestType.SUB_MON,f, ExecType.CP, -3, ref);
+ }
+
+ private void runDateProcessingTest(TestType testName, FrameBlock test_frame, ExecType et, int valToAdd, FrameBlock reference)
+ {
+ Types.ExecMode platformOld = setExecMode(et);
+ try {
+ getAndLoadTestConfiguration(TEST_NAME);
+
+ String HOME = SCRIPT_DIR + TEST_DIR;
+ fullDMLScriptName = HOME + TEST_NAME + ".dml";
+ programArgs = new String[] {"-nvargs", "F=" + input("F"),
+ "Y=" + output("Y"), "valToAdd=" + valToAdd, "test="+testName.toString()};
+ FrameWriterFactory.createFrameWriter(Types.FileFormat.CSV).
+ writeFrameToHDFS(test_frame, input("F"), test_frame.getNumRows(), test_frame.getNumColumns());
+ System.out.println("running test ...");
+ runTest(true, false, null, -1);
+ System.out.println("Done. Validating results.");
+ FrameBlock outputFrame = readDMLFrameFromHDFS("Y", Types.FileFormat.CSV);
+
+ validateResults(reference, outputFrame);
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ finally {
+ resetExecMode(platformOld);
+ }
+ }
+
+ private void validateResults (FrameBlock reference, FrameBlock results){
+ int cols = results.getNumColumns();
+ int rows = results.getNumRows();
+
+ for (int col = 0; col < cols; col++) {
+ for (int row = 0; row < rows; row++) {
+ assertEquals(reference.get(row, col), results.get(row, col));
+ System.out.println("ref: " + reference.get(row, col) + ", out: " + results.get(row, col));
+ }
+ }
+ }
+
+ private static FrameBlock generateRandomFrameBlock(int rows, int cols, String[][] defined_strings)
+ {
+ Types.ValueType[] schema = new Types.ValueType[cols];
+ for(int i = 0; i < cols; i++) {
+ schema[i] = Types.ValueType.STRING;
+ }
+
+ if(defined_strings != null) {
+ String[] names = new String[cols];
+ for(int i = 0; i < cols; i++)
+ names[i] = schema[i].toString();
+ FrameBlock frameBlock = new FrameBlock(schema, names);
+ frameBlock.ensureAllocatedColumns(rows);
+ for(int row = 0; row < rows; row++)
+ for(int col = 0; col < cols; col++)
+ frameBlock.set(row, col, defined_strings[col][row]);
+ return frameBlock;
+ }
+ return TestUtils.generateRandomFrameBlock(rows, cols, schema ,TestUtils.getPositiveRandomInt());
+ }
+}
diff --git a/src/test/scripts/functions/builtin/date_processing.dml b/src/test/scripts/functions/builtin/date_processing.dml
new file mode 100644
index 0000000..0997513
--- /dev/null
+++ b/src/test/scripts/functions/builtin/date_processing.dml
@@ -0,0 +1,63 @@
+#-------------------------------------------------------------
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#-------------------------------------------------------------
+# Date processing functionalities supported via map() function.
+#
+# 1. convert date to number
+# 2. find dominating pattern in the date column and convert all dates in the date column accordingly
+# 3. addition and substraction on date column
+#
+# USAGE INFORMATION:
+# ---------------------------------------------------------------------------------------------------------------------------
+# Functionalities
+# ---------------------------------------------------------------------------------------------------------------------------
+# Date column gets converted to number (timestamp)
+# Date column gets converted to dominant date format in the date column
+# Hours, mins, secs, and days added to date use formats ("d", "H", "ms", "s", "m" = minutes, "M" = months, "w", and "y")
+# Hours, mins, secs, and days subtracted from date
+
+#
+X = read($F, data_type="frame", format="csv", header=FALSE)
+valToAdd=$valToAdd
+test=$test
+Y = X
+if(test == "DOMINANT") # convert to dominant date format in the date column
+ Y[, 3] = map(X[, 3], "x -> UtilFunctions.getDominantDateFormat(x)", margin=2)
+else if(test == "DOMINANT_DAY") # convert to dominant format and add days
+{
+ D = map(X[, 3], "x -> UtilFunctions.getDominantDateFormat(x)", margin=2)
+ Y[, 3] = map(D, "x -> UtilFunctions.addTimeToDate(x, "+valToAdd+", \"d\")")
+
+}
+else if(test == "TIMESTAMP") # convert date to timestamp
+{
+ Y[, 3] = map(X[, 3], "x -> UtilFunctions.getTimestamp(x)", margin=2)
+}
+else if(test == "ADD_HOURS") # add hours to date
+{
+ Y[, 3] = map(X[, 3], "x -> UtilFunctions.addTimeToDate(x, "+valToAdd+", \"H\")")
+}
+else if(test == "SUB_MON") # add hours to date
+{
+ Y[, 2] = map(X[, 2], "x -> UtilFunctions.addTimeToDate(x, "+valToAdd+", \"M\")")
+}
+else
+ print("test no supported")
+write(Y, $Y, format = "csv")