From 357dd695a9481f5796a49ed659f4fe91a5b7106a Mon Sep 17 00:00:00 2001 From: Ninglin Du Date: Wed, 27 Aug 2025 16:46:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?-=20maxGrain=20=E8=A1=A5=E5=85=85=E5=9C=A8?= =?UTF-8?q?=E6=9B=B4=E5=A4=9Apredicate=E4=B8=AD=E7=9A=84=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=20-=20intersect=20=E5=A2=9E=E5=8A=A0=20maxGrain=20=E8=80=83?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiaomi/duckling/dimension/time/Rules.scala | 5 ++++- .../com/xiaomi/duckling/dimension/time/Time.scala | 14 +++++++------- .../time/helper/TimePredicateHelpers.scala | 8 ++++---- .../xiaomi/duckling/dimension/time/package.scala | 2 +- .../duckling/dimension/time/predicates.scala | 12 +++++++++--- .../xiaomi/duckling/dimension/time/TimeTest.scala | 8 ++++++-- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Rules.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Rules.scala index cd8779fe..c76c154d 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Rules.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Rules.scala @@ -213,7 +213,10 @@ trait Rules extends DimRules { if td1.timeGrain > td2.timeGrain && !(td1.hint == Hint.Date && td2.hint == Hint.Date) || // 上午的8-9点 td1.timeGrain == td2.timeGrain && td1.timeGrain == Hour && isAPartOfDay(t1) && !isAPartOfDay(t2) => - intersectToken(options, td1, td2) + val g1 = td1.timePred.maxGrain + val g2 = td2.timePred.maxGrain + if (g1.isDefined && g2.isDefined && g1.get < g2.get) None + else intersectToken(options, td1, td2) } ) diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Time.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Time.scala index e08974c9..f3c14189 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Time.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Time.scala @@ -208,7 +208,7 @@ case class TimeData(timePred: TimePredicate, case TimeDatePredicate(second, minute, hour, _, _, dayOfMonth, month, year, _) => // 如:2021年10月1日12点三十分 Simple(year, month, dayOfMonth, hour, minute, second, offset=Some(false)) - case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(TimeDatePredicate(_, _, _, _, _, dayOfMonth, month, _, _), SeriesPredicate(_))) => + case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(TimeDatePredicate(_, _, _, _, _, dayOfMonth, month, _, _), SeriesPredicate(_, _))) => // 如: 明年三月一号十二点三十分, 年份隐式表达SeriesPredicate Simple(None, month, dayOfMonth, hour, minute, second, offset=Some(true)) case IntersectTimePredicate(SequencePredicate(_), TimeDatePredicate(_, _, _, _, _, _, _, year, _)) => @@ -217,26 +217,26 @@ case class TimeData(timePred: TimePredicate, case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), SequencePredicate(_)) => // 如:除夕十二点三十分,没有年份表达,除夕SequencePredicate Simple(hour=hour, minute=minute, second=second, offset=Some(false)) - case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(SequencePredicate(_), SeriesPredicate(_))) => + case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(SequencePredicate(_), SeriesPredicate(_, _))) => // 如:今年除夕十二点三十分,年份隐式表达,除夕SequencePredicate Simple(hour=hour, minute=minute, second=second, offset=Some(true)) case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(_, TimeDatePredicate(_, _, _, _, _, _, _, year, _))) => // 如:2021年[除夕、节气、西方节日]十二点三十分,有具体年份表达 Simple(year=year, hour=hour, minute=minute, second=second, offset=Some(false)) - case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(SeriesPredicate(_), SeriesPredicate(_))) => + case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, _, _, _, _), IntersectTimePredicate(SeriesPredicate(_, _), SeriesPredicate(_, _))) => // 如:明年[清明节、西方节日]十二点三十分,年份隐式表达,节气SeriesPredicate Simple(hour=hour, minute=minute, second=second, offset=Some(true)) - case IntersectTimePredicate(SeriesPredicate(_), TimeDatePredicate(second, minute, hour, _, _, _, _, year, _)) => + case IntersectTimePredicate(SeriesPredicate(_, _), TimeDatePredicate(second, minute, hour, _, _, _, _, year, _)) => // 如:2021年[清明节、西方节日],节气需要根据年份查表获取具体日期,只抽取年,节气SeriesPredicate Simple(year=year, hour=hour, minute=minute, second=second, offset=Some(false)) - case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, dayOfMonth, month, _, _), SeriesPredicate(_)) => + case IntersectTimePredicate(TimeDatePredicate(second, minute, hour, _, _, dayOfMonth, month, _, _), SeriesPredicate(_, grain)) => // 如:[清明节、西方节日]十二点三十分,今年国庆节,明年中秋节,今天十二点三十分,年份/日期隐式表达和节气SeriesPredicate val offset = (second.isDefined || minute.isDefined || hour.isDefined) && holiday.exists(_.nonEmpty) Simple(None, month, dayOfMonth, hour, minute, second, offset=Some(!offset)) - case IntersectTimePredicate(SeriesPredicate(_), SeriesPredicate(_)) => + case IntersectTimePredicate(SeriesPredicate(_, _), SeriesPredicate(_, _)) => // 如:今年[清明节、西方节日],年份隐式表达和节气SeriesPredicate Simple(offset=Some(true)) - case SeriesPredicate(_) => + case SeriesPredicate(_, _) => // 如:[清明节、西方节日]西方节日和节气SeriesPredicate Simple(offset=if (holiday.exists(_.nonEmpty)) Some(false) else Some(true)) case _ => Simple() diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimePredicateHelpers.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimePredicateHelpers.scala index fd1ab199..ca5e1dfa 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimePredicateHelpers.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimePredicateHelpers.scala @@ -145,7 +145,7 @@ object TimePredicateHelpers { } } - SeriesPredicate(timeSeqMap(false, f, basePred)) + SeriesPredicate(timeSeqMap(false, f, basePred), basePred.maxGrain) } def timeCycle(grain: Grain): CycleSeriesPredicate = timeCycle(grain, grain) @@ -192,7 +192,7 @@ object TimePredicateHelpers { } } - SeriesPredicate(series) + SeriesPredicate(series, cycleSP.maxGrain) } /** @@ -221,7 +221,7 @@ object TimePredicateHelpers { else (Stream(nth), Stream.empty) } } - SeriesPredicate(series) + SeriesPredicate(series, f.maxGrain) } /** @@ -255,7 +255,7 @@ object TimePredicateHelpers { (Stream.iterate(anchor)(f(-1)).tail, Stream.iterate(anchor)(f(1))) } } - SeriesPredicate(series) + SeriesPredicate(series, Some(Day)) } /** diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala index 9fd8b0d1..2ddd3de2 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala @@ -144,7 +144,7 @@ package object time { def runPredicate(tp: TimePredicate): SeriesPredicateF = { tp match { case EmptyTimePredicate => EmptySeriesPredicate - case SeriesPredicate(f) => f + case SeriesPredicate(f, _) => f case TimeDatePredicate(sec, min, hour, ampm, dayOfWeek, dayOfMonth, month, year, calendar) => if (hour.isEmpty && ampm.nonEmpty) EmptySeriesPredicate else { diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/predicates.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/predicates.scala index 4332ba5d..0679b443 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/predicates.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/predicates.scala @@ -73,8 +73,9 @@ object predicates { } } - case class SeriesPredicate(f: SeriesPredicateF) extends TimePredicate { + case class SeriesPredicate(f: SeriesPredicateF, grainOpt: Option[Grain]) extends TimePredicate { override def toString: String = "f:Series" + override val maxGrain: Option[Grain] = grainOpt } case class IntersectTimePredicate(pred1: TimePredicate, pred2: TimePredicate) @@ -105,12 +106,17 @@ object predicates { /** * 用td1的部分信息替换td2的结果,解决明年的今天、上个月的今天之类的问题 */ - case class ReplacePartPredicate(td1: TimeData, td2: TimeData) extends TimePredicate + case class ReplacePartPredicate(td1: TimeData, td2: TimeData) extends TimePredicate { + override val maxGrain: Option[Grain] = td1.timeGrain + } case object EndOfGrainPredicate extends TimePredicate case class CycleSeriesPredicate(seriesF: SeriesPredicateF, n: Int, grain: Grain) - extends TimePredicate + extends TimePredicate { + + override val maxGrain: Option[Grain] = Some(grain) + } val singleNumeber: Predicate = isIntegerBetween(0, 9) diff --git a/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/time/TimeTest.scala b/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/time/TimeTest.scala index 86bc2d0c..83a58581 100644 --- a/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/time/TimeTest.scala +++ b/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/time/TimeTest.scala @@ -139,9 +139,13 @@ class TimeTest extends UnitSpec { } } - it("fix #187") { + it("不需要支持的") { val opt = options.copy(full = true, withLatent = false) - val cases = Table("query", "国庆晚", "十月一晚", "下钟", "晚") + val cases = Table("query", + // fix #187 + "国庆晚", "十月一晚", "下钟", "晚", + // fix #259 + "上午今天早上七点") forAll(cases) {query => parse(query, options = opt) should be (empty) } From 2197c6b0b51da2e15c9b3f4f319aa16b0316ad9f Mon Sep 17 00:00:00 2001 From: Ninglin Du Date: Wed, 27 Aug 2025 17:46:06 +0800 Subject: [PATCH 2/2] =?UTF-8?q?-=20maxGrain=20=E8=A1=A5=E5=85=85=E5=9C=A8?= =?UTF-8?q?=E6=9B=B4=E5=A4=9Apredicate=E4=B8=AD=E7=9A=84=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=20-=20intersect=20=E5=A2=9E=E5=8A=A0=20maxGrain=20=E8=80=83?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scala/com/xiaomi/duckling/dimension/time/package.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala index 2ddd3de2..c0f35a37 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/package.scala @@ -197,8 +197,8 @@ package object time { t2 <- resolveTimeData(t, td2) } yield { val to = - if (td2.timePred.maxGrain.nonEmpty && td1.timeGrain > td2.timePred.maxGrain.get) { - copyGrain(Year, td2.timePred.maxGrain.get, t1, t2) + if (td2.timePred.maxGrain.nonEmpty && td1.timeGrain > td2.timePred.maxGrain.get && td1.timeGrain.finer().isDefined) { + copyGrain(Year, td1.timeGrain.finer().get, t1, t2) } else { (td1.timeGrain, td2.timeGrain) match { case (Year, Month) => t2.copy(start = t2.start.withYear(t1.start.year))