From 628627a3164da650b89dad3ad3bdaa9f86439b2e Mon Sep 17 00:00:00 2001 From: Ninglin Du Date: Thu, 1 Aug 2024 17:24:42 +0800 Subject: [PATCH 1/2] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=E5=B0=91=E9=87=8F?= =?UTF-8?q?=E8=8A=82=E6=97=A5=E8=AF=B4=E6=B3=95=20-=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=99=A4=E5=A4=95=E6=99=9A=E4=B8=8A8=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BA=86Sequence=E7=9A=84=E8=A1=A8=E8=BE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scala/com/xiaomi/duckling/Types.scala | 3 +- .../duckling/dimension/time/Rules.scala | 11 +-- .../duckling/dimension/time/Types.scala | 5 +- .../time/helper/HolidayProvider.scala | 5 +- .../duckling/dimension/time/package.scala | 70 +++++++++++++++---- .../duckling/dimension/time/Examples.scala | 5 +- .../time/helper/TimeValueHelpers.scala | 11 ++- .../dimension/time/repeat/Examples.scala | 4 +- 8 files changed, 84 insertions(+), 30 deletions(-) diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/Types.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/Types.scala index 923db434..54a51173 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/Types.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/Types.scala @@ -78,7 +78,8 @@ object Types { def and(ps: Predicate*): Predicate = { case token: Token => - ps.forall(p => p.isDefinedAt(token) && p(token)) + val result = ps.forall(p => p.isDefinedAt(token) && p(token)) + result } def or(ps: Predicate*): Predicate = { 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 a1b62edc..bba0a406 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 @@ -191,14 +191,17 @@ trait Rules extends DimRules { // else if (td1.timeGrain == Day && td2.timeGrain == Hour) None else { val hint = - if (td1.timeGrain == Year && td2.hint == MonthOnly) YearMonth + if (td1.timePred.isInstanceOf[SequencePredicate]) Sequence + else if (td1.timeGrain == Year && td2.hint == MonthOnly) YearMonth else Intersect // 10号八点,需要去掉AMPM (今天是10号9点时,不应再出20点) // 今天8点,需要根据当前时间是出8/20点 val _td2 = if (td1.hint == Hint.RecentNominal) td2 else removeAMPM(td2) val _td1 = FuzzyDayIntervals.enlarge(td1, options.timeOptions.beforeEndOfInterval) - val td = intersect(_td1, _td2).map(_.copy(hint = hint)) - tt(td) + hint match { + case Sequence => sequenceProd(_td1, _td2) + case _ => tt(intersect(_td1, _td2).map(_.copy(hint = hint))) + } } } ) @@ -533,7 +536,7 @@ trait Rules extends DimRules { case _ => if (td1.timeGrain >= td2.timeGrain) { val cal = calendar(td1, td2) - if (td1.timeGrain > td2.timeGrain && recentHint(td2.hint)) { + if (td1.timeGrain > td2.timeGrain && (td1.timePred.isInstanceOf[SequencePredicate] || recentHint(td2.hint))) { tt( TimeData( ReplacePartPredicate(td1, td2), diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Types.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Types.scala index e917ac1f..e17b6b83 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Types.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/Types.scala @@ -355,10 +355,11 @@ object Types { } def withYear(y: Int): DuckDateTime = this.copy(date = date.withYear(y)) - def withMonth(m: Int): DuckDateTime = this.copy(date = date.withMonth(m)) - def withDayOfMonth(d: Int): DuckDateTime = this.copy(date = date.withDayOfMonth(d)) + def withHour(h: Int): DuckDateTime = this.copy(time = time.withHour(h)) + def withMinute(m: Int): DuckDateTime = this.copy(time = time.withMinute(m)) + def withSecond(s: Int): DuckDateTime = this.copy(time = time.withSecond(s)) def to(calendar: Calendar): DuckDateTime = { calendar match { diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/HolidayProvider.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/HolidayProvider.scala index c24cb253..c58f9b09 100644 --- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/HolidayProvider.scala +++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/time/helper/HolidayProvider.scala @@ -50,6 +50,7 @@ class LocalHolidayProvider extends HolidayProvider { val rulePeriodicHolidays: List[(Token, String, String)] = mkRuleHolidays( // Fixed dates, year over year List( + (monthDay(1, 10), "中国人民警察节", "中国人民警察节|人民警察节|中国警察节|警察节"), (monthDay(2, 7), "国际声援南非日", "国际声援南非日|声援南非日"), (monthDay(2, 15), "中国12亿人口日", "中国12亿人口日"), (monthDay(2, 21), "反对殖民主义斗争日", "反对殖民主义斗争日|反对殖民制度斗争日"), @@ -140,7 +141,7 @@ class LocalHolidayProvider extends HolidayProvider { (monthDay(12, 12), "双十二", "双十二|双十二电商节"), (monthDay(12, 12), "西安事变纪念日", "西安事变纪念日"), (monthDay(12, 13), "南京大屠杀纪念日", "南京大屠杀纪念日"), - (monthDay(12, 13), "南京大屠杀死难者国家公祭日", "南京大屠杀死难者国家公祭日"), + (monthDay(12, 13), "南京大屠杀死难者国家公祭日", "南京大屠杀死难者国家公祭日|国家公祭日"), (monthDay(12, 2), "全国交通安全日", "全国交通安全日|交通安全日"), (monthDay(12, 20), "澳门回归纪念日", "澳门回归纪念日|澳门回归日"), (monthDay(12, 24), "平安夜", "平安夜"), @@ -160,7 +161,7 @@ class LocalHolidayProvider extends HolidayProvider { (monthDay(3, 1), "国际海豹日", "国际海豹日|海豹日"), (monthDay(3, 12), "植树节", "中国植树节|植树节"), (monthDay(3, 14), "白色情人节", "白色情人节"), - (monthDay(3, 15), "国际消费者权益日", "国际消费者权益日|世界消费者权益日|消费者权益日|三一五"), + (monthDay(3, 15), "国际消费者权益日", "国际消费者权益日|世界消费者权益日|消费者权益日|三一五|消费者日"), (monthDay(3, 17), "中国国医节", "中国国医节|国医节"), (monthDay(3, 17), "圣帕特里克节", "圣帕特里克节"), (monthDay(3, 21), "世界森林日", "世界森林日|森林日"), 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 af1c19bf..2a5239fd 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 @@ -191,26 +191,68 @@ package object time { t1 <- resolveTimeData(t, td1) t2 <- resolveTimeData(t, td2) } yield { - val to = (td1.timeGrain, td2.timeGrain) match { - case (Year, Month) => t2.copy(start = t2.start.withYear(t1.start.year)) - case (Year, Day) => t2.copy(start = t2.start.withYear(t1.start.year)) - case (Month, Day) => - t2.copy(start = t2.start.withMonth(t1.start.month).withYear(t1.start.year)) - case (Day, NoGrain) => // 今天现在 - t2.copy( - start = t2.start - .withYear(t1.start.year) - .withMonth(t1.start.month) - .withDayOfMonth(t1.start.dayOfMonth) - ) - case _ => null - } + val to = + if (td2.timePred.maxGrain.nonEmpty && td1.timeGrain > td2.timePred.maxGrain.get) { + copyGrain(Year, td2.timePred.maxGrain.get, t1, t2) + } else { + (td1.timeGrain, td2.timeGrain) match { + case (Year, Month) => t2.copy(start = t2.start.withYear(t1.start.year)) + case (Year, Day) => t2.copy(start = t2.start.withYear(t1.start.year)) + case (Month, Day) => + t2.copy(start = t2.start.withMonth(t1.start.month).withYear(t1.start.year)) + case (Day, NoGrain) => // 今天现在 + t2.copy( + start = t2.start + .withYear(t1.start.year) + .withMonth(t1.start.month) + .withDayOfMonth(t1.start.dayOfMonth) + ) + case _ => null + } + } if (to == null) EmptySeries else if (to.start.isBefore(t.start)) (Stream(to), Stream.empty) else (Stream.empty, Stream(to)) }).getOrElse(EmptySeries) } + def copy(grain: Grain, t1: DuckDateTime, t2: DuckDateTime): DuckDateTime = { + grain match { + case Grain.Minute => t2.withMinute(t1.minute) + case Grain.Hour => t2.withHour(t1.hour) + case Grain.Day => t2.withDayOfMonth(t1.dayOfMonth) + case Grain.Month => t2.withMonth(t1.month) + case Grain.Year => t2.withYear(t1.year) + case _ => t2 + } + } + + @scala.annotation.tailrec + def copyGrain(thisGrain: Grain, stopGrain: Grain, t1: TimeObject, t2: TimeObject): TimeObject = { + if (t1.start.date.isInstanceOf[LunarDate]) { + if (thisGrain >= Day && stopGrain < Day) { + val start = t2.start.copy(date = t1.start.date) + val end = + if (t2.end.isEmpty) None + else if (t1.end.isEmpty) t2.end + else t2.end.map(d => d.copy(date = t1.start.date)) + val t = t2.copy(start = start, end = end) + copyGrain(Hour, stopGrain, t1, t) + } else t2 + } else if (thisGrain > stopGrain) { + val start = copy(thisGrain, t1.start, t2.start) + val end = + if (t2.end.isEmpty) None + else if (t1.end.isEmpty) t2.end + else t2.end.map(d => copy(thisGrain, t1.end.get, t2.end.get)) + val t = t2.copy(start = start, end = end) + thisGrain.finer() match { + case Some(g) => copyGrain(g, stopGrain, t1, t) + case _ => t2 + } + } else t2 + } + @scala.annotation.tailrec def runSequencePredicate(list: List[TimeData])(t: TimeObject, context: TimeContext): PastFutureTime = { diff --git a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/Examples.scala b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/Examples.scala index 005d110a..3f60dd54 100644 --- a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/Examples.scala +++ b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/Examples.scala @@ -52,6 +52,7 @@ object Examples extends DimExamples { (y(2013), List("今年", "这一年")), (y(2014), List("明年", "下一年")), (ymd(2013, 12, 30, calendar = Lunar(false)).copy(holiday = "除夕"), List("今年除夕", "大年三十", "年三十")), + (ymdhms(2013, 12, 30, 20, grain=Hour, calendar = Lunar(false)), List("除夕晚上八点")), (ymd(2021, 12, 29, calendar = Lunar(false), holiday = "除夕"), List("2021年除夕")), // 2021年没有大年三十 ( localDateTimeInterval( @@ -148,8 +149,8 @@ object Examples extends DimExamples { val times = List( (hms(4, 30, 0), List("现在", "此时", "此刻", "当前", "4:30:00", "04点30分0秒")), (hm(15, 15), List("下午三点十五", "下午3:15", "15:15", "3:15pm", "3:15p.m", "下午三点一刻", "下午的三点一刻")), - (yMdHms(d=13, H=2, grain = Hour), List("晚上两点")), - (yMdHms(d=12, H=17, grain = Hour), List("晚上五点")), + (ymdhms(d=13, h=2, grain = Hour), List("晚上两点")), + (ymdhms(d=12, h=17, grain = Hour), List("晚上五点")), (hm(16, 40), List("十六时四十分", "十六点四十")), (hm(6, 10), List("六点十分", "六点一十")), (hms(4, 33, 0), List("过三分钟")), diff --git a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimeValueHelpers.scala b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimeValueHelpers.scala index ffcf2459..0f1a0d79 100644 --- a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimeValueHelpers.scala +++ b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/helper/TimeValueHelpers.scala @@ -1,6 +1,6 @@ package com.xiaomi.duckling.dimension.time.helper -import java.time.{LocalDateTime, LocalTime} +import java.time.{LocalDate, LocalDateTime, LocalTime} import com.github.heqiao2010.lunar.LunarCalendar @@ -10,6 +10,7 @@ import com.xiaomi.duckling.Types.{Context, ZoneCN} import com.xiaomi.duckling.dimension.time.enums._ import com.xiaomi.duckling.dimension.time.enums.Grain._ import com.xiaomi.duckling.dimension.time.Types._ +import com.xiaomi.duckling.ranking.Testing import com.xiaomi.duckling.ranking.Testing.testContext object TimeValueHelpers { @@ -71,8 +72,12 @@ object TimeValueHelpers { def md(m: Int, d: Int): TimeValue = ymd(2013, m, d) - def yMdHms(y: Int = 2013, M: Int = 2, d: Int = 12, H: Int = 0, m: Int = 0, s: Int = 0, grain: Grain): TimeValue = { - datetime(LocalDateTime.of(y, M, d, H, m, s), grain) + def ymdhms(y: Int = 2013, M: Int = 2, d: Int = 12, h: Int = 0, m: Int = 0, s: Int = 0, grain: Grain, calendar: Calendar = Solar, holiday: Option[String] = None): TimeValue = { + val date = calendar match { + case Solar => SolarDate(LocalDate.of(y, M, d)) + case Lunar(leap) => LunarDate(new LunarCalendar(y, M, d, leap)) + } + datetime(DuckDateTime(date, LocalTime.of(h, m, s), Testing.testContext.referenceTime.getZone), grain, holiday) } def datetimeInterval(dt1: DuckDateTime, dt2: DuckDateTime, g: Grain, holiday: Option[String] = None, diff --git a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/repeat/Examples.scala b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/repeat/Examples.scala index 22a0dda8..a51f949f 100644 --- a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/repeat/Examples.scala +++ b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/time/repeat/Examples.scala @@ -45,11 +45,11 @@ object Examples extends DimExamples { Hour) ), List("每个月五号的早上")), (RepeatValue(DurationData(1, Month), start = ymd(m = 3, d = 5)), List("每个月的五号")), - (RepeatValue(DurationData(1, Month), start = yMdHms(M = 3, d = 2, H = 14, grain = Hour)), List("每月2号下午2点")), + (RepeatValue(DurationData(1, Month), start = ymdhms(M = 3, d = 2, h = 14, grain = Hour)), List("每月2号下午2点")), (RepeatValue(DurationData(1, Week), start = (ymd(d = 13), Some(form.DayOfWeek))), List("每周三", "每个星期三")), (RepeatValue(DurationData(1, Day), start = (h(8), Some(form.TimeOfDay(Some(8), false)))), List("每天上午八点", "每个上午八点")), (RepeatValue(workdayType = NonWorkday), List("非工作日", "节假日")), - (RepeatValue(workdayType = Workday, start = (yMdHms(d = 13, H = 3, grain = Hour), Some(form.TimeOfDay(Some(3), false)))), List("工作日三点", "工作日每天三点", "每个工作日三点")), + (RepeatValue(workdayType = Workday, start = (ymdhms(d = 13, h = 3, grain = Hour), Some(form.TimeOfDay(Some(3), false)))), List("工作日三点", "工作日每天三点", "每个工作日三点")), (RepeatValue(workdayType = Workday, start = (datetimeInterval( new DuckDateTime(LocalDateTime.of(2013, 2, 12, 8, 0, 0)), new DuckDateTime(LocalDateTime.of(2013, 2, 12, 12, 0, 0)), From 0afb4e5c53cd0764a23bb4e7fa8740d6afbf4be0 Mon Sep 17 00:00:00 2001 From: Ninglin Du Date: Fri, 2 Aug 2024 10:40:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=E5=B0=91=E9=87=8F?= =?UTF-8?q?=E8=8A=82=E6=97=A5=E8=AF=B4=E6=B3=95=20-=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=99=A4=E5=A4=95=E6=99=9A=E4=B8=8A8=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BA=86Sequence=E7=9A=84=E8=A1=A8=E8=BE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/scala/com/xiaomi/duckling/dimension/time/package.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2a5239fd..8ff74be9 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 @@ -244,7 +244,7 @@ package object time { val end = if (t2.end.isEmpty) None else if (t1.end.isEmpty) t2.end - else t2.end.map(d => copy(thisGrain, t1.end.get, t2.end.get)) + else t2.end.map(copy(thisGrain, t1.end.get, _)) val t = t2.copy(start = start, end = end) thisGrain.finer() match { case Some(g) => copyGrain(g, stopGrain, t1, t)