@@ -1294,87 +1294,181 @@ case class ParseToTimestamp(left: Expression, format: Option[Expression], child:
1294
1294
override def dataType : DataType = TimestampType
1295
1295
}
1296
1296
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
1319
1300
override def nullable : Boolean = true
1320
- override def prettyName : String = " trunc"
1321
1301
1322
1302
private lazy val truncLevel : Int =
1323
1303
DateTimeUtils .parseTruncLevel(format.eval().asInstanceOf [UTF8String ])
1324
1304
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 = {
1326
1312
val level = if (format.foldable) {
1327
1313
truncLevel
1328
1314
} else {
1329
1315
DateTimeUtils .parseTruncLevel(format.eval().asInstanceOf [UTF8String ])
1330
1316
}
1331
- if (level == - 1 ) {
1332
- // unknown format
1317
+ if (level == DateTimeUtils . TRUNC_INVALID || level > maxLevel ) {
1318
+ // unknown format or too large level
1333
1319
null
1334
1320
} else {
1335
- val d = date .eval(input)
1336
- if (d == null ) {
1321
+ val t = instant .eval(input)
1322
+ if (t == null ) {
1337
1323
null
1338
1324
} else {
1339
- DateTimeUtils .truncDate(d. asInstanceOf [ Int ] , level)
1325
+ truncFunc(t , level)
1340
1326
}
1341
1327
}
1342
1328
}
1343
1329
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 = {
1345
1337
val dtu = DateTimeUtils .getClass.getName.stripSuffix(" $" )
1346
1338
1347
1339
if (format.foldable) {
1348
- if (truncLevel == - 1 ) {
1340
+ if (truncLevel == DateTimeUtils . TRUNC_INVALID || truncLevel > maxLevel ) {
1349
1341
ev.copy(code = s """
1350
1342
boolean ${ev.isNull} = true;
1351
1343
${ctx.javaType(dataType)} ${ev.value} = ${ctx.defaultValue(dataType)}; """ )
1352
1344
} else {
1353
- val d = date.genCode(ctx)
1345
+ val t = instant.genCode(ctx)
1346
+ val truncFuncStr = truncFunc(t.value, truncLevel.toString)
1354
1347
ev.copy(code = s """
1355
- ${d .code}
1356
- boolean ${ev.isNull} = ${d .isNull};
1348
+ ${t .code}
1349
+ boolean ${ev.isNull} = ${t .isNull};
1357
1350
${ctx.javaType(dataType)} ${ev.value} = ${ctx.defaultValue(dataType)};
1358
1351
if (! ${ev.isNull}) {
1359
- ${ev.value} = $dtu.truncDate( ${d.value} , $truncLevel ) ;
1352
+ ${ev.value} = $dtu. $truncFuncStr ;
1360
1353
} """ )
1361
1354
}
1362
1355
} else {
1363
- nullSafeCodeGen(ctx, ev, (dateVal, fmt ) => {
1356
+ nullSafeCodeGen(ctx, ev, (left, right ) => {
1364
1357
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)
1365
1364
s """
1366
1365
int $form = $dtu.parseTruncLevel( $fmt);
1367
- if ( $form == -1) {
1366
+ if ( $form == -1 || $form > $maxLevel ) {
1368
1367
${ev.isNull} = true;
1369
1368
} else {
1370
- ${ev.value} = $dtu.truncDate( $dateVal , $form );
1369
+ ${ev.value} = $dtu. $truncFuncStr
1371
1370
}
1372
1371
"""
1373
1372
})
1374
1373
}
1375
1374
}
1376
1375
}
1377
1376
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
+
1378
1472
/**
1379
1473
* Returns the number of days from startDate to endDate.
1380
1474
*/
0 commit comments