xref: /aosp_15_r20/external/libtextclassifier/native/utils/calendar/calendar-common.h (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1*993b0882SAndroid Build Coastguard Worker /*
2*993b0882SAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*993b0882SAndroid Build Coastguard Worker  *
4*993b0882SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*993b0882SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*993b0882SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*993b0882SAndroid Build Coastguard Worker  *
8*993b0882SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*993b0882SAndroid Build Coastguard Worker  *
10*993b0882SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*993b0882SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*993b0882SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*993b0882SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*993b0882SAndroid Build Coastguard Worker  * limitations under the License.
15*993b0882SAndroid Build Coastguard Worker  */
16*993b0882SAndroid Build Coastguard Worker 
17*993b0882SAndroid Build Coastguard Worker #ifndef LIBTEXTCLASSIFIER_UTILS_CALENDAR_CALENDAR_COMMON_H_
18*993b0882SAndroid Build Coastguard Worker #define LIBTEXTCLASSIFIER_UTILS_CALENDAR_CALENDAR_COMMON_H_
19*993b0882SAndroid Build Coastguard Worker 
20*993b0882SAndroid Build Coastguard Worker #include "annotator/types.h"
21*993b0882SAndroid Build Coastguard Worker #include "utils/base/integral_types.h"
22*993b0882SAndroid Build Coastguard Worker #include "utils/base/logging.h"
23*993b0882SAndroid Build Coastguard Worker #include "utils/base/macros.h"
24*993b0882SAndroid Build Coastguard Worker 
25*993b0882SAndroid Build Coastguard Worker namespace libtextclassifier3 {
26*993b0882SAndroid Build Coastguard Worker namespace calendar {
27*993b0882SAndroid Build Coastguard Worker 
28*993b0882SAndroid Build Coastguard Worker // Macro to reduce the amount of boilerplate needed for propagating errors.
29*993b0882SAndroid Build Coastguard Worker #define TC3_CALENDAR_CHECK(EXPR) \
30*993b0882SAndroid Build Coastguard Worker   if (!(EXPR)) {                 \
31*993b0882SAndroid Build Coastguard Worker     return false;                \
32*993b0882SAndroid Build Coastguard Worker   }
33*993b0882SAndroid Build Coastguard Worker 
34*993b0882SAndroid Build Coastguard Worker // An implementation of CalendarLib that is independent of the particular
35*993b0882SAndroid Build Coastguard Worker // calendar implementation used (implementation type is passed as template
36*993b0882SAndroid Build Coastguard Worker // argument).
37*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
38*993b0882SAndroid Build Coastguard Worker class CalendarLibTempl {
39*993b0882SAndroid Build Coastguard Worker  public:
40*993b0882SAndroid Build Coastguard Worker   bool InterpretParseData(const DatetimeParsedData& parse_data,
41*993b0882SAndroid Build Coastguard Worker                           int64 reference_time_ms_utc,
42*993b0882SAndroid Build Coastguard Worker                           const std::string& reference_timezone,
43*993b0882SAndroid Build Coastguard Worker                           const std::string& reference_locale,
44*993b0882SAndroid Build Coastguard Worker                           bool prefer_future_for_unspecified_date,
45*993b0882SAndroid Build Coastguard Worker                           TCalendar* calendar,
46*993b0882SAndroid Build Coastguard Worker                           DatetimeGranularity* granularity) const;
47*993b0882SAndroid Build Coastguard Worker 
48*993b0882SAndroid Build Coastguard Worker   DatetimeGranularity GetGranularity(const DatetimeParsedData& data) const;
49*993b0882SAndroid Build Coastguard Worker 
50*993b0882SAndroid Build Coastguard Worker  private:
51*993b0882SAndroid Build Coastguard Worker   // Adjusts the calendar's time instant according to a relative date reference
52*993b0882SAndroid Build Coastguard Worker   // in the parsed data.
53*993b0882SAndroid Build Coastguard Worker   bool ApplyRelationField(const DatetimeComponent& relative_date_time_component,
54*993b0882SAndroid Build Coastguard Worker                           TCalendar* calendar) const;
55*993b0882SAndroid Build Coastguard Worker 
56*993b0882SAndroid Build Coastguard Worker   // Round the time instant's precision down to the given granularity.
57*993b0882SAndroid Build Coastguard Worker   bool RoundToGranularity(DatetimeGranularity granularity,
58*993b0882SAndroid Build Coastguard Worker                           TCalendar* calendar) const;
59*993b0882SAndroid Build Coastguard Worker 
60*993b0882SAndroid Build Coastguard Worker   // Adjusts time in steps of relation_type, by distance steps.
61*993b0882SAndroid Build Coastguard Worker   // For example:
62*993b0882SAndroid Build Coastguard Worker   // - Adjusting by -2 MONTHS will return the beginning of the 1st
63*993b0882SAndroid Build Coastguard Worker   //   two weeks ago.
64*993b0882SAndroid Build Coastguard Worker   // - Adjusting by +4 Wednesdays will return the beginning of the next
65*993b0882SAndroid Build Coastguard Worker   //   Wednesday at least 4 weeks from now.
66*993b0882SAndroid Build Coastguard Worker   // If allow_today is true, the same day of the week may be kept
67*993b0882SAndroid Build Coastguard Worker   // if it already matches the relation type.
68*993b0882SAndroid Build Coastguard Worker   bool AdjustByRelation(DatetimeComponent date_time_component, int distance,
69*993b0882SAndroid Build Coastguard Worker                         bool allow_today, TCalendar* calendar) const;
70*993b0882SAndroid Build Coastguard Worker };
71*993b0882SAndroid Build Coastguard Worker 
HasOnlyTimeComponents(const DatetimeParsedData & parse_data)72*993b0882SAndroid Build Coastguard Worker inline bool HasOnlyTimeComponents(const DatetimeParsedData& parse_data) {
73*993b0882SAndroid Build Coastguard Worker   std::vector<DatetimeComponent> components;
74*993b0882SAndroid Build Coastguard Worker   parse_data.GetDatetimeComponents(&components);
75*993b0882SAndroid Build Coastguard Worker 
76*993b0882SAndroid Build Coastguard Worker   for (const DatetimeComponent& component : components) {
77*993b0882SAndroid Build Coastguard Worker     if (!(component.component_type == DatetimeComponent::ComponentType::HOUR ||
78*993b0882SAndroid Build Coastguard Worker           component.component_type ==
79*993b0882SAndroid Build Coastguard Worker               DatetimeComponent::ComponentType::MINUTE ||
80*993b0882SAndroid Build Coastguard Worker           component.component_type ==
81*993b0882SAndroid Build Coastguard Worker               DatetimeComponent::ComponentType::SECOND ||
82*993b0882SAndroid Build Coastguard Worker           component.component_type ==
83*993b0882SAndroid Build Coastguard Worker               DatetimeComponent::ComponentType::MERIDIEM)) {
84*993b0882SAndroid Build Coastguard Worker       return false;
85*993b0882SAndroid Build Coastguard Worker     }
86*993b0882SAndroid Build Coastguard Worker   }
87*993b0882SAndroid Build Coastguard Worker   return true;
88*993b0882SAndroid Build Coastguard Worker }
89*993b0882SAndroid Build Coastguard Worker 
90*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
InterpretParseData(const DatetimeParsedData & parse_data,int64 reference_time_ms_utc,const std::string & reference_timezone,const std::string & reference_locale,bool prefer_future_for_unspecified_date,TCalendar * calendar,DatetimeGranularity * granularity)91*993b0882SAndroid Build Coastguard Worker bool CalendarLibTempl<TCalendar>::InterpretParseData(
92*993b0882SAndroid Build Coastguard Worker     const DatetimeParsedData& parse_data, int64 reference_time_ms_utc,
93*993b0882SAndroid Build Coastguard Worker     const std::string& reference_timezone, const std::string& reference_locale,
94*993b0882SAndroid Build Coastguard Worker     bool prefer_future_for_unspecified_date, TCalendar* calendar,
95*993b0882SAndroid Build Coastguard Worker     DatetimeGranularity* granularity) const {
96*993b0882SAndroid Build Coastguard Worker   TC3_CALENDAR_CHECK(calendar->Initialize(reference_timezone, reference_locale,
97*993b0882SAndroid Build Coastguard Worker                                           reference_time_ms_utc))
98*993b0882SAndroid Build Coastguard Worker 
99*993b0882SAndroid Build Coastguard Worker   bool should_round_to_granularity = true;
100*993b0882SAndroid Build Coastguard Worker   *granularity = GetGranularity(parse_data);
101*993b0882SAndroid Build Coastguard Worker 
102*993b0882SAndroid Build Coastguard Worker   // Apply each of the parsed fields in order of increasing granularity.
103*993b0882SAndroid Build Coastguard Worker   static const int64 kMillisInMinute = 1000 * 60;
104*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasFieldType(DatetimeComponent::ComponentType::ZONE_OFFSET)) {
105*993b0882SAndroid Build Coastguard Worker     int zone_offset;
106*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::ZONE_OFFSET,
107*993b0882SAndroid Build Coastguard Worker                              &zone_offset);
108*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetZoneOffset(zone_offset * kMillisInMinute))
109*993b0882SAndroid Build Coastguard Worker   }
110*993b0882SAndroid Build Coastguard Worker   static const int64 kMillisInHour = 1000 * 60 * 60;
111*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasFieldType(DatetimeComponent::ComponentType::DST_OFFSET)) {
112*993b0882SAndroid Build Coastguard Worker     int dst_offset;
113*993b0882SAndroid Build Coastguard Worker     if (parse_data.GetFieldValue(DatetimeComponent::ComponentType::DST_OFFSET,
114*993b0882SAndroid Build Coastguard Worker                                  &dst_offset)) {
115*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDstOffset(dst_offset * kMillisInHour))
116*993b0882SAndroid Build Coastguard Worker     }
117*993b0882SAndroid Build Coastguard Worker   }
118*993b0882SAndroid Build Coastguard Worker   std::vector<DatetimeComponent> relative_components;
119*993b0882SAndroid Build Coastguard Worker   parse_data.GetRelativeDatetimeComponents(&relative_components);
120*993b0882SAndroid Build Coastguard Worker   if (!relative_components.empty()) {
121*993b0882SAndroid Build Coastguard Worker     // Currently only one relative date time component is possible.
122*993b0882SAndroid Build Coastguard Worker     const DatetimeComponent& relative_component = relative_components.back();
123*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(ApplyRelationField(relative_component, calendar));
124*993b0882SAndroid Build Coastguard Worker     should_round_to_granularity = relative_component.ShouldRoundToGranularity();
125*993b0882SAndroid Build Coastguard Worker   } else {
126*993b0882SAndroid Build Coastguard Worker     // By default, the parsed time is interpreted to be on the reference day.
127*993b0882SAndroid Build Coastguard Worker     // But a parsed date should have time 0:00:00 unless specified.
128*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetHourOfDay(0))
129*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetMinute(0))
130*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetSecond(0))
131*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetMillisecond(0))
132*993b0882SAndroid Build Coastguard Worker   }
133*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(DatetimeComponent::ComponentType::YEAR)) {
134*993b0882SAndroid Build Coastguard Worker     int year;
135*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::YEAR, &year);
136*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetYear(year))
137*993b0882SAndroid Build Coastguard Worker   }
138*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(DatetimeComponent::ComponentType::MONTH)) {
139*993b0882SAndroid Build Coastguard Worker     int month;
140*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::MONTH, &month);
141*993b0882SAndroid Build Coastguard Worker     // ICU has months starting at 0, Java and Datetime parser at 1, so we
142*993b0882SAndroid Build Coastguard Worker     // need to subtract 1.
143*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetMonth(month - 1))
144*993b0882SAndroid Build Coastguard Worker   }
145*993b0882SAndroid Build Coastguard Worker 
146*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(
147*993b0882SAndroid Build Coastguard Worker           DatetimeComponent::ComponentType::DAY_OF_MONTH)) {
148*993b0882SAndroid Build Coastguard Worker     int day_of_month;
149*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::DAY_OF_MONTH,
150*993b0882SAndroid Build Coastguard Worker                              &day_of_month);
151*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetDayOfMonth(day_of_month))
152*993b0882SAndroid Build Coastguard Worker   }
153*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(DatetimeComponent::ComponentType::HOUR)) {
154*993b0882SAndroid Build Coastguard Worker     int hour;
155*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::HOUR, &hour);
156*993b0882SAndroid Build Coastguard Worker     if (parse_data.HasFieldType(DatetimeComponent::ComponentType::MERIDIEM)) {
157*993b0882SAndroid Build Coastguard Worker       int merdiem;
158*993b0882SAndroid Build Coastguard Worker       parse_data.GetFieldValue(DatetimeComponent::ComponentType::MERIDIEM,
159*993b0882SAndroid Build Coastguard Worker                                &merdiem);
160*993b0882SAndroid Build Coastguard Worker       if (merdiem == 1 && hour < 12) {
161*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->SetHourOfDay(hour + 12))
162*993b0882SAndroid Build Coastguard Worker       } else if (merdiem == 0 && hour == 12) {
163*993b0882SAndroid Build Coastguard Worker         // Set hour of the day's value to zero (12am == 0:00 in 24 hour format).
164*993b0882SAndroid Build Coastguard Worker         // Please see issue b/139923083.
165*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->SetHourOfDay(0));
166*993b0882SAndroid Build Coastguard Worker       } else {
167*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->SetHourOfDay(hour))
168*993b0882SAndroid Build Coastguard Worker       }
169*993b0882SAndroid Build Coastguard Worker     } else {
170*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetHourOfDay(hour))
171*993b0882SAndroid Build Coastguard Worker     }
172*993b0882SAndroid Build Coastguard Worker   }
173*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(DatetimeComponent::ComponentType::MINUTE)) {
174*993b0882SAndroid Build Coastguard Worker     int minute;
175*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::MINUTE, &minute);
176*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetMinute(minute))
177*993b0882SAndroid Build Coastguard Worker   }
178*993b0882SAndroid Build Coastguard Worker   if (parse_data.HasAbsoluteValue(DatetimeComponent::ComponentType::SECOND)) {
179*993b0882SAndroid Build Coastguard Worker     int second;
180*993b0882SAndroid Build Coastguard Worker     parse_data.GetFieldValue(DatetimeComponent::ComponentType::SECOND, &second);
181*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(calendar->SetSecond(second))
182*993b0882SAndroid Build Coastguard Worker   }
183*993b0882SAndroid Build Coastguard Worker   if (should_round_to_granularity) {
184*993b0882SAndroid Build Coastguard Worker     TC3_CALENDAR_CHECK(RoundToGranularity(*granularity, calendar))
185*993b0882SAndroid Build Coastguard Worker   }
186*993b0882SAndroid Build Coastguard Worker 
187*993b0882SAndroid Build Coastguard Worker   int64 calendar_millis;
188*993b0882SAndroid Build Coastguard Worker   TC3_CALENDAR_CHECK(calendar->GetTimeInMillis(&calendar_millis))
189*993b0882SAndroid Build Coastguard Worker   if (prefer_future_for_unspecified_date &&
190*993b0882SAndroid Build Coastguard Worker       calendar_millis < reference_time_ms_utc &&
191*993b0882SAndroid Build Coastguard Worker       HasOnlyTimeComponents(parse_data)) {
192*993b0882SAndroid Build Coastguard Worker     calendar->AddDayOfMonth(1);
193*993b0882SAndroid Build Coastguard Worker   }
194*993b0882SAndroid Build Coastguard Worker 
195*993b0882SAndroid Build Coastguard Worker   return true;
196*993b0882SAndroid Build Coastguard Worker }
197*993b0882SAndroid Build Coastguard Worker 
198*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
ApplyRelationField(const DatetimeComponent & relative_date_time_component,TCalendar * calendar)199*993b0882SAndroid Build Coastguard Worker bool CalendarLibTempl<TCalendar>::ApplyRelationField(
200*993b0882SAndroid Build Coastguard Worker     const DatetimeComponent& relative_date_time_component,
201*993b0882SAndroid Build Coastguard Worker     TCalendar* calendar) const {
202*993b0882SAndroid Build Coastguard Worker   switch (relative_date_time_component.relative_qualifier) {
203*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::UNSPECIFIED:
204*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "UNSPECIFIED RelationType.";
205*993b0882SAndroid Build Coastguard Worker       return false;
206*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::NEXT:
207*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(AdjustByRelation(relative_date_time_component,
208*993b0882SAndroid Build Coastguard Worker                                           /*distance=*/1,
209*993b0882SAndroid Build Coastguard Worker                                           /*allow_today=*/false, calendar));
210*993b0882SAndroid Build Coastguard Worker       return true;
211*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::THIS:
212*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(AdjustByRelation(relative_date_time_component,
213*993b0882SAndroid Build Coastguard Worker                                           /*distance=*/1,
214*993b0882SAndroid Build Coastguard Worker                                           /*allow_today=*/true, calendar))
215*993b0882SAndroid Build Coastguard Worker       return true;
216*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::LAST:
217*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(AdjustByRelation(relative_date_time_component,
218*993b0882SAndroid Build Coastguard Worker                                           /*distance=*/-1,
219*993b0882SAndroid Build Coastguard Worker                                           /*allow_today=*/false, calendar))
220*993b0882SAndroid Build Coastguard Worker       return true;
221*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::NOW:
222*993b0882SAndroid Build Coastguard Worker       return true;  // NOOP
223*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::TOMORROW:
224*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(1));
225*993b0882SAndroid Build Coastguard Worker       return true;
226*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::YESTERDAY:
227*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(-1));
228*993b0882SAndroid Build Coastguard Worker       return true;
229*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::PAST:
230*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(
231*993b0882SAndroid Build Coastguard Worker           AdjustByRelation(relative_date_time_component,
232*993b0882SAndroid Build Coastguard Worker                            relative_date_time_component.relative_count,
233*993b0882SAndroid Build Coastguard Worker                            /*allow_today=*/false, calendar))
234*993b0882SAndroid Build Coastguard Worker       return true;
235*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::RelativeQualifier::FUTURE:
236*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(
237*993b0882SAndroid Build Coastguard Worker           AdjustByRelation(relative_date_time_component,
238*993b0882SAndroid Build Coastguard Worker                            relative_date_time_component.relative_count,
239*993b0882SAndroid Build Coastguard Worker                            /*allow_today=*/false, calendar))
240*993b0882SAndroid Build Coastguard Worker       return true;
241*993b0882SAndroid Build Coastguard Worker   }
242*993b0882SAndroid Build Coastguard Worker   return false;
243*993b0882SAndroid Build Coastguard Worker }
244*993b0882SAndroid Build Coastguard Worker 
245*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
RoundToGranularity(DatetimeGranularity granularity,TCalendar * calendar)246*993b0882SAndroid Build Coastguard Worker bool CalendarLibTempl<TCalendar>::RoundToGranularity(
247*993b0882SAndroid Build Coastguard Worker     DatetimeGranularity granularity, TCalendar* calendar) const {
248*993b0882SAndroid Build Coastguard Worker   // Force recomputation before doing the rounding.
249*993b0882SAndroid Build Coastguard Worker   int unused;
250*993b0882SAndroid Build Coastguard Worker   TC3_CALENDAR_CHECK(calendar->GetDayOfWeek(&unused));
251*993b0882SAndroid Build Coastguard Worker 
252*993b0882SAndroid Build Coastguard Worker   switch (granularity) {
253*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_YEAR:
254*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetMonth(0));
255*993b0882SAndroid Build Coastguard Worker       TC3_FALLTHROUGH_INTENDED;
256*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_MONTH:
257*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDayOfMonth(1));
258*993b0882SAndroid Build Coastguard Worker       TC3_FALLTHROUGH_INTENDED;
259*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_DAY:
260*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetHourOfDay(0));
261*993b0882SAndroid Build Coastguard Worker       TC3_FALLTHROUGH_INTENDED;
262*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_HOUR:
263*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetMinute(0));
264*993b0882SAndroid Build Coastguard Worker       TC3_FALLTHROUGH_INTENDED;
265*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_MINUTE:
266*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetSecond(0));
267*993b0882SAndroid Build Coastguard Worker       break;
268*993b0882SAndroid Build Coastguard Worker 
269*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_WEEK:
270*993b0882SAndroid Build Coastguard Worker       int first_day_of_week;
271*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->GetFirstDayOfWeek(&first_day_of_week));
272*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDayOfWeek(first_day_of_week));
273*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetHourOfDay(0));
274*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetMinute(0));
275*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetSecond(0));
276*993b0882SAndroid Build Coastguard Worker       break;
277*993b0882SAndroid Build Coastguard Worker 
278*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_UNKNOWN:
279*993b0882SAndroid Build Coastguard Worker     case GRANULARITY_SECOND:
280*993b0882SAndroid Build Coastguard Worker       break;
281*993b0882SAndroid Build Coastguard Worker   }
282*993b0882SAndroid Build Coastguard Worker   return true;
283*993b0882SAndroid Build Coastguard Worker }
284*993b0882SAndroid Build Coastguard Worker 
285*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
AdjustByRelation(DatetimeComponent date_time_component,int distance,bool allow_today,TCalendar * calendar)286*993b0882SAndroid Build Coastguard Worker bool CalendarLibTempl<TCalendar>::AdjustByRelation(
287*993b0882SAndroid Build Coastguard Worker     DatetimeComponent date_time_component, int distance, bool allow_today,
288*993b0882SAndroid Build Coastguard Worker     TCalendar* calendar) const {
289*993b0882SAndroid Build Coastguard Worker   const int distance_sign = distance < 0 ? -1 : 1;
290*993b0882SAndroid Build Coastguard Worker   switch (date_time_component.component_type) {
291*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::DAY_OF_WEEK:
292*993b0882SAndroid Build Coastguard Worker       if (!allow_today) {
293*993b0882SAndroid Build Coastguard Worker         // If we're not including the same day as the reference, skip it.
294*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(distance_sign))
295*993b0882SAndroid Build Coastguard Worker       }
296*993b0882SAndroid Build Coastguard Worker       // Keep walking back until we hit the desired day of the week.
297*993b0882SAndroid Build Coastguard Worker       while (distance != 0) {
298*993b0882SAndroid Build Coastguard Worker         int day_of_week;
299*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->GetDayOfWeek(&day_of_week))
300*993b0882SAndroid Build Coastguard Worker         if (day_of_week == (date_time_component.value)) {
301*993b0882SAndroid Build Coastguard Worker           distance += -distance_sign;
302*993b0882SAndroid Build Coastguard Worker           if (distance == 0) break;
303*993b0882SAndroid Build Coastguard Worker         }
304*993b0882SAndroid Build Coastguard Worker         TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(distance_sign))
305*993b0882SAndroid Build Coastguard Worker       }
306*993b0882SAndroid Build Coastguard Worker       return true;
307*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::SECOND:
308*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddSecond(distance));
309*993b0882SAndroid Build Coastguard Worker       return true;
310*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::MINUTE:
311*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddMinute(distance));
312*993b0882SAndroid Build Coastguard Worker       return true;
313*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::HOUR:
314*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddHourOfDay(distance));
315*993b0882SAndroid Build Coastguard Worker       return true;
316*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::DAY_OF_MONTH:
317*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(distance));
318*993b0882SAndroid Build Coastguard Worker       return true;
319*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::WEEK:
320*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddDayOfMonth(7 * distance))
321*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDayOfWeek(1))
322*993b0882SAndroid Build Coastguard Worker       return true;
323*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::MONTH:
324*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddMonth(distance))
325*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDayOfMonth(1))
326*993b0882SAndroid Build Coastguard Worker       return true;
327*993b0882SAndroid Build Coastguard Worker     case DatetimeComponent::ComponentType::YEAR:
328*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->AddYear(distance))
329*993b0882SAndroid Build Coastguard Worker       TC3_CALENDAR_CHECK(calendar->SetDayOfYear(1))
330*993b0882SAndroid Build Coastguard Worker       return true;
331*993b0882SAndroid Build Coastguard Worker     default:
332*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Unknown relation type: "
333*993b0882SAndroid Build Coastguard Worker                      << static_cast<int>(date_time_component.component_type);
334*993b0882SAndroid Build Coastguard Worker       return false;
335*993b0882SAndroid Build Coastguard Worker   }
336*993b0882SAndroid Build Coastguard Worker   return false;
337*993b0882SAndroid Build Coastguard Worker }
338*993b0882SAndroid Build Coastguard Worker 
339*993b0882SAndroid Build Coastguard Worker template <class TCalendar>
GetGranularity(const DatetimeParsedData & data)340*993b0882SAndroid Build Coastguard Worker DatetimeGranularity CalendarLibTempl<TCalendar>::GetGranularity(
341*993b0882SAndroid Build Coastguard Worker     const DatetimeParsedData& data) const {
342*993b0882SAndroid Build Coastguard Worker   return data.GetFinestGranularity();
343*993b0882SAndroid Build Coastguard Worker }
344*993b0882SAndroid Build Coastguard Worker 
345*993b0882SAndroid Build Coastguard Worker };  // namespace calendar
346*993b0882SAndroid Build Coastguard Worker 
347*993b0882SAndroid Build Coastguard Worker #undef TC3_CALENDAR_CHECK
348*993b0882SAndroid Build Coastguard Worker 
349*993b0882SAndroid Build Coastguard Worker }  // namespace libtextclassifier3
350*993b0882SAndroid Build Coastguard Worker 
351*993b0882SAndroid Build Coastguard Worker #endif  // LIBTEXTCLASSIFIER_UTILS_CALENDAR_CALENDAR_COMMON_H_
352