• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "annotator/datetime/datetime-grounder.h"
18 
19 #include <algorithm>
20 #include <limits>
21 #include <unordered_map>
22 #include <vector>
23 
24 #include "annotator/datetime/datetime_generated.h"
25 #include "annotator/datetime/utils.h"
26 #include "annotator/types.h"
27 #include "utils/base/integral_types.h"
28 #include "utils/base/status.h"
29 #include "utils/base/status_macros.h"
30 
31 using ::libtextclassifier3::grammar::datetime::AbsoluteDateTime;
32 using ::libtextclassifier3::grammar::datetime::ComponentType;
33 using ::libtextclassifier3::grammar::datetime::Meridiem;
34 using ::libtextclassifier3::grammar::datetime::RelativeDateTime;
35 using ::libtextclassifier3::grammar::datetime::RelativeDatetimeComponent;
36 using ::libtextclassifier3::grammar::datetime::UngroundedDatetime;
37 using ::libtextclassifier3::grammar::datetime::RelativeDatetimeComponent_::
38     Modifier;
39 
40 namespace libtextclassifier3 {
41 
42 namespace {
43 
44 const std::unordered_map<int, int> kMonthDefaultLastDayMap(
45     {{/*no_month*/ 0, 31},
46      {/*January*/ 1, 31},
47      {/*Febuary*/ 2, 29},
48      {/*March*/ 3, 31},
49      {/*April*/ 4, 30},
50      {/*May*/ 5, 31},
51      {/*June*/ 6, 30},
52      {/*July*/ 7, 31},
53      {/*August*/ 8, 31},
54      {/*September*/ 9, 30},
55      {/*October*/ 10, 31},
56      {/*November*/ 11, 30},
57      {/*December*/ 12, 31}});
58 
IsValidDatetime(const AbsoluteDateTime * absolute_datetime)59 bool IsValidDatetime(const AbsoluteDateTime* absolute_datetime) {
60   // Sanity Checks.
61   if (absolute_datetime->minute() > 59 || absolute_datetime->second() > 59 ||
62       absolute_datetime->hour() > 23 || absolute_datetime->month() > 12 ||
63       absolute_datetime->month() == 0) {
64     return false;
65   }
66   if (absolute_datetime->day() >= 0) {
67     int min_day_value = 1;
68     int max_day_value = 31;
69     if (absolute_datetime->month() >= 0 && absolute_datetime->month() <= 12) {
70       max_day_value = kMonthDefaultLastDayMap.at(absolute_datetime->month());
71       if (absolute_datetime->day() < min_day_value ||
72           absolute_datetime->day() > max_day_value) {
73         return false;
74       }
75     }
76   }
77   return true;
78 }
79 
IsValidDatetime(const RelativeDateTime * relative_datetime)80 bool IsValidDatetime(const RelativeDateTime* relative_datetime) {
81   if (relative_datetime->base()) {
82     return IsValidDatetime(relative_datetime->base());
83   }
84   return true;
85 }
86 
ToRelativeQualifier(const Modifier & modifier)87 StatusOr<DatetimeComponent::RelativeQualifier> ToRelativeQualifier(
88     const Modifier& modifier) {
89   switch (modifier) {
90     case Modifier::Modifier_THIS:
91       return DatetimeComponent::RelativeQualifier::THIS;
92     case Modifier::Modifier_LAST:
93       return DatetimeComponent::RelativeQualifier::LAST;
94     case Modifier::Modifier_NEXT:
95       return DatetimeComponent::RelativeQualifier::NEXT;
96     case Modifier::Modifier_NOW:
97       return DatetimeComponent::RelativeQualifier::NOW;
98     case Modifier::Modifier_TOMORROW:
99       return DatetimeComponent::RelativeQualifier::TOMORROW;
100     case Modifier::Modifier_YESTERDAY:
101       return DatetimeComponent::RelativeQualifier::YESTERDAY;
102     case Modifier::Modifier_PAST:
103       return DatetimeComponent::RelativeQualifier::PAST;
104     case Modifier::Modifier_FUTURE:
105       return DatetimeComponent::RelativeQualifier::FUTURE;
106     case Modifier::Modifier_UNSPECIFIED:
107       return DatetimeComponent::RelativeQualifier::UNSPECIFIED;
108     default:
109       return Status(StatusCode::INTERNAL,
110                     "Couldn't parse the Modifier to RelativeQualifier.");
111   }
112 }
113 
ToComponentType(const grammar::datetime::ComponentType component_type)114 StatusOr<DatetimeComponent::ComponentType> ToComponentType(
115     const grammar::datetime::ComponentType component_type) {
116   switch (component_type) {
117     case grammar::datetime::ComponentType_YEAR:
118       return DatetimeComponent::ComponentType::YEAR;
119     case grammar::datetime::ComponentType_MONTH:
120       return DatetimeComponent::ComponentType::MONTH;
121     case grammar::datetime::ComponentType_WEEK:
122       return DatetimeComponent::ComponentType::WEEK;
123     case grammar::datetime::ComponentType_DAY_OF_WEEK:
124       return DatetimeComponent::ComponentType::DAY_OF_WEEK;
125     case grammar::datetime::ComponentType_DAY_OF_MONTH:
126       return DatetimeComponent::ComponentType::DAY_OF_MONTH;
127     case grammar::datetime::ComponentType_HOUR:
128       return DatetimeComponent::ComponentType::HOUR;
129     case grammar::datetime::ComponentType_MINUTE:
130       return DatetimeComponent::ComponentType::MINUTE;
131     case grammar::datetime::ComponentType_SECOND:
132       return DatetimeComponent::ComponentType::SECOND;
133     case grammar::datetime::ComponentType_MERIDIEM:
134       return DatetimeComponent::ComponentType::MERIDIEM;
135     case grammar::datetime::ComponentType_UNSPECIFIED:
136       return DatetimeComponent::ComponentType::UNSPECIFIED;
137     default:
138       return Status(StatusCode::INTERNAL,
139                     "Couldn't parse the DatetimeComponent's ComponentType from "
140                     "grammar's datetime ComponentType.");
141   }
142 }
143 
FillAbsoluteDateTimeComponents(const grammar::datetime::AbsoluteDateTime * absolute_datetime,DatetimeParsedData * datetime_parsed_data)144 void FillAbsoluteDateTimeComponents(
145     const grammar::datetime::AbsoluteDateTime* absolute_datetime,
146     DatetimeParsedData* datetime_parsed_data) {
147   if (absolute_datetime->year() >= 0) {
148     datetime_parsed_data->SetAbsoluteValue(
149         DatetimeComponent::ComponentType::YEAR,
150         GetAdjustedYear(absolute_datetime->year()));
151   }
152   if (absolute_datetime->month() >= 0) {
153     datetime_parsed_data->SetAbsoluteValue(
154         DatetimeComponent::ComponentType::MONTH, absolute_datetime->month());
155   }
156   if (absolute_datetime->day() >= 0) {
157     datetime_parsed_data->SetAbsoluteValue(
158         DatetimeComponent::ComponentType::DAY_OF_MONTH,
159         absolute_datetime->day());
160   }
161   if (absolute_datetime->week_day() >= 0) {
162     datetime_parsed_data->SetAbsoluteValue(
163         DatetimeComponent::ComponentType::DAY_OF_WEEK,
164         absolute_datetime->week_day());
165   }
166   if (absolute_datetime->hour() >= 0) {
167     datetime_parsed_data->SetAbsoluteValue(
168         DatetimeComponent::ComponentType::HOUR, absolute_datetime->hour());
169   }
170   if (absolute_datetime->minute() >= 0) {
171     datetime_parsed_data->SetAbsoluteValue(
172         DatetimeComponent::ComponentType::MINUTE, absolute_datetime->minute());
173   }
174   if (absolute_datetime->second() >= 0) {
175     datetime_parsed_data->SetAbsoluteValue(
176         DatetimeComponent::ComponentType::SECOND, absolute_datetime->second());
177   }
178   if (absolute_datetime->meridiem() != grammar::datetime::Meridiem_UNKNOWN) {
179     datetime_parsed_data->SetAbsoluteValue(
180         DatetimeComponent::ComponentType::MERIDIEM,
181         absolute_datetime->meridiem() == grammar::datetime::Meridiem_AM ? 0
182                                                                         : 1);
183   }
184   if (absolute_datetime->time_zone()) {
185     datetime_parsed_data->SetAbsoluteValue(
186         DatetimeComponent::ComponentType::ZONE_OFFSET,
187         absolute_datetime->time_zone()->utc_offset_mins());
188   }
189 }
190 
FillRelativeDateTimeComponents(const grammar::datetime::RelativeDateTime * relative_datetime)191 StatusOr<DatetimeParsedData> FillRelativeDateTimeComponents(
192     const grammar::datetime::RelativeDateTime* relative_datetime) {
193   DatetimeParsedData datetime_parsed_data;
194   for (const RelativeDatetimeComponent* relative_component :
195        *relative_datetime->relative_datetime_component()) {
196     TC3_ASSIGN_OR_RETURN(const DatetimeComponent::ComponentType component_type,
197                          ToComponentType(relative_component->component_type()));
198     datetime_parsed_data.SetRelativeCount(component_type,
199                                           relative_component->value());
200     TC3_ASSIGN_OR_RETURN(
201         const DatetimeComponent::RelativeQualifier relative_qualifier,
202         ToRelativeQualifier(relative_component->modifier()));
203     datetime_parsed_data.SetRelativeValue(component_type, relative_qualifier);
204   }
205   if (relative_datetime->base()) {
206     FillAbsoluteDateTimeComponents(relative_datetime->base(),
207                                    &datetime_parsed_data);
208   }
209   return datetime_parsed_data;
210 }
211 
212 }  // namespace
213 
DatetimeGrounder(const CalendarLib * calendarlib)214 DatetimeGrounder::DatetimeGrounder(const CalendarLib* calendarlib)
215     : calendarlib_(*calendarlib) {}
216 
Ground(const int64 reference_time_ms_utc,const std::string & reference_timezone,const std::string & reference_locale,const grammar::datetime::UngroundedDatetime * ungrounded_datetime) const217 StatusOr<std::vector<DatetimeParseResult>> DatetimeGrounder::Ground(
218     const int64 reference_time_ms_utc, const std::string& reference_timezone,
219     const std::string& reference_locale,
220     const grammar::datetime::UngroundedDatetime* ungrounded_datetime) const {
221   DatetimeParsedData datetime_parsed_data;
222   if (ungrounded_datetime->absolute_datetime()) {
223     FillAbsoluteDateTimeComponents(ungrounded_datetime->absolute_datetime(),
224                                    &datetime_parsed_data);
225   } else if (ungrounded_datetime->relative_datetime()) {
226     TC3_ASSIGN_OR_RETURN(datetime_parsed_data,
227                          FillRelativeDateTimeComponents(
228                              ungrounded_datetime->relative_datetime()));
229   }
230   std::vector<DatetimeParsedData> interpretations;
231   FillInterpretations(datetime_parsed_data,
232                       calendarlib_.GetGranularity(datetime_parsed_data),
233                       &interpretations);
234   std::vector<DatetimeParseResult> datetime_parse_result;
235 
236   for (const DatetimeParsedData& interpretation : interpretations) {
237     std::vector<DatetimeComponent> date_components;
238     interpretation.GetDatetimeComponents(&date_components);
239     DatetimeParseResult result;
240     // Text classifier only provides ambiguity limited to “AM/PM” which is
241     // encoded in the pair of DatetimeParseResult; both corresponding to the
242     // same date, but one corresponding to “AM” and the other one corresponding
243     //  to “PM”.
244     if (!calendarlib_.InterpretParseData(
245             interpretation, reference_time_ms_utc, reference_timezone,
246             reference_locale, /*prefer_future_for_unspecified_date=*/true,
247             &(result.time_ms_utc), &(result.granularity))) {
248       return Status(
249           StatusCode::INTERNAL,
250           "Couldn't parse the UngroundedDatetime to DatetimeParseResult.");
251     }
252 
253     // Sort the date time units by component type.
254     std::stable_sort(date_components.begin(), date_components.end(),
255                      [](DatetimeComponent a, DatetimeComponent b) {
256                        return a.component_type > b.component_type;
257                      });
258     result.datetime_components.swap(date_components);
259     datetime_parse_result.push_back(result);
260   }
261   return datetime_parse_result;
262 }
263 
IsValidUngroundedDatetime(const UngroundedDatetime * ungrounded_datetime) const264 bool DatetimeGrounder::IsValidUngroundedDatetime(
265     const UngroundedDatetime* ungrounded_datetime) const {
266   if (ungrounded_datetime->absolute_datetime()) {
267     return IsValidDatetime(ungrounded_datetime->absolute_datetime());
268   } else if (ungrounded_datetime->relative_datetime()) {
269     return IsValidDatetime(ungrounded_datetime->relative_datetime());
270   }
271   return false;
272 }
273 
274 }  // namespace libtextclassifier3
275