text: // Copyright 2020 Google LLC
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     https://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  
  #include "utils/calendar/calendar-icu.h"
  
  #include <memory>
  
  #include "utils/base/macros.h"
  #include "utils/calendar/calendar-common.h"
  
  namespace libtextclassifier3 {
  namespace {
  
  // Make sure the constants are compatible with ICU.
  #define TC3_CHECK_DAY_CONSTANT(NAME, day_of_week)                        \
    static_assert(                                                         \
        static_cast<int>(UCalendarDaysOfWeek::UCAL_##NAME) == day_of_week, \
        "Mismatching constant value for " #NAME);
  TC3_CHECK_DAY_CONSTANT(SUNDAY, kSunday)
  TC3_CHECK_DAY_CONSTANT(MONDAY, kMonday)
  TC3_CHECK_DAY_CONSTANT(TUESDAY, kTuesday)
  TC3_CHECK_DAY_CONSTANT(WEDNESDAY, kWednesday)
  TC3_CHECK_DAY_CONSTANT(THURSDAY, kThursday)
  TC3_CHECK_DAY_CONSTANT(FRIDAY, kFriday)
  TC3_CHECK_DAY_CONSTANT(SATURDAY, kSaturday)
  #undef TC3_CHECK_DAY_CONSTANT
  
  // Generic version of icu::Calendar::add with error checking.
  bool CalendarAdd(UCalendarDateFields field, int value,
                   icu::Calendar* calendar) {
    UErrorCode status = U_ZERO_ERROR;
    calendar->add(field, value, status);
    if (U_FAILURE(status)) {
      TC3_LOG(ERROR) << "failed to add " << field;
      return false;
    }
    return true;
  }
  
  // Generic version of icu::Calendar::get with error checking.
  bool CalendarGet(UCalendarDateFields field, int* value,
                   icu::Calendar* calendar) {
    UErrorCode status = U_ZERO_ERROR;
    *value = calendar->get(field, status);
    if (U_FAILURE(status)) {
      TC3_LOG(ERROR) << "failed to get " << field;
      return false;
    }
    return true;
  }
  
  // Generic version of icu::Calendar::set with error checking.
  bool CalendarSet(UCalendarDateFields field, int value,
                   icu::Calendar* calendar) {
    calendar->set(field, value);
    return true;
  }
  
  }  // namespace
  
  bool Calendar::Initialize(const std::string& time_zone,
                            const std::string& locale, int64 time_ms_utc) {
    UErrorCode status = U_ZERO_ERROR;
    calendar_.reset(icu::Calendar::createInstance(
        icu::Locale::createFromName(locale.c_str()), status));
    if (U_FAILURE(status)) {
      TC3_LOG(ERROR) << "error getting calendar instance";
      return false;
    }
    calendar_->adoptTimeZone(
        icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(time_zone)));
    calendar_->setTime(time_ms_utc, status);
    if (U_FAILURE(status)) {
      TC3_LOG(ERROR) << "failed to set time";
      return false;
    }
    return true;
  }
  
  bool Calendar::GetFirstDayOfWeek(int* value) const {
    *value = calendar_->getFirstDayOfWeek();
    return true;
  }
  
  bool Calendar::GetTimeInMillis(int64* value) const {
    UErrorCode status = U_ZERO_ERROR;
    *value = calendar_->getTime(status);
    if (U_FAILURE(status)) {
      TC3_LOG(ERROR) << "error getting time from instance";
      return false;
    }
    return true;
  }
  
  // Below is the boilerplate code for implementing the specialisations of
  // get/set/add for the various field types.
  #define TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, KIND, TYPE)          \
    bool Calendar::KIND##NAME(TYPE value) const {                     \
      return Calendar##KIND(UCalendarDateFields::UCAL_##CONST, value, \
                            calendar_.get());                         \
    }
  #define TC3_DEFINE_ADD(NAME, CONST) \
    TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Add, int)
  #define TC3_DEFINE_GET(NAME, CONST) \
    TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Get, int*)
  #define TC3_DEFINE_SET(NAME, CONST) \
    TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Set, int)
  
  TC3_DEFINE_ADD(Second, SECOND)
  TC3_DEFINE_ADD(Minute, MINUTE)
  TC3_DEFINE_ADD(HourOfDay, HOUR_OF_DAY)
  TC3_DEFINE_ADD(DayOfMonth, DAY_OF_MONTH)
  TC3_DEFINE_ADD(Year, YEAR)
  TC3_DEFINE_ADD(Month, MONTH)
  TC3_DEFINE_GET(DayOfWeek, DAY_OF_WEEK)
  TC3_DEFINE_SET(ZoneOffset, ZONE_OFFSET)
  TC3_DEFINE_SET(DstOffset, DST_OFFSET)
  TC3_DEFINE_SET(Year, YEAR)
  TC3_DEFINE_SET(Month, MONTH)
  TC3_DEFINE_SET(DayOfYear, DAY_OF_YEAR)
  TC3_DEFINE_SET(DayOfMonth, DAY_OF_MONTH)
  TC3_DEFINE_SET(DayOfWeek, DAY_OF_WEEK)
  TC3_DEFINE_SET(HourOfDay, HOUR_OF_DAY)
  TC3_DEFINE_SET(Minute, MINUTE)
  TC3_DEFINE_SET(Second, SECOND)
  TC3_DEFINE_SET(Millisecond, MILLISECOND)
  
  #undef TC3_DEFINE_FIELD_ACCESSOR
  #undef TC3_DEFINE_ADD
  #undef TC3_DEFINE_GET
  #undef TC3_DEFINE_SET
  
  }  // namespace libtextclassifier3
  