std::string DateTimeFormatter::format()

in velox/functions/lib/DateTimeFormatter.cpp [132:305]


std::string DateTimeFormatter::format(
    const Timestamp& timestamp,
    const date::time_zone* timezone) const {
  const std::chrono::
      time_point<std::chrono::system_clock, std::chrono::milliseconds>
          timePoint(std::chrono::milliseconds(timestamp.toMillis()));
  validateTimePoint(timePoint);
  const auto daysTimePoint = date::floor<date::days>(timePoint);

  const auto durationInTheDay = date::make_time(timePoint - daysTimePoint);
  const date::year_month_day calDate(daysTimePoint);
  const date::weekday weekday(daysTimePoint);

  std::string result;
  for (auto& token : tokens_) {
    if (token.type == DateTimeToken::Type::kLiteral) {
      result += token.literal;
    } else {
      switch (token.pattern.specifier) {
        case DateTimeFormatSpecifier::ERA:
          result += static_cast<signed>(calDate.year()) >= 0 ? "AD" : "BC";
          break;

        case DateTimeFormatSpecifier::CENTURY_OF_ERA: {
          auto year = static_cast<signed>(calDate.year());
          if (year < 0) {
            VELOX_USER_FAIL(
                "With century of era in format, year should be greater than 0.")
          } else {
            auto century = year / 100;
            result +=
                padContent(century, '0', token.pattern.minRepresentDigits);
          }
        } break;

        case DateTimeFormatSpecifier::YEAR_OF_ERA:
          result += padContent(
              std::abs(static_cast<signed>(calDate.year())),
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::DAY_OF_WEEK_0_BASED:
        case DateTimeFormatSpecifier::DAY_OF_WEEK_1_BASED: {
          auto weekdayNum = weekday.c_encoding();
          if (weekdayNum == 0 &&
              token.pattern.specifier ==
                  DateTimeFormatSpecifier::DAY_OF_WEEK_1_BASED) {
            weekdayNum = 7;
          }
          result +=
              padContent(weekdayNum, '0', token.pattern.minRepresentDigits);
        } break;

        case DateTimeFormatSpecifier::DAY_OF_WEEK_TEXT: {
          auto weekdayNum = weekday.c_encoding();
          if (token.pattern.minRepresentDigits <= 3) {
            result += weekdaysShort[weekdayNum];
          } else {
            result += weekdaysFull[weekdayNum];
          }
        } break;

        case DateTimeFormatSpecifier::YEAR:
          result += padContent(
              static_cast<signed>(calDate.year()),
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::DAY_OF_YEAR: {
          auto firstDayOfTheYear = date::year_month_day(
              calDate.year(), date::month(1), date::day(1));
          auto delta =
              (date::sys_days{calDate} - date::sys_days{firstDayOfTheYear})
                  .count();
          delta += 1;
          result += padContent(delta, '0', token.pattern.minRepresentDigits);
        } break;

        case DateTimeFormatSpecifier::MONTH_OF_YEAR:
          result += padContent(
              static_cast<unsigned>(calDate.month()),
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::MONTH_OF_YEAR_TEXT:
          if (token.pattern.minRepresentDigits <= 3) {
            result += monthsShort[static_cast<unsigned>(calDate.month()) - 1];
          } else {
            result += monthsFull[static_cast<unsigned>(calDate.month()) - 1];
          }
          break;

        case DateTimeFormatSpecifier::DAY_OF_MONTH:
          result += padContent(
              static_cast<unsigned>(calDate.day()),
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::HALFDAY_OF_DAY:
          result += durationInTheDay.hours().count() < 12 ? "AM" : "PM";
          break;

        case DateTimeFormatSpecifier::HOUR_OF_HALFDAY:
        case DateTimeFormatSpecifier::CLOCK_HOUR_OF_HALFDAY:
        case DateTimeFormatSpecifier::HOUR_OF_DAY:
        case DateTimeFormatSpecifier::CLOCK_HOUR_OF_DAY: {
          auto hourNum = durationInTheDay.hours().count();
          if (token.pattern.specifier ==
              DateTimeFormatSpecifier::CLOCK_HOUR_OF_HALFDAY) {
            hourNum = (hourNum + 11) % 12 + 1;
          } else if (
              token.pattern.specifier ==
              DateTimeFormatSpecifier::HOUR_OF_HALFDAY) {
            hourNum = hourNum % 12;
          } else if (
              token.pattern.specifier ==
              DateTimeFormatSpecifier::CLOCK_HOUR_OF_DAY) {
            hourNum = (hourNum + 23) % 24 + 1;
          }
          result += padContent(hourNum, '0', token.pattern.minRepresentDigits);
        } break;

        case DateTimeFormatSpecifier::MINUTE_OF_HOUR:
          result += padContent(
              durationInTheDay.minutes().count() % 60,
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::SECOND_OF_MINUTE:
          result += padContent(
              durationInTheDay.seconds().count() % 60,
              '0',
              token.pattern.minRepresentDigits);
          break;

        case DateTimeFormatSpecifier::FRACTION_OF_SECOND:
          result += padContent(
                        durationInTheDay.subseconds().count(),
                        '0',
                        token.pattern.minRepresentDigits,
                        false)
                        .substr(0, token.pattern.minRepresentDigits);
          break;
        case DateTimeFormatSpecifier::TIMEZONE:
          // TODO: implement short name time zone, need a map from full name to
          // short name
          if (token.pattern.minRepresentDigits <= 3) {
            VELOX_UNSUPPORTED("short name time zone is not yet supported")
          }
          if (timezone == nullptr) {
            VELOX_USER_FAIL("Timezone unknown")
          }
          result += timezone->name();
          break;

        case DateTimeFormatSpecifier::TIMEZONE_OFFSET_ID:
          // TODO: implement timezone offset id formatting, need a map from full
          // name to offset time
        case DateTimeFormatSpecifier::WEEK_YEAR:
        case DateTimeFormatSpecifier::WEEK_OF_WEEK_YEAR:
        default:
          VELOX_UNSUPPORTED(
              "format is not supported for specifier {}",
              token.pattern.specifier);
      }
    }
  }
  return result;
}