diff --git a/src/adql/parser/ADQLQueryFactory.java b/src/adql/parser/ADQLQueryFactory.java
index b24f5adb2dcaa30666d85c23598f0866ca2996b7..03abe421fbc50f01c85685ac6d5c732b4746eb44 100644
--- a/src/adql/parser/ADQLQueryFactory.java
+++ b/src/adql/parser/ADQLQueryFactory.java
@@ -64,6 +64,7 @@ import adql.query.operand.function.MathFunction;
 import adql.query.operand.function.MathFunctionType;
 import adql.query.operand.function.SQLFunction;
 import adql.query.operand.function.SQLFunctionType;
+import adql.query.operand.function.UnitConversionFunction;
 import adql.query.operand.function.UserDefinedFunction;
 import adql.query.operand.function.geometry.AreaFunction;
 import adql.query.operand.function.geometry.BoxFunction;
@@ -317,6 +318,11 @@ public class ADQLQueryFactory {
 		return new MathFunction(type, param1, param2);
 	}
 
+	/** @since 2.0 */
+	public UnitConversionFunction createUnitConversionFunction(final ADQLOperand value, final ADQLOperand targetUnit) throws Exception {
+		return new UnitConversionFunction(value, targetUnit);
+	}
+
 	/**
 	 * Creates the user defined functions called as the given name and with
 	 * the given parameters.
diff --git a/src/adql/parser/feature/FeatureSet.java b/src/adql/parser/feature/FeatureSet.java
index 69ea872fb25095a4a02f1f1335e4939b22de4183..70ac87c5934f17d2af376daf1990841939a56b88 100644
--- a/src/adql/parser/feature/FeatureSet.java
+++ b/src/adql/parser/feature/FeatureSet.java
@@ -32,6 +32,7 @@ import adql.query.ClauseOffset;
 import adql.query.constraint.ComparisonOperator;
 import adql.query.operand.BitNotOperand;
 import adql.query.operand.OperationType;
+import adql.query.operand.function.UnitConversionFunction;
 import adql.query.operand.function.geometry.AreaFunction;
 import adql.query.operand.function.geometry.BoxFunction;
 import adql.query.operand.function.geometry.CentroidFunction;
@@ -584,9 +585,7 @@ public class FeatureSet implements Iterable<LanguageFeature> {
 	
 	public static final LanguageFeature WITH = new LanguageFeature(FeatureType.ADQL_COMMON_TABLE, "WITH");  // TODO WITH
 	
-	public static final LanguageFeature CAST = new LanguageFeature(FeatureType.ADQL_TYPE, "CAST");  // TODO CAST
-	
-	public static final LanguageFeature IN_UNIT = new LanguageFeature(FeatureType.ADQL_UNIT, "IN_UNIT");  // TODO IN_UNIT*/
+	public static final LanguageFeature CAST = new LanguageFeature(FeatureType.ADQL_TYPE, "CAST");  // TODO CAST*/
 
 	/** All standard features available.
 	 * <p>
@@ -598,7 +597,7 @@ public class FeatureSet implements Iterable<LanguageFeature> {
 	 * <p><i><b>Important note:</b>
 	 * 	All of them must be optional and must have a type.
 	 * </i></p> */
