@@ -1294,87 +1294,181 @@ case class ParseToTimestamp(left: Expression, format: Option[Expression], child:
12941294 override def dataType : DataType = TimestampType
12951295}
12961296
1297- /**
1298- * Returns date truncated to the unit specified by the format.
1299- */
1300- // scalastyle:off line.size.limit
1301- @ ExpressionDescription (
1302- usage = " _FUNC_(date, fmt) - Returns `date` with the time portion of the day truncated to the unit specified by the format model `fmt`." ,
1303- examples = """
1304- Examples:
1305- > SELECT _FUNC_('2009-02-12', 'MM');
1306- 2009-02-01
1307- > SELECT _FUNC_('2015-10-27', 'YEAR');
1308- 2015-01-01
1309- """ ,
1310- since = " 1.5.0" )
1311- // scalastyle:on line.size.limit
1312- case class TruncDate (date : Expression , format : Expression )
1313- extends BinaryExpression with ImplicitCastInputTypes {
1314- override def left : Expression = date
1315- override def right : Expression = format
1316-
1317- override def inputTypes : Seq [AbstractDataType ] = Seq (DateType , StringType )
1318- override def dataType : DataType = DateType
1297+ trait TruncInstant extends BinaryExpression with ImplicitCastInputTypes {
1298+ val instant : Expression
1299+ val format : Expression
13191300 override def nullable : Boolean = true
1320- override def prettyName : String = " trunc"
13211301
13221302 private lazy val truncLevel : Int =
13231303 DateTimeUtils .parseTruncLevel(format.eval().asInstanceOf [UTF8String ])
13241304
1325- override def eval (input : InternalRow ): Any = {
1305+ /**
1306+ * @param input internalRow (time)
1307+ * @param maxLevel Maximum level that can be used for truncation (e.g MONTH for Date input)
1308+ * @param truncFunc function: (time, level) => time
1309+ */
1310+ protected def evalHelper (input : InternalRow , maxLevel : Int )(
1311+ truncFunc : (Any , Int ) => Any ): Any = {
13261312 val level = if (format.foldable) {
13271313 truncLevel
13281314 } else {
13291315 DateTimeUtils .parseTruncLevel(format.eval().asInstanceOf [UTF8String ])
13301316 }
1331- if (level == - 1 ) {
1332- // unknown format
1317+ if (level == DateTimeUtils . TRUNC_INVALID || level > maxLevel ) {
1318+ // unknown format or too large level
13331319 null
13341320 } else {
1335- val d = date .eval(input)
1336- if (d == null ) {
1321+ val t = instant .eval(input)
1322+ if (t == null ) {
13371323 null
13381324 } else {
1339- DateTimeUtils .truncDate(d. asInstanceOf [ Int ] , level)
1325+ truncFunc(t , level)
13401326 }
13411327 }
13421328 }
13431329
1344- override def doGenCode (ctx : CodegenContext , ev : ExprCode ): ExprCode = {
1330+ protected def codeGenHelper (
1331+ ctx : CodegenContext ,
1332+ ev : ExprCode ,
1333+ maxLevel : Int ,
1334+ orderReversed : Boolean = false )(
1335+ truncFunc : (String , String ) => String )
1336+ : ExprCode = {
13451337 val dtu = DateTimeUtils .getClass.getName.stripSuffix(" $" )
13461338
13471339 if (format.foldable) {
1348- if (truncLevel == - 1 ) {
1340+ if (truncLevel == DateTimeUtils . TRUNC_INVALID || truncLevel > maxLevel ) {
13491341 ev.copy(code = s """
13501342 boolean ${ev.isNull} = true;
13511343 ${ctx.javaType(dataType)} ${ev.value} = ${ctx.defaultValue(dataType)}; """ )
13521344 } else {
1353- val d = date.genCode(ctx)
1345+ val t = instant.genCode(ctx)
1346+ val truncFuncStr = truncFunc(t.value, truncLevel.toString)
13541347 ev.copy(code = s """
1355- ${d .code}
1356- boolean ${ev.isNull} = ${d .isNull};
1348+ ${t .code}
1349+ boolean ${ev.isNull} = ${t .isNull};
13571350 ${ctx.javaType(dataType)} ${ev.value} = ${ctx.defaultValue(dataType)};
13581351 if (! ${ev.isNull}) {
1359- ${ev.value} = $dtu.truncDate( ${d.value} , $truncLevel ) ;
1352+ ${ev.value} = $dtu. $truncFuncStr ;
13601353 } """ )
13611354 }
13621355 } else {
1363- nullSafeCodeGen(ctx, ev, (dateVal, fmt ) => {
1356+ nullSafeCodeGen(ctx, ev, (left, right ) => {
13641357 val form = ctx.freshName(" form" )
1358+ val (dateVal, fmt) = if (orderReversed) {
1359+ (right, left)
1360+ } else {
1361+ (left, right)
1362+ }
1363+ val truncFuncStr = truncFunc(dateVal, form)
13651364 s """
13661365 int $form = $dtu.parseTruncLevel( $fmt);
1367- if ( $form == -1) {
1366+ if ( $form == -1 || $form > $maxLevel ) {
13681367 ${ev.isNull} = true;
13691368 } else {
1370- ${ev.value} = $dtu.truncDate( $dateVal , $form );
1369+ ${ev.value} = $dtu. $truncFuncStr
13711370 }
13721371 """
13731372 })
13741373 }
13751374 }
13761375}
13771376
1377+ /**
1378+ * Returns date truncated to the unit specified by the format.
1379+ */
1380+ // scalastyle:off line.size.limit
1381+ @ ExpressionDescription (
1382+ usage = """
1383+ _FUNC_(date, fmt) - Returns `date` with the time portion of the day truncated to the unit specified by the format model `fmt`.
1384+ `fmt` should be one of ["year", "yyyy", "yy", "mon", "month", "mm"]
1385+ """ ,
1386+ examples = """
1387+ Examples:
1388+ > SELECT _FUNC_('2009-02-12', 'MM');
1389+ 2009-02-01
1390+ > SELECT _FUNC_('2015-10-27', 'YEAR');
1391+ 2015-01-01
1392+ """ ,
1393+ since = " 1.5.0" )
1394+ // scalastyle:on line.size.limit
1395+ case class TruncDate (date : Expression , format : Expression )
1396+ extends TruncInstant {
1397+ override def left : Expression = date
1398+ override def right : Expression = format
1399+
1400+ override def inputTypes : Seq [AbstractDataType ] = Seq (DateType , StringType )
1401+ override def dataType : DataType = DateType
1402+ override def prettyName : String = " trunc"
1403+ override val instant = date
1404+
1405+ override def eval (input : InternalRow ): Any = {
1406+ evalHelper(input, maxLevel = DateTimeUtils .TRUNC_TO_MONTH ) { (d : Any , level : Int ) =>
1407+ DateTimeUtils .truncDate(d.asInstanceOf [Int ], level)
1408+ }
1409+ }
1410+
1411+ override def doGenCode (ctx : CodegenContext , ev : ExprCode ): ExprCode = {
1412+ codeGenHelper(ctx, ev, maxLevel = DateTimeUtils .TRUNC_TO_MONTH ) { (date : String , fmt : String ) =>
1413+ s " truncDate( $date, $fmt); "
1414+ }
1415+ }
1416+ }
1417+
1418+ /**
1419+ * Returns timestamp truncated to the unit specified by the format.
1420+ */
1421+ // scalastyle:off line.size.limit
1422+ @ ExpressionDescription (
1423+ usage = """
1424+ _FUNC_(fmt, ts) - Returns timestamp `ts` truncated to the unit specified by the format model `fmt`.
1425+ `fmt` should be one of ["YEAR", "YYYY", "YY", "MON", "MONTH", "MM", "DAY", "DD", "HOUR", "MINUTE", "SECOND", "WEEK", "QUARTER"]
1426+ """ ,
1427+ examples = """
1428+ Examples:
1429+ > SELECT _FUNC_('2015-03-05T09:32:05.359', 'YEAR');
1430+ 2015-01-01T00:00:00
1431+ > SELECT _FUNC_('2015-03-05T09:32:05.359', 'MM');
1432+ 2015-03-01T00:00:00
1433+ > SELECT _FUNC_('2015-03-05T09:32:05.359', 'DD');
1434+ 2015-03-05T00:00:00
1435+ > SELECT _FUNC_('2015-03-05T09:32:05.359', 'HOUR');
1436+ 2015-03-05T09:00:00
1437+ """ ,
1438+ since = " 2.3.0" )
1439+ // scalastyle:on line.size.limit
1440+ case class TruncTimestamp (
1441+ format : Expression ,
1442+ timestamp : Expression ,
1443+ timeZoneId : Option [String ] = None )
1444+ extends TruncInstant with TimeZoneAwareExpression {
1445+ override def left : Expression = format
1446+ override def right : Expression = timestamp
1447+
1448+ override def inputTypes : Seq [AbstractDataType ] = Seq (StringType , TimestampType )
1449+ override def dataType : TimestampType = TimestampType
1450+ override def prettyName : String = " date_trunc"
1451+ override val instant = timestamp
1452+ override def withTimeZone (timeZoneId : String ): TimeZoneAwareExpression =
1453+ copy(timeZoneId = Option (timeZoneId))
1454+
1455+ def this (format : Expression , timestamp : Expression ) = this (format, timestamp, None )
1456+
1457+ override def eval (input : InternalRow ): Any = {
1458+ evalHelper(input, maxLevel = DateTimeUtils .TRUNC_TO_SECOND ) { (t : Any , level : Int ) =>
1459+ DateTimeUtils .truncTimestamp(t.asInstanceOf [Long ], level, timeZone)
1460+ }
1461+ }
1462+
1463+ override def doGenCode (ctx : CodegenContext , ev : ExprCode ): ExprCode = {
1464+ val tz = ctx.addReferenceObj(" timeZone" , timeZone)
1465+ codeGenHelper(ctx, ev, maxLevel = DateTimeUtils .TRUNC_TO_SECOND , true ) {
1466+ (date : String , fmt : String ) =>
1467+ s " truncTimestamp( $date, $fmt, $tz); "
1468+ }
1469+ }
1470+ }
1471+
13781472/**
13791473 * Returns the number of days from startDate to endDate.
13801474 */
0 commit comments