Move commited bits out of folly
authorAlexey Spiridonov <lesha@fb.com>
Mon, 17 Mar 2014 22:59:25 +0000 (15:59 -0700)
committerDave Watson <davejwatson@fb.com>
Tue, 18 Mar 2014 17:02:24 +0000 (10:02 -0700)
Summary: Preparing to land the other diffs. No code changes here, just moving files around.

Test Plan: fbconfig bistro/common/cron/test bistro/common/cron/utils/test ; fbmake runtests

Reviewed By: yinwang@fb.com

FB internal diff: D1225159

folly/experimental/cron/CrontabSelector.h [deleted file]
folly/experimental/cron/date_time_utils.cpp [deleted file]
folly/experimental/cron/date_time_utils.h [deleted file]
folly/experimental/cron/test/test_crontab_selector.cpp [deleted file]
folly/experimental/cron/test/test_date_time_utils.cpp [deleted file]

diff --git a/folly/experimental/cron/CrontabSelector.h b/folly/experimental/cron/CrontabSelector.h
deleted file mode 100644 (file)
index e5fe0bf..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * 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
- *
- *   http://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.
- */
-
-#pragma once
-
-#include <algorithm>
-#include <vector>
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-
-#include "folly/Conv.h"
-#include "folly/dynamic.h"
-#include "folly/Format.h"
-#include "folly/json.h"
-
-// Add stateful Cron support for more robustness, see README for a design.
-
-namespace folly { namespace cron {
-
-/**
- * A CrontabSelector is a wildcard for matching some periodic value (e.g.
- * days of week, years, minutes, ...).  This is a base class, so Cron itself
- * uses a bunch of specialized descendants.
- *
- * The periodic values being matched are integers of varying signedness and
- * bit-widths.
- *
- * CrontabSelector is a union type, representing either:
- *   - A list of one or more values to match. E.g. January & March.
- *   - A start, an end, and an interval. This range type does not wrap:
- *     starting from Tuesday with an interval of 6 is equivalent to only
- *     Tuesday.
- *
- * A selector knows its minimum and maximum permissible value. For example,
- * a day-of-month is between 1 and 31, but real months may have fewer days,
- * which is handled at a higher level -- the selector is meant to apply
- * uniformly to all months.
- *
- * A selector also supports 3-character string-prefix aliases for the
- * integer value.  For example, "sun", "Sund", and "Sunday" would all map to
- * 1, while "sAt" is 7.  In the month type, "july" maps to 7, etc.  Note
- * that these strings are not localized -- specs are English-only, but this
- * is a feature, since otherwise a Cron configuration might be ambiguous
- * depending on the system locale.  Also note that the day-of-week numbering
- * is similarly unlocalized English, so the Russians who think Sunday == 7
- * will have to cope.
- *
- * Selectors are specified using JSON. Here are some month selectors:
- *
- *   5                              // The value 5, or May
- *   "Apr"                          // The value 4, or April
- *   ["Jun", "Jul", 12]             // June, July, or December
- *   {"interval": 1}                // Match any of the 12 months
- *   {"start": "Aug", "end": 11}    // August through November, inclusive
- *   {"interval": 2, "end": "Jul"}  // January, March, May, or July
- *
- * We do not implement the traditional Cron syntax because it's hard to read
- * and verify, and implies the presence of some Cron misfeatures that we do
- * not support.  A reasonable patch adding such parser would be accepted,
- * however.
- */
-template<typename Num>
-class CrontabSelector {
-public:
-
-  CrontabSelector(const CrontabSelector&) = delete;  // can neither copy
-  CrontabSelector& operator=(const CrontabSelector&) = delete;  // nor assign
-  virtual ~CrontabSelector() {}
-
-  /**
-   * Initialize the selector from a JSON object (see the class docblock).
-   */
-  void loadDynamic(const folly::dynamic &d) {
-    if (initialized_) {  // This restriction could be lifted if necessary.
-      throw std::runtime_error("loadDynamic cannot be called twice");
-    }
-    switch (d.type()) {
-      case folly::dynamic::Type::INT64:
-      case folly::dynamic::Type::STRING:
-        sorted_values_.emplace_back(parseValue(d));
-        break;
-      case folly::dynamic::Type::ARRAY:
-        for (const auto& val : d) {
-          sorted_values_.emplace_back(parseValue(val));
-        }
-        // If somebody specifies [] for a selector, we have to silently
-        // accept it, since PHP's JSON library can morph {} into [], and {}
-        // must mean "default selector accepting all values."
-        break;
-      case folly::dynamic::Type::OBJECT:
-        for (const auto& pair : d.items()) {
-          // Interval is first so that it doesn't accept strings like "jan"
-          if (pair.first == "interval") {
-            interval_ = pair.second.asInt();
-            if (interval_ < 1 || interval_ >= max_val_ - min_val_) {
-              throw std::runtime_error(folly::format(
-                "interval not in [1, {}]: {}", max_val_ - min_val_, interval_
-              ).str());
-            }
-            continue;
-          }
-          // For start & end, we are happy to accept string names
-          auto val = parseValue(pair.second);
-          if (pair.first == "start") {
-            start_ = val;
-          } else if (pair.first == "end") {
-            end_ = val;
-          } else {
-            throw std::runtime_error(folly::format(
-              "Unknown key: {}", pair.first
-            ).str());
-          }
-        }
-        // If we got an empty object, no problem -- this selector will
-        // follow the default of "match everything".
-        break;
-      default:
-        throw std::runtime_error(folly::format(
-          "Bad type for crontab selector: {}", d.typeName()
-        ).str());
-    }
-    std::sort(sorted_values_.begin(), sorted_values_.end());
-    initialized_ = true;
-  }
-
-  /**
-   * Returns the first t_match >= t such that t_match matches this selector.
-   * If no match is found, wraps around to the selector's first element, and
-   * sets the "carry" bool to true.  Otherwise, that value is false.
-   *
-   * Note: no modular arithmetic happens here -- as soon as we exceed end_,
-   * we simply reset to start_.  This is not the full story for
-   * day-of-month, so StandardCrontabItem has to do some extra work there.
-   * The simple "wrap to start" behavior is correct because with modular
-   * arithmetic a "week" selector starting on Tuesday with a stride of 6
-   * would accept any day of the week, which is far more surprising than
-   * only accepting Tuesday.
-   */
-  std::pair<Num, bool> findFirstMatch(Num t) const {
-    if (!initialized_) {
-      throw std::runtime_error("Selector not initialized");
-    }
-
-    if (!sorted_values_.empty()) {
-      // If this were stateful, we could remember where the previous item was,
-      // but as is, we incur the log(n) search time every time.
-      auto i =
-        std::lower_bound(sorted_values_.begin(), sorted_values_.end(), t);
-      if (i == sorted_values_.end()) {  // Wrap to the start
-        return std::make_pair(*sorted_values_.begin(), true);
-      }
-      return std::make_pair(*i, false);
-    }
-
-    if (t < start_) {
-      return std::make_pair(start_, false);
-    }
-    int64_t next = t + (interval_ - (t - start_) % interval_) % interval_;
-    if (next > end_) {
-      return std::make_pair(start_, true);
-    }
-    return std::make_pair(next, false);
-  }
-
-  bool isInitialized() const { return initialized_; }
-
-  /**
-   * A compact string representation for unit tests or debugging, which sort
-   * of emulates standard cron syntax.  This function is subject to change
-   * without notice -- send a patch with toDynamic() if you need to inspect
-   * the contents of a selector in production code.  We will NOT fix your
-   * code if you are using this function.  You have been warned.
-   */
-  std::string getPrintable() const {
-    std::string s;
-    for (const auto &v : sorted_values_) {
-      folly::toAppend(v, ',', &s);
-    }
-    if (start_ != min_val_ || end_ != max_val_ || interval_ != 1) {
-      if (!sorted_values_.empty()) {
-        s[s.size() - 1] = '/';
-      }
-      folly::toAppend(start_, '-', end_, ':', interval_, &s);
-    } else if (sorted_values_.empty()) {
-      folly::toAppend('*', &s);
-    } else {
-      s.pop_back();
-    }
-    return s;
-  }
-
-  Num getMin() const { return min_val_; }
-  Num getMax() const { return max_val_; }
-
-protected:
-  typedef std::unordered_map<std::string, Num> PrefixMap;
-
-  CrontabSelector(Num min_val, Num max_val)
-    : initialized_(false),
-      start_(min_val),
-      end_(max_val),
-      interval_(1),
-      min_val_(min_val),
-      max_val_(max_val) {}
-
-  /**
-   * Converts 3-letter string prefixes to numeric values, e.g. Jan=>1, Wed=>3
-   */
-  virtual const PrefixMap& getPrefixMap() const { return emptyPrefixMap_; }
-
-private:
-  Num parseValue(const folly::dynamic& d) {
-    Num res;
-    if (d.isInt()) {
-      res = d.asInt();
-    } else if (d.isString()) {
-      // TODO: This prefix-matching could be more robust... we don't even
-      // check if the prefix map is populated with 3-character strings.
-      auto s = d.asString().substr(0, 3).toStdString();
-      std::transform(s.begin(), s.end(), s.begin(), ::tolower);
-      auto prefix_map = getPrefixMap();
-      auto i = prefix_map.find(s);
-      if (i == prefix_map.end()) {
-        throw std::runtime_error(folly::format(
-          "Cannot convert prefix to int: {}", s
-        ).str());
-      }
-      res = i->second;
-    } else {
-      throw std::runtime_error(folly::format(
-        "Cannot parse {}", folly::toJson(d)
-      ).str());
-    }
-    if (res < min_val_ || res > max_val_) {
-      throw std::runtime_error(folly::format(
-        "Value {} out of range [{}, {}]", res, min_val_, max_val_
-      ).str());
-    }
-    return res;
-  }
-
-  bool initialized_;
-
-  std::vector<Num> sorted_values_;
-  // These three are unused when sorted_values_ is set
-  Num start_;
-  Num end_;
-  Num interval_;
-
-  // Default values for start & end, also used for range-checking.
-  const Num min_val_;
-  const Num max_val_;
-
-  // Should be static but it's too hard to do that with templates.
-  // TODO(agoder): Make this better.
-  const std::unordered_map<std::string, Num> emptyPrefixMap_;
-};
-
-}}
diff --git a/folly/experimental/cron/date_time_utils.cpp b/folly/experimental/cron/date_time_utils.cpp
deleted file mode 100644 (file)
index d518250..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * 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
- *
- *   http://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 "folly/experimental/cron/date_time_utils.h"
-
-#include <boost/date_time/c_local_time_adjustor.hpp>
-#include <cerrno>
-
-#include "folly/Format.h"
-
-namespace folly { namespace cron {
-
-using namespace boost::local_time;
-using namespace boost::posix_time;
-using namespace std;
-
-// NB: The exceptions below are intended to confirm that the underlying
-// libraries behave in a sane way.  This makes them untestable.  I got each
-// of them to fire by temporarily changing their checks, so if they do fire,
-// the printouts should be okay.  It's fine to change them to CHECKs.
-
-time_t getUTCOffset(time_t utc_time, time_zone_ptr tz) {
-  auto utc_pt = from_time_t(utc_time);
-  auto local_pt = utcPTimeToTimezoneLocalPTime(utc_pt, tz);
-  return (local_pt - utc_pt).total_seconds();
-}
-
-ptime utcPTimeToTimezoneLocalPTime(ptime utc_pt, time_zone_ptr tz) {
-  if (tz) {
-    return local_date_time{utc_pt, tz}.local_time();
-  } else {
-    return boost::date_time::c_local_adjustor<ptime>::utc_to_local(utc_pt);
-  }
-}
-
-UTCTimestampsForLocalTime _boostTimezoneLocalPTimeToUTCTimestamps(
-  ptime local_pt,
-  time_zone_ptr tz
-) {
-  UTCTimestampsForLocalTime res;
-  auto local_date = local_pt.date();
-  auto local_time = local_pt.time_of_day();
-
-  auto save_timestamp_if_valid = [&](bool is_dst, time_t *out) {
-    try {
-      auto local_dt = local_date_time(local_date, local_time, tz, is_dst);
-      // local_date_time() ignores is_dst if the timezone does not have
-      // DST (instead of throwing dst_not_valid).  So, we must confirm
-      // that our is_dst guess was correct to avoid storing the same
-      // timestamp in both fields of res (same as problem (b) in the
-      // localtime_r code path).
-      if (local_dt.is_dst() == is_dst) {
-        *out = (local_dt.utc_time() - from_time_t(0)).total_seconds();
-      }
-    } catch (dst_not_valid& e) {
-      // Continue, we're trying both values of is_dst
-    }
-  };
-
-  try {
-    save_timestamp_if_valid(true, &res.dst_time);
-    save_timestamp_if_valid(false, &res.non_dst_time);
-  } catch (time_label_invalid& e) {
-    // This local time label was skipped by DST, so res will be empty.
-  }
-  return res;
-}
-
-UTCTimestampsForLocalTime _systemTimezoneLocalPTimeToUTCTimestamps(
-  ptime local_pt
-) {
-  UTCTimestampsForLocalTime res;
-  struct tm tm = to_tm(local_pt);
-  auto save_timestamp_if_valid = [tm, &local_pt](int is_dst, time_t *out) {
-    // Try to make a UTC timestamp based on our DST guess and local time.
-    struct tm tmp_tm = tm;  // Make a copy since mktime changes the tm
-    tmp_tm.tm_isdst = is_dst;
-    time_t t = mktime(&tmp_tm);
-    if (t == -1) {  // Not sure of the error cause or how to handle it.
-      throw runtime_error(folly::format(
-        "{}: mktime error {}", to_simple_string(local_pt), errno
-      ).str());
-    }
-
-    // Convert the timestamp to a local time to see if the guess was right.
-    struct tm new_tm;
-    auto out_tm = localtime_r(&t, &new_tm);
-    if (out_tm == nullptr) {  // Not sure if such errors can be handled.
-      throw runtime_error(folly::format(
-        "{}: localtime_r error {}", to_simple_string(local_pt), errno
-      ).str());
-    }
-
-    // Does the original tm argree with the tm generated from the mktime()
-    // UTC timestamp?  (We'll check tm_isdst separately.)
-    //
-    // This test never passes when we have a local time label that is
-    // skipped when a DST change moves the clock forward.
-    //
-    // A valid local time label always has one or two valid DST values.
-    // When the timezone has not DST, that value is "false".
-    //
-    // This test always passes when:
-    //  - The DST value is ambiguous (due to the local clock moving back).
-    //  - We guessed the uniquely valid DST value.
-    //
-    // The test may or may not always pass (implementation-dependent) when
-    // we did not guess a valid DST value.
-    //  (a) If it does not pass, we are good, because we also try the other
-    //      DST value, which will make the test pass, and then res will have
-    //      a unique timestamp.
-    //  (b) If it does pass, we're in more trouble, because it means that
-    //      the implementation ignored our is_dst value. Then, the timestamp
-    //      t is the same as for the other is_dst value.  But, we don't want
-    //      res to be labeled ambiguous, and we don't want to randomly pick
-    //      a DST value to set to kNotATime, because clients may want to
-    //      know the real DST value.  The solution is the extra test below.
-    if (
-      tm.tm_sec == new_tm.tm_sec && tm.tm_min == new_tm.tm_min &&
-      tm.tm_hour == new_tm.tm_hour && tm.tm_mday == new_tm.tm_mday &&
-      tm.tm_mon == new_tm.tm_mon && tm.tm_year == new_tm.tm_year &&
-      // To fix problem (b) above, we must assume that localtime_r returns
-      // the correct tm_isdst (if not, it's a system bug anyhow).  Then, we
-      // can just check our DST guess against the truth.  If our guess was
-      // invalid, we shouldn't store the result, avoiding (b).
-      !(  // tm_isdst can also be negative but we'll check that later
-        (new_tm.tm_isdst == 0 && is_dst) || (new_tm.tm_isdst > 0 && !is_dst)
-      )
-    ) {
-      *out = t;
-    }
-    return new_tm.tm_isdst < 0;  // Used for a sanity-check below.
-  };
-
-  bool neg_isdst1 = save_timestamp_if_valid(1, &res.dst_time);
-  bool neg_isdst2 = save_timestamp_if_valid(0, &res.non_dst_time);
-
-  // The only legitimate way for localtime_r() to give back a negative
-  // tm_isdst is if the input local time label is ambiguous due to DST.
-  if (neg_isdst1 || neg_isdst2) {
-    if (neg_isdst1 ^ neg_isdst2) {  // Can't be ambiguous half the time
-      throw runtime_error(folly::format(
-        "{}: one tm_isdst negative but not both", to_simple_string(local_pt)
-      ).str());
-    }
-    if (!res.isAmbiguous()) {
-      throw runtime_error(folly::format(
-        "{}: negative tm_isdst but time label is unambiguous",
-        to_simple_string(local_pt)
-      ).str());
-    }
-  }
-  return res;
-}
-
-UTCTimestampsForLocalTime timezoneLocalPTimeToUTCTimestamps(
-  ptime local_pt,
-  time_zone_ptr tz
-) {
-  UTCTimestampsForLocalTime res;
-  if (tz) {
-    res = _boostTimezoneLocalPTimeToUTCTimestamps(local_pt, tz);
-  } else {
-    res = _systemTimezoneLocalPTimeToUTCTimestamps(local_pt);
-  }
-  // Both code paths have fixes to prevent this (see e.g. problem (b) above).
-  if (res.isAmbiguous() && res.dst_time == res.non_dst_time) {
-    throw runtime_error(folly::format(
-      "{}: local time maps to {} regardless of tm_isdst",
-      to_simple_string(local_pt), res.dst_time
-    ).str());
-  }
-  return res;
-}
-
-}}
diff --git a/folly/experimental/cron/date_time_utils.h b/folly/experimental/cron/date_time_utils.h
deleted file mode 100644 (file)
index 04a27d7..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * 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
- *
- *   http://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.
- */
-
-/**
- * This is a small extension on top of boost::date_time, which handles
- * conversions between "local time labels" (e.g.  "2am, March 10, 2013") and
- * POSIX UTC timestamps.
- *
- * Our library hides two sources of complexity:
- *
- *  - Time zones. You can easily use the system time zone (pass a NULL
- *    pointer), or provide a boost::time_zone_ptr (usually created via a
- *    POSIX-like timezone format, or from the boost::date_time timezone DB).
- *
- *  - The one-to-many relationship between time labels and UTC timestamps.
- *
- *    UTC timestamps are effectively monotonic (aside from leap seconds,
- *    which are ignored in POSIX time, and are irrelevant for Cron).
- *
- *    Local time labels, on the other hand, can move forward or backward due
- *    to daylight-savings changes.  Thus, when the local clock rewinds due
- *    to DST, some local time labels become ambiguous (is it 1:30am before
- *    or after the DST rewind?).  When the local time moves forward due to
- *    DST, some local time labels are skipped (in the US Pacific timezone,
- *    2:30am never happened on March 10, 2013).
- *
- *    As a consequence, timezoneLocalPTimeToUTCTimestamps() returns a struct
- *    UTCTimestampsForLocalTime that can represent 0, 1, or 2 UTC timestamps.
- *
- *    The ambiguity could be avoided by adding an 'is_dst' flag to the local
- *    time label, but this is not useful for the purposes of Cron (and is
- *    handled adequately in existing libraries).
- *
- *    Going from UTC to a local time label is easy and unambiguous, see
- *    utcPTimeToTimezoneLocalPTime().
- *
- * CAVEAT: We use boost::posix_time::ptime to represent both UTC timestamps,
- * *and* local time labels.  This is confusing -- it would be better if
- * local time labels should used a separate type.  However, a ptime is very
- * convenient for the purpose, since it supports all the usual time
- * operations you might want to do.  Patches are welcome.
- *
- * Our library thus accounts for the following deficiencies of
- * boost::date_time:
- *
- *  - boost::date_time has almost no support for the system timezone (the only
- *    related feature is the hacky "c_local_adjustor"). In contrast, our
- *    library interprets a time_zone_ptr value of NULL as referring to the
- *    system timezone, and then does the right thing.
- *
- *  - boost::date_time has a rather annoying exception-based API for
- *    determining whether a local time label is ambiguous, nonexistent, or
- *    unique.  Our struct is much more usable.
- */
-
-#pragma once
-
-#include <boost/date_time/local_time/local_time_types.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-#include <ctime>
-#include <stdexcept>
-#include <utility>
-
-#include "folly/Format.h"
-
-namespace folly { namespace cron {
-
-/**
- * How many seconds must be added to UTC in order to get the local time for
- * the given time point?
- */
-time_t getUTCOffset(time_t utc_time, boost::local_time::time_zone_ptr tz);
-
-/**
- * Convert a UTC ptime into a timezone-local ptime.
- *
- * If tz is a null pointer, use the local timezone.
- *
- * This is a lossy transformation, since the UTC offset of a timezone
- * is not constant -- see timezoneLocalPTimeToUTCTimestamps()
- * for a detailed explanation.
- */
-boost::posix_time::ptime utcPTimeToTimezoneLocalPTime(
-  boost::posix_time::ptime utc_pt,
-  boost::local_time::time_zone_ptr tz
-);
-
-/**
- * A local time label can correspond to 0, 1, or 2 UTC timestamps due to
- * DST time shifts:
- *  - If the clock went back and your label lands in the repeated interval,
- *    you'll get both timestamps.
- *  - If the clock went forward, and your label landed in the skipped time,
- *    you get neither.
- *  - For all other labels you get exactly one timestamp.
- * See also timezoneLocalPTimeToUTCTimestamps().
- */
-struct UTCTimestampsForLocalTime {
-  static const time_t kNotATime = -1;  // Might not portable, but easy.
-
-  UTCTimestampsForLocalTime() : dst_time{kNotATime}, non_dst_time{kNotATime} {}
-
-  bool isNotATime() const {
-    return dst_time == kNotATime && non_dst_time == kNotATime;
-  }
-
-  bool isAmbiguous() const {
-    return dst_time != kNotATime && non_dst_time != kNotATime;
-  }
-
-  time_t getUnique() const {
-    if (isAmbiguous()) {
-      throw std::runtime_error(folly::format(
-        "Local time maps to both {} and {}", dst_time, non_dst_time
-      ).str());
-    } else if (dst_time != kNotATime) {
-      return dst_time;
-    } else if (non_dst_time != kNotATime) {
-      return non_dst_time;
-    } else {
-      throw std::runtime_error("This local time was skipped due to DST");
-    }
-  }
-
-  /**
-   * For ambiguous local time labels, return the pair of (lesser UTC
-   * timestamp, greater UTC timestamp).
-   *
-   * NOTE: This may not be strictly necessary, since DST is probably less
-   * than non-DST in all real timezones, but it's better to be safe than
-   * sorry.
-   *
-   * More specifically, the POSIX timezone specification (IEEE Std 1003.1)
-   * allows DST to be either ahead or behind of the regular timezone, so the
-   * local timezone could shift either way.  The docs for
-   * boost::local_time::posix_time_zone (which is not even a POSIX-compliant
-   * implementation, see README) are ambiguous, but can be read as intending
-   * to forbid DST that sets the clock backwards.
-   */
-  std::pair<time_t, time_t> getBothInOrder() const {
-    if (!isAmbiguous()) {
-      throw std::runtime_error(folly::format(
-        "{} and {} is not ambiguous", dst_time, non_dst_time
-      ).str());
-    }
-    if (dst_time < non_dst_time) {
-      return std::make_pair(dst_time, non_dst_time);
-    }
-    return std::make_pair(non_dst_time, dst_time);
-  }
-
-  time_t dst_time;
-  time_t non_dst_time;
-};
-
-/**
- * Convert a timezone-local ptime into UTC epoch timestamp(s).
- *
- * If tz is a null pointer, use the local timezone.
- *
- * WARNING 1: When DST sets back the clock, some local times become
- * ambiguous -- you cannot tell if the timestamp lies before or after the
- * DST change.  For example, "November 3 01:30:00 2013" could be either PST
- * or PDT, with a difference of one hour.
- *
- * WARNING 2: You can inadvertently make a local time that does not exist
- * because a daylight savings change skips that time period.
- */
-UTCTimestampsForLocalTime timezoneLocalPTimeToUTCTimestamps(
-  boost::posix_time::ptime local_pt,
-  boost::local_time::time_zone_ptr tz
-);
-
-}}
diff --git a/folly/experimental/cron/test/test_crontab_selector.cpp b/folly/experimental/cron/test/test_crontab_selector.cpp
deleted file mode 100644 (file)
index 9fbd7af..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * 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
- *
- *   http://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 <gtest/gtest.h>
-
-#include "folly/experimental/cron/CrontabSelector.h"
-#include "folly/json.h"
-
-using namespace folly::cron;
-using namespace folly;
-using namespace std;
-
-// Templates lead to poor error messages, so the preprocessor rules this land
-
-#define check_parse_impl(Selector, json, expected_printable) { \
-  Selector c; \
-  c.loadDynamic(parseJson(json)); \
-  EXPECT_EQ(expected_printable, c.getPrintable()); \
-}
-
-#define check_first_match_impl(Selector, Num, json, t, match, carry) { \
-  Num match_ = match;  /* Make the input the right type */ \
-  Selector c; \
-  c.loadDynamic(parseJson(json)); \
-  EXPECT_EQ(make_pair(match_, carry), c.findFirstMatch(t)); \
-}
-
-#define check_parse(json, expected_printable) \
-  check_parse_impl(ClownCrontabSelector, json, expected_printable)
-
-#define check_first_match(json, t, match, carry) \
-  check_first_match_impl( \
-    ClownCrontabSelector, unsigned short, json, t, match, carry \
-  )
-
-/**
- * There are 7 clowns in the world, numbered from 1 through 7, with the
- * names listed below.
- */
-class ClownCrontabSelector : public CrontabSelector<unsigned short> {
-public:
-  ClownCrontabSelector() : CrontabSelector(1, 7) {}
-protected:
-  virtual const PrefixMap& getPrefixMap() const { return clownToInt_; }
-private:
-  const static PrefixMap clownToInt_;
-};
-const ClownCrontabSelector::PrefixMap ClownCrontabSelector::clownToInt_{
-  {"sun", 1},  // Sunny
-  {"mon", 2},  // Monica
-  {"tue", 3},  // Tuelly
-  {"wed", 4},  // Wedder
-  {"thu", 5},  // Thurmond
-  {"fri", 6},  // Friedrich
-  {"sat", 7},  // Satay
-};
-
-TEST(TestCrontabSelector, CheckParseRanges) {
-  check_parse("{}", "*");
-  check_parse("{\"interval\": 3}", "1-7:3");
-  check_parse("{\"start\": 2}", "2-7:1");
-  check_parse("{\"start\": \"Sunny\"}", "*");
-  check_parse("{\"end\": \"Thu\"}", "1-5:1");
-  check_parse("{\"start\": \"Tuelly\", \"end\": \"Wed\"}", "3-4:1");
-  check_parse("{\"end\": \"Fri\", \"interval\": 2}", "1-6:2");
-}
-
-TEST(TestCrontabSelector, CheckParseValues) {
-  check_parse("5", "5");
-  check_parse("\"Thu\"", "5");
-  check_parse("[\"Sat\", 1, \"Fri\"]", "1,6,7");
-}
-
-TEST(TestCrontabSelector, CheckMatchValues) {
-  for (int64_t i = 0; i < 10; ++i) {  // A single value never changes
-    check_first_match("5", i, 5, i > 5);
-  }
-
-  // Now check a list of values
-  check_first_match("[2,4,5]", 0, 2, false);
-  check_first_match("[2,4,5]", 1, 2, false);
-  check_first_match("[2,4,5]", 2, 2, false);
-  check_first_match("[2,4,5]", 3, 4, false);
-  check_first_match("[2,4,5]", 4, 4, false);
-  check_first_match("[2,4,5]", 5, 5, false);
-  check_first_match("[2,4,5]", 6, 2, true);
-  check_first_match("[2,4,5]", 7, 2, true);
-}
-
-TEST(TestCrontabSelector, CheckMatchDefaultRange) {
-  check_first_match("{}", 0, 1, false);
-  check_first_match("[]", 1, 1, false);
-  check_first_match("[]", 6, 6, false);
-  check_first_match("{}", 7, 7, false);
-  check_first_match("{}", 8, 1, true);
-  check_first_match("[]", 10, 1, true);
-}
-
-TEST(TestCrontabSelector, CheckMatchNontrivialRange) {
-  string range = "{\"start\": 3, \"end\": 7, \"interval\": 2}";
-  check_first_match(range, 1, 3, false);
-  check_first_match(range, 2, 3, false);
-  check_first_match(range, 3, 3, false);
-  check_first_match(range, 4, 5, false);
-  check_first_match(range, 5, 5, false);
-  check_first_match(range, 6, 7, false);
-  check_first_match(range, 7, 7, false);
-  check_first_match(range, 8, 3, true);
-}
-
-TEST(TestCrontabSelector, CheckInit) {
-  ClownCrontabSelector s;
-
-  EXPECT_THROW(s.findFirstMatch(5), runtime_error);  // Not initialized
-
-  // Initialized and functional
-  s.loadDynamic(dynamic::object());
-  EXPECT_EQ(make_pair((unsigned short)5, false), s.findFirstMatch(5));
-
-  // Cannot double-initialize
-  EXPECT_THROW(s.loadDynamic(dynamic::object()), runtime_error);
-}
-
-TEST(TestCrontabSelector, CheckParseErrors) {
-  // Out-of-range intervals
-  EXPECT_THROW(check_parse("{\"interval\": 0}", ""), runtime_error);
-  EXPECT_THROW(check_parse("{\"interval\": -1}", ""), runtime_error);
-  EXPECT_THROW(check_parse("{\"interval\": 7}", ""), runtime_error);
-
-  // Out-of-range start or end
-  EXPECT_THROW(check_parse("{\"start\": 0}", ""), runtime_error);
-  EXPECT_THROW(check_parse("{\"end\": 8}", ""), runtime_error);
-
-  // Problematic JSON inputs
-  EXPECT_THROW(check_parse("{\"bad_key\": 3}", ""), runtime_error);
-  EXPECT_THROW(check_parse("0.1", ""), runtime_error);  // no floats
-
-  // Invalid values
-  EXPECT_THROW(check_parse("\"Chicken\"", ""), runtime_error);
-  EXPECT_THROW(check_parse("\"Th\"", ""), runtime_error);  // Need >= 3 chars
-  EXPECT_THROW(check_parse("\"S\"", ""), runtime_error);
-  EXPECT_THROW(check_parse("{\"start\": 0.3}", ""), runtime_error);  // float
-}
diff --git a/folly/experimental/cron/test/test_date_time_utils.cpp b/folly/experimental/cron/test/test_date_time_utils.cpp
deleted file mode 100644 (file)
index d338da0..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * 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
- *
- *   http://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 <gtest/gtest.h>
-
-#include <boost/date_time/gregorian/gregorian.hpp>
-#include <boost/date_time/local_time/local_time.hpp>  // for posix_time_zone
-#include <cstdlib>   // for setenv()
-
-#include "folly/experimental/cron/date_time_utils.h"
-
-using namespace folly::cron;
-using namespace boost::local_time;
-using namespace boost::gregorian;
-using namespace boost::posix_time;
-using namespace std;
-
-enum class Ambiguity {
-  Unique,
-  Ambiguous,
-  Unknown,
-};
-
-void check_not_a_local_time(ptime local_pt, time_zone_ptr tz) {
-  EXPECT_TRUE(
-    timezoneLocalPTimeToUTCTimestamps(local_pt, tz).isNotATime()
-  );
-}
-
-void check_local_ptime(time_t utc_t, time_zone_ptr tz, ptime expected_pt) {
-  EXPECT_EQ(
-    expected_pt, utcPTimeToTimezoneLocalPTime(from_time_t(utc_t), tz)
-  );
-}
-
-void check_to_local_and_back(time_t utc_t, time_zone_ptr tz, Ambiguity n) {
-  auto utc_ts = timezoneLocalPTimeToUTCTimestamps(
-    utcPTimeToTimezoneLocalPTime(from_time_t(utc_t), tz), tz
-  );
-  if (n == Ambiguity::Unique) {
-    EXPECT_FALSE(utc_ts.isAmbiguous());
-    EXPECT_FALSE(utc_ts.isNotATime());
-  } else if (n == Ambiguity::Ambiguous) {
-    EXPECT_TRUE(utc_ts.isAmbiguous());
-  }
-  EXPECT_FALSE(utc_ts.isNotATime());  // Cannot get here from a UTC timestamp
-  if (utc_ts.isAmbiguous()) {
-    EXPECT_PRED3(
-      [](time_t t1, time_t t2, time_t t3){ return t1 == t3 || t2 == t3; },
-      utc_ts.dst_time, utc_ts.non_dst_time, utc_t
-    );
-  } else {
-    EXPECT_EQ(utc_t, utc_ts.getUnique());
-  }
-}
-
-const int k1980_Feb29_4AM_PST = 320673600;
-const time_t kTestTimestamps[] = {
-  0, k1980_Feb29_4AM_PST, // Some edge cases
-  // Random values from [randrange(0, int(time())) for i in range(200)]
-  851763124, 261861130, 743855544, 30239098, 569168784, 850101954,
-  1113053877, 1364858223, 1082354444, 1294020427, 258495434, 1121030318,
-  192467213, 484525368, 579184768, 167376207, 689233030, 1351587900,
-  1214561991, 661713049, 381308132, 665152213, 94230657, 1349426746,
-  298195324, 1257615713, 682132890, 1018217831, 916554585, 995955072,
-  1117317370, 802646927, 608115326, 633743809, 109769810, 543272111,
-  609037871, 104418231, 264752638, 306399494, 1035358804, 766418015,
-  1128611920, 181391436, 839616511, 796842798, 653512454, 1010622273,
-  875647954, 708203495, 822980713, 991547420, 1265028641, 1347606382,
-  1002331337, 1164592802, 31466919, 1065361177, 1225097252, 631276316,
-  527190864, 492850662, 327182508, 869358924, 140894012, 1146198515,
-  501023608, 933017248, 324137101, 710311561, 556527520, 38622381,
-  203388537, 475269797, 724361468, 814834023, 208189749, 815722762,
-  45610280, 761977400, 933451311, 660014659, 494207495, 765580653,
-  1243453093, 234300455, 1345693003, 158935011, 1173706097, 315858792,
-  1184431509, 477296062, 276535773, 928860110, 103635291, 708434135,
-  51126476, 160505670, 153146671, 354980180, 890292051, 1155669986,
-  630375563, 349261331, 620264499, 477756621, 901672130, 618524356,
-  252709868, 1213920374, 233303580, 3012130, 969038324, 202252395,
-  1187016766, 669825568, 257426556, 214600753, 995259569, 360335117,
-  1199390931, 925221855, 616957946, 745607758, 1304023574, 383936310,
-  952313824, 251320075, 1018206981, 18254870, 949794553, 794223010,
-  22167074, 971353751, 836775665, 132713147, 1385328705, 564225254,
-  89489672, 970288768, 727638691, 1384138213, 295605253, 565194711,
-  268066246, 262980328, 878120933, 501014040, 950529654, 899180133,
-  452320225, 1232572199, 894784724, 24260103, 331355470, 593434097,
-  986752149, 590771435, 36704582, 1081058342, 231884390, 418573190,
-  580513906, 416611430, 778410883, 393299067, 891265387, 545143528,
-  242177530, 43413747, 774970054, 623606322, 1088170511, 925487121,
-  276552897, 904380544, 407117624, 877143874, 901504406, 1060658206,
-  378376447, 566370202, 903180278, 299280550, 1064440994, 742066503,
-  402041226, 1388625249, 1316863228, 749053705, 426181185, 1239538923,
-  221164890, 1049484190, 98669029, 414059052, 930992061, 34048214,
-  496162677, 206881990
-};
-
-void check_timezone_without_dst(time_zone_ptr tz) {
-  for (time_t utc_t: kTestTimestamps) {
-    check_to_local_and_back(utc_t, tz, Ambiguity::Unique);
-  }
-}
-
-void check_timezone_with_dst(
-  time_zone_ptr tz, int amb_first, int amb_mid, int amb_last, int after_skip
-) {
-  // DST-ambiguous values
-  for (time_t utc_t : {amb_first, amb_mid, amb_mid + 1, amb_last}) {
-    check_to_local_and_back(utc_t, tz, Ambiguity::Ambiguous);
-  }
-
-  // Timestamps bordering the DST transitions
-  for (time_t utc_t : {
-    amb_first - 1, amb_last + 1,  // The ambiguous range is tight
-    after_skip, after_skip - 1  // The DST-skipped interval has no impact
-  }) {
-    check_to_local_and_back(utc_t, tz, Ambiguity::Unique);
-  }
-
-  // Lots of random timestamps
-  for (time_t utc_t: kTestTimestamps) {
-    check_to_local_and_back(utc_t, tz, Ambiguity::Unknown);
-  }
-}
-
-// These are 3 hours apart with the same DST rules in 2013, so it's easy to
-// check UTC => local ptime conversions, and invalid local time labels.
-void check_us_eastern_or_pacific(time_zone_ptr tz, int offset_from_pacific) {
-  // 2013: Nov 3 - 1AM PDT, 1:59:59AM PDT, 1:59:59AM PST; Mar 10 - 3AM PDT
-  time_t amb_start = 1383465600 + offset_from_pacific;
-  time_t amb_mid = 1383469199 + offset_from_pacific;
-  time_t amb_end = 1383472799 + offset_from_pacific;
-  time_t after_skip = 1362909600 + offset_from_pacific;
-  ptime amb_start_pt(date(2013, 11, 3), hours(1));
-  ptime amb_mid_end_pt(date(2013, 11, 3), time_duration(1, 59, 59));
-  ptime before_skip_pt(date(2013, 3, 10), time_duration(1, 59, 59));
-  ptime after_skip_pt(date(2013, 3, 10), hours(3));
-
-  // Test mapping to local ptimes and back to UTC timestamps.
-  check_timezone_with_dst(tz, amb_start, amb_mid, amb_end, after_skip);
-  check_local_ptime(amb_start, tz, amb_start_pt);
-  check_local_ptime(amb_mid, tz, amb_mid_end_pt);
-  check_local_ptime(amb_end, tz, amb_mid_end_pt);
-  check_local_ptime(after_skip - 1, tz, before_skip_pt);
-  check_local_ptime(
-    k1980_Feb29_4AM_PST + offset_from_pacific, tz,
-    ptime(date(1980, 2, 29), hours(4))
-  );
-  check_not_a_local_time(before_skip_pt + seconds(1), tz);
-  check_not_a_local_time(before_skip_pt + seconds(1800), tz);
-  check_not_a_local_time(before_skip_pt + seconds(3600), tz);
-  check_not_a_local_time(after_skip_pt - seconds(1), tz);
-  check_not_a_local_time(after_skip_pt - seconds(1800), tz);
-  check_not_a_local_time(after_skip_pt - seconds(3600), tz);
-  check_local_ptime(after_skip, tz, after_skip_pt);
-
-  // A light test for getUTCOffset(), since its constituents are well-tested.
-  EXPECT_EQ(-25200 - offset_from_pacific, getUTCOffset(amb_start, tz));
-  EXPECT_EQ(-25200 - offset_from_pacific, getUTCOffset(amb_mid, tz));
-  EXPECT_EQ(-28800 - offset_from_pacific, getUTCOffset(amb_mid + 1, tz));
-  EXPECT_EQ(-28800 - offset_from_pacific, getUTCOffset(amb_end, tz));
-  EXPECT_EQ(
-    -28800 - offset_from_pacific, getUTCOffset(k1980_Feb29_4AM_PST, tz)
-  );
-}
-
-TEST(TestDateTimeUtils, AllTheThings) {
-
-  // Exercise the local timezone code path: US Pacific & US Eastern
-  time_zone_ptr tz;
-  setenv("TZ", "PST+8PDT,M3.2.0,M11.1.0", 1);
-  tzset();
-  check_us_eastern_or_pacific(tz, 0);
-  setenv("TZ", "EST+5EDT,M3.2.0,M11.1.0", 1);
-  tzset();
-  check_us_eastern_or_pacific(tz, -10800);
-
-  // Local timezone code with DST-free timezones
-  for (auto& tz_name :  {"MST+7", "GMT-14", "GMT+12", "GMT-4:30"}) {
-    setenv("TZ", tz_name, 1);
-    tzset();
-    check_timezone_without_dst(tz);
-  }
-
-  // Also US Pacific & US Eastern, but with the boost::date_time code.
-  // The signs differ from the setenv() calls above, since boost::local_time
-  // incorrectly implements the standard. Compare these:
-  // http://tools.ietf.org/html/draft-ietf-dhc-timezone-01
-  // http://www.boost.org/doc/libs/1_55_0/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone
-  tz.reset(new posix_time_zone{"PST-8PDT,M3.2.0,M11.1.0"});
-  check_us_eastern_or_pacific(tz, 0);
-  tz.reset(new posix_time_zone{"EST-5EDT,M3.2.0,M11.1.0"});
-  check_us_eastern_or_pacific(tz, -10800);
-
-  // DST-free timezones with the boost::date_time code (signs also flipped)
-  for (auto& tz_name : {"MST-7", "GMT+14", "GMT-12", "GMT+4:30"}) {
-    tz.reset(new posix_time_zone{tz_name});
-    check_timezone_without_dst(tz);
-  }
-}