-	static LanguageFeature[] availableFeatures = new LanguageFeature[]{ BitNotOperand.FEATURE, OperationType.BIT_AND.getFeatureDescription(), OperationType.BIT_OR.getFeatureDescription(), OperationType.BIT_XOR.getFeatureDescription(), ClauseOffset.FEATURE, ComparisonOperator.ILIKE.getFeatureDescription(), LowerFunction.FEATURE, AreaFunction.FEATURE, BoxFunction.FEATURE, CentroidFunction.FEATURE, CircleFunction.FEATURE, ContainsFunction.FEATURE, ExtractCoord.FEATURE_COORD1, ExtractCoord.FEATURE_COORD2, ExtractCoordSys.FEATURE, DistanceFunction.FEATURE, IntersectsFunction.FEATURE, PointFunction.FEATURE, PolygonFunction.FEATURE, RegionFunction.FEATURE };
+	static LanguageFeature[] availableFeatures = new LanguageFeature[]{ UnitConversionFunction.FEATURE, BitNotOperand.FEATURE, OperationType.BIT_AND.getFeatureDescription(), OperationType.BIT_OR.getFeatureDescription(), OperationType.BIT_XOR.getFeatureDescription(), ClauseOffset.FEATURE, ComparisonOperator.ILIKE.getFeatureDescription(), LowerFunction.FEATURE, AreaFunction.FEATURE, BoxFunction.FEATURE, CentroidFunction.FEATURE, CircleFunction.FEATURE, ContainsFunction.FEATURE, ExtractCoord.FEATURE_COORD1, ExtractCoord.FEATURE_COORD2, ExtractCoordSys.FEATURE, DistanceFunction.FEATURE, IntersectsFunction.FEATURE, PointFunction.FEATURE, PolygonFunction.FEATURE, RegionFunction.FEATURE };
 
 	/**
 	 * List all available language features.
diff --git a/src/adql/parser/grammar/adqlGrammar201.jj b/src/adql/parser/grammar/adqlGrammar201.jj
index 2a5443ba611d7551979fbde2b5248cb069705338..8568988ea0e5f6cb8b9fefaef22330d1311a8315 100644
--- a/src/adql/parser/grammar/adqlGrammar201.jj
+++ b/src/adql/parser/grammar/adqlGrammar201.jj
@@ -387,6 +387,13 @@ TOKEN : {
 |	< TAN: "TAN" >     { matchedToken.adqlReserved = matchedToken.isFunctionName = true; }
 }
 
+/* ******************** */
+/* Conversion functions */
+/* ******************** */
+TOKEN : {
+	< IN_UNIT: "IN_UNIT" >   { matchedToken.adqlReserved = matchedToken.isFunctionName = true; }
+}
+
 /* ******* */
 /* Comment */
 /* ******* */
@@ -1493,10 +1500,24 @@ ADQLFunction NumericFunction(): {ADQLFunction fct;} {
 	(fct=MathFunction()
 	| fct=TrigFunction()
 	| fct=GeometryFunction()
+	| fct=UnitConversionFunction()
 	| fct=UserDefinedFunction() { ((UserDefinedFunction)fct).setExpectedType('N'); })
 	{return fct;}
 }
 
