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