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")