+UnitConversionFunction UnitConversionFunction() : { Token start, end; ADQLOperand value, destUnit; } {
+	start=<IN_UNIT> <LEFT_PAR> value=NumericExpression() <COMMA> destUnit=StringExpression() end=<RIGHT_PAR>
+	{
+		try {
+			UnitConversionFunction fct = queryFactory.createUnitConversionFunction(value, destUnit);
+			fct.setPosition(new TextPosition(start, end));
+			return fct;
+		}catch(Exception ex) {
+			throw generateParseException(ex);
+		}
+	}
+}
+
 MathFunction MathFunction(): {Token fct=null, end; ADQLOperand param1=null, param2=null; NumericConstant integerValue = null;} {
 	try{
 		((fct=<ABS> <LEFT_PAR> param1=NumericExpression() end=<RIGHT_PAR>)
diff --git a/src/adql/query/operand/function/UnitConversionFunction.java b/src/adql/query/operand/function/UnitConversionFunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..00bbac99e7114d76e3e7c2a1991537fbc7fa94f7
--- /dev/null
+++ b/src/adql/query/operand/function/UnitConversionFunction.java
@@ -0,0 +1,202 @@
+package adql.query.operand.function;
+
+/*
+ * This file is part of ADQLLibrary.
+ *
+ * ADQLLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ADQLLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ */
+
+import adql.parser.feature.LanguageFeature;
+import adql.query.ADQLObject;
+import adql.query.operand.ADQLOperand;
+
+/**
+ * It represents the IN_UNIT function of ADQL.
+ *
+ * <p>This function converts the given value into the given VO-Unit.</p>
+ *
+ * <p>
+ * 	This function should report an error if the specified unit is not valid or
+ * 	if the conversion is not possible.
+ * </p>
+ *
+ * @author Gr&eacute;gory Mantelet (CDS)
+ * @version 2.0 (08/2019)
+ * @since 2.0
+ */
+public class UnitConversionFunction extends ADQLFunction {
+
+	/** Description of this ADQL Feature. */
+	public static final LanguageFeature FEATURE = new LanguageFeature(LanguageFeature.TYPE_ADQL_UNIT, "IN_UNIT", true, "Convert the given value (1st argument) into the given VO-Unit (2nd argument).");
+
+	/** Constant name of this function. */
+	protected final String FCT_NAME = "IN_UNIT";
+
+	/** The value to convert. */
+	protected ADQLOperand value;
+
+	/** The VO-Unit into which the value must be converted. */
+	protected ADQLOperand targetUnit;
+
+	/**
+	 * Create the object representation of the ADQL function IN_UNIT.
+	 *
+	 * @param value			The value to convert.
+	 * @param targetUnit	The VO-Unit into which the value must be converted.
+	 *
+	 * @throws NullPointerException		If one of the given operands is NULL.
+	 * @throws IllegalArgumentException	If the 1st operand is not a numeric,
+	 *                                 	or if the 2nd is not a string.
+	 */
+	public UnitConversionFunction(final ADQLOperand value, final ADQLOperand targetUnit) throws NullPointerException, IllegalArgumentException {
+		setValue(value);
+		setTargetUnit(targetUnit);
+	}
+
+	/**
+	 * Get the numeric operand to convert into a different unit.
+	 *
+	 * @return	The value to convert.
+	 */
+	public final ADQLOperand getValue() {
+		return value;
+	}
+
+	/**
+	 * Set the numeric operand to convert into a different unit.
+	 *
+	 * @param value	The value to convert.
+	 *
+	 * @throws NullPointerException		If the given operand is NULL.
+	 * @throws IllegalArgumentException	If the given operand is not a numeric.
+	 */
+	public final void setValue(final ADQLOperand value) throws NullPointerException, IllegalArgumentException {
+		if (value == null)
+			throw new NullPointerException("The 1st argument of the ADQL function " + FCT_NAME + " (i.e. the value to convert) must be non-NULL!");
+
+		if (!value.isNumeric())
+			throw new IllegalArgumentException("The 1st argument of the ADQL function " + FCT_NAME + " (i.e. the value to convert) must be a numeric!");
+
+		this.value = value;
+	}
+
+	/**
+	 * Get the VO-Unit into which the value must be converted.
+	 *
+	 * @return	The target unit.
+	 */
+	public final ADQLOperand getTargetUnit() {
+		return targetUnit;
+	}
+
+	/**
+	 * Set the VO-Unit into which the value must be converted.
+	 *
+	 * @param targetUnit	The target unit.
+	 *
+	 * @throws NullPointerException		If the given operand is NULL.
+	 * @throws IllegalArgumentException	If the given operand is not a string.
+	 */
+	public final void setTargetUnit(final ADQLOperand targetUnit) throws NullPointerException, IllegalArgumentException {
+		if (targetUnit == null)
+			throw new NullPointerException("The 2nd argument of the ADQL function " + FCT_NAME + " (i.e. target unit) must be non-NULL!");
+
+		if (!targetUnit.isString())
+			throw new IllegalArgumentException("The 2nd argument of the ADQL function " + FCT_NAME + " (i.e. target unit) must be of type VARCHAR (i.e. a string)!");
+
+		this.targetUnit = targetUnit;
+	}
+
+	@Override
+	public final boolean isNumeric() {
+		return true;
+	}
+
+	@Override
+	public final boolean isString() {
+		return false;
+	}
+
+	@Override
+	public final boolean isGeometry() {
+		return false;
+	}
+
+	@Override
+	public String getName() {
+		return FCT_NAME;
+	}
+
+	@Override
+	public LanguageFeature getFeatureDescription() {
+		return FEATURE;
+	}
+
+	@Override
+	public ADQLObject getCopy() throws Exception {
+		return new UnitConversionFunction((ADQLOperand)value.getCopy(), (ADQLOperand)targetUnit.getCopy());
+	}
+
+	@Override
+	public int getNbParameters() {
+		return 2;
+	}
+
+	@Override
+	public ADQLOperand[] getParameters() {
+		return new ADQLOperand[]{ value, targetUnit };
+	}
+
+	@Override
+	public ADQLOperand getParameter(int index) throws ArrayIndexOutOfBoundsException {
+		if (index < 0 || index >= getNbParameters())
+			throw new ArrayIndexOutOfBoundsException("No " + index + "-th parameter for the function \"" + FCT_NAME + "\" (nb required params = " + getNbParameters() + ")!");
+
+		switch(index) {
+			case 0:
+				return value;
+			case 1:
+				return targetUnit;
+			default:
+				return null;
+		}
+	}
+
+	@Override
+	public ADQLOperand setParameter(int index, ADQLOperand replacer) throws ArrayIndexOutOfBoundsException, NullPointerException, Exception {
+		if (index < 0 || index >= getNbParameters())
+			throw new ArrayIndexOutOfBoundsException("No " + index + "-th parameter for the function \"" + FCT_NAME + "\" (nb required params = " + getNbParameters() + ")!");
+		else if (replacer == null)
+			throw new NullPointerException("Impossible to remove any parameter from the " + FCT_NAME + " function! All parameters are required!");
+		else {
+			ADQLOperand replaced = null;
+			switch(index) {
+				case 0:
+					replaced = value;
+					setValue(replacer);
+					setPosition(null);
+					break;
+				case 1:
+					replaced = targetUnit;
+					setTargetUnit(replacer);
+					setPosition(null);
+					break;
+			}
+			return replaced;
+		}
+	}
+
+}
diff --git a/src/adql/query/operand/function/string/LowerFunction.java b/src/adql/query/operand/function/string/LowerFunction.java
index 817a7fe94986dd48132168125efe3e0a53f1c7ec..676323dc3109c4ec07a2cfbfed714818386c9c2d 100644
--- a/src/adql/query/operand/function/string/LowerFunction.java
+++ b/src/adql/query/operand/function/string/LowerFunction.java
@@ -63,16 +63,17 @@ public class LowerFunction extends ADQLFunction {
 	/**
 	 * Builds a LOWER function with its parameter.
 	 *
-	 * @param param					Parameter of LOWER.
-	 * @throws NullPointerException	If the given operand is NULL
-	 *                             	or if it's not a string parameter.
+	 * @param param		Parameter of LOWER.
+	 *
+	 * @throws NullPointerException		If the given operand is NULL.
+	 * @throws IllegalArgumentException	If the operand is not a string parameter.
 	 */
-	public LowerFunction(final ADQLOperand strParam) {
+	public LowerFunction(final ADQLOperand strParam) throws NullPointerException, IllegalArgumentException {
 		if (strParam == null)
 			throw new NullPointerException("The function " + FCT_NAME + " must have one non-NULL parameter!");
 
 		if (!strParam.isString())
-			throw new NullPointerException("The ADQL function " + FCT_NAME + " must have one parameter of type VARCHAR (i.e. a String)!");
+			throw new IllegalArgumentException("The ADQL function " + FCT_NAME + " must have one parameter of type VARCHAR (i.e. a String)!");
 
 		this.strParam = strParam;
 	}