text: define(
    [
      'module',
      'exports'
    ],
    function(
      $module,
      exports
    ) {
  
  ////////////////////////////////////////////////////////////////////////////////
  // Time
  //
  // == JS Dates
  //
  // We primarily deal in UTC timestamps.  When we need to talk dates with IMAP
  // (see next section), we need these timestamps to line up with midnight for
  // a given day.  We do not need to line up with weeks, months, or years,
  // saving us a lot of complexity.
  //
  // Day algebra is straightforward because JS Date objects have no concept of
  // leap seconds.  We don't need to worry that a leap second will cause adding
  // a day to be less than or more than a day.  Hooray!
  //
  // == IMAP and Time
  //
  // The stock IMAP SEARCH command's SINCE and BEFORE predicates only operate on
  // whole-dates (and ignore the non-date time parts).  Additionally, SINCE is
  // inclusive and BEFORE is exclusive.
  //
  // We use JS millisecond timestamp values throughout, and it's important to us
  // that our date logic is consistent with IMAP's time logic where relevant.
  // All of our IMAP-exposed time-interval related logic operates on day
  // granularities.  Our timestamp/date values are always normalized to midnight
  // which happily works out with intuitive range operations.
  //
  // Observe the pretty ASCII art where as you move to the right you are moving
  // forward in time.
  //
  //             ________________________________________
  //      BEFORE)| midnight (0 millis) ... 11:59:59:999 |
  // ON_OR_BEFORE]
  //             [SINCE......................................
  //              (AFTER.....................................
  //
  // Our date range comparisons (noting that larger timestamps are 'younger') are:
  // SINCE analog:  (testDate >= comparisonDate)
  //   testDate is as-recent-as or more-recent-than the comparisonDate.
  // BEFORE analog: (testDate < comparisonDate)
  //   testDate is less-recent-than the comparisonDate
  //
  // Because "who is the test date and who is the range value under discussion"
  // can be unclear and the numerical direction of time is not always intuitive,
  // I'm introducing simple BEFORE and SINCE helper functions to try and make
  // the comparison logic ridiculously explicit as well as calling out where we
  // are being consistent with IMAP.
  //
  // Not all of our time logic is consistent with IMAP!  Specifically, use of
  // exclusive time bounds without secondary comparison keys means that ranges
  // defined in this way cannot spread messages with the same timestamp over
  // multiple ranges.  This allows for pathological data structure situations
  // where there's too much data in a data block, etc.
  // Our date ranges are defined by 'startTS' and 'endTS'.  Using math syntax, our
  // IMAP-consistent time ranges end up as: [startTS, endTS).  It is always true
  // that BEFORE(startTS, endTS) and SINCE(endTS, startTS) in these cases.
  //
  // As such, I've also created an ON_OR_BEFORE helper that allows equivalence and
  // STRICTLY_AFTER that does not check equivalence to round out all possibilities
  // while still being rather explicit.
  
  
  /**
   * IMAP-consistent date comparison; read this as "Is `testDate` BEFORE
   * `comparisonDate`"?
   *
   * !BEFORE(a, b) === SINCE(a, b)
   */
  var BEFORE = exports.BEFORE =
        function BEFORE(testDate, comparisonDate) {
    // testDate is numerically less than comparisonDate, so it is chronologically
    // before it.
    return testDate < comparisonDate;
  };
  
  var ON_OR_BEFORE = exports.ON_OR_BEFORE =
        function ON_OR_BEFORE(testDate, comparisonDate) {
    return testDate <= comparisonDate;
  };
  
  /**
   * IMAP-consistent date comparison; read this as "Is `testDate` SINCE
   * `comparisonDate`"?
   *
   * !SINCE(a, b) === BEFORE(a, b)
   */
  var SINCE = exports.SINCE =
        function SINCE(testDate, comparisonDate) {
    // testDate is numerically greater-than-or-equal-to comparisonDate, so it
    // chronologically after/since it.
    return testDate >= comparisonDate;
  };
  
  var STRICTLY_AFTER = exports.STRICTLY_AFTER =
        function STRICTLY_AFTER(testDate, comparisonDate) {
    return testDate > comparisonDate;
  };
  
  var IN_BS_DATE_RANGE = exports.IN_BS_DATE_RANGE =
        function IN_BS_DATE_RANGE(testDate, startTS, endTS) {
    return testDate >= startTS && testDate < endTS;
  };
  
  var PASTWARDS = 1, FUTUREWARDS = -1;
  /**
   * Check if `testDate` is "beyond" the comparison date given the `dir`.  If
   * the direction is pastwards, we will return true if testDate happened
   * chronologically before comparisonDate.  If the direction is futurewards,
   * we will return true if testDate happened chronologically after
   * comparisonDate.
   */
  var TIME_DIR_AT_OR_BEYOND = exports.TIME_DIR_AT_OR_BEYOND =
        function TIME_DIR_AT_OR_BEYOND(dir, testDate, comparisonDate) {
    if (dir === PASTWARDS)
      return testDate <= comparisonDate;
    // we use null as a sentinel value for 'the future'/'now'
    else if (comparisonDate === null)
      return testDate >= NOW();
    else // FUTUREWARDS
      return testDate >= comparisonDate;
  };
  /**
   * Compute the delta of the `testDate` relative to the `comparisonDate` where
   * a positive value indicates `testDate` is beyond the `comparisonDate` in
   * the given direction and a negative value indicates it is before it.
   */
  var TIME_DIR_DELTA = exports.TIME_DIR_DELTA =
        function TIME_DIR_DELTA(dir, testDate, comparisonDate) {
    if (dir === PASTWARDS)
      return testDate - comparisonDate;
    else // FUTUREWARDS
      return comparisonDate - testDate;
  };
  /**
   * Add `time` to the `baseDate` in the given direction.  So if the direction
   * is `PASTWARDS`, then we add the date, otherwise we subtract it.
   */
  var TIME_DIR_ADD = exports.TIME_DIR_ADD =
        function TIME_DIR_ADD(dir, baseDate, time) {
    if (dir === PASTWARDS)
      return baseDate + time;
    else // FUTUREWARDS
      return baseDate - time;
  };
  
  //function DATE_RANGES_OVERLAP(A_startTS, A_endTS, B_startTS, B_endTS) {
  //}
  
  var HOUR_MILLIS = exports.HOUR_MILLIS = 60 * 60 * 1000;
  var DAY_MILLIS = exports.DAY_MILLIS = 24 * 60 * 60 * 1000;
  
  /**
   * Testing override that when present replaces use of Date.now().
   */
  var TIME_WARPED_NOW = null;
  
  /**
   * Pretend that 'now' is actually a fixed point in time for the benefit of
   * unit tests using canned message stores.
   */
  exports.TEST_LetsDoTheTimewarpAgain = function(fakeNow) {
    if (fakeNow === null) {
      TIME_WARPED_NOW = null;
      return;
    }
    if (typeof(fakeNow) !== 'number')
      fakeNow = fakeNow.valueOf();
    TIME_WARPED_NOW = fakeNow;
  };
  
  var NOW = exports.NOW =
        function NOW() {
    return TIME_WARPED_NOW || Date.now();
  };
  
  /**
   * Like NOW, but uses performance.now instead. This means it can be reset or
   * changed to a value different than the last window or worker lifetime value.
   * Allows TIME_WARP overrides and falls back to Date.now. So while all the
   * values are in milliseconds, if performance.now() is used, can have a decimal
   * value indicating up to one thousandth of a millisecond.
   */
  var perfObj = typeof performance !== 'undefined' ? performance : Date;
  exports.PERFNOW = function PERFNOW() {
    return TIME_WARPED_NOW || perfObj.now();
  };
  
  /**
   * Make a timestamp some number of days in the past, quantized to midnight of
   * that day.  This results in rounding up; if it's noon right now and you
   * ask for 2 days ago, you really get 2.5 days worth of time.
   */
  var makeDaysAgo = exports.makeDaysAgo =
        function makeDaysAgo(numDays) {
    var past = quantizeDate(TIME_WARPED_NOW || Date.now()) -
                 numDays * DAY_MILLIS;
    return past;
  };
  var makeDaysBefore = exports.makeDaysBefore =
        function makeDaysBefore(date, numDaysBefore) {
    if (date === null)
      return makeDaysAgo(numDaysBefore - 1);
    return quantizeDate(date) - numDaysBefore * DAY_MILLIS;
  };
  /**
   * Quantize a date to midnight on that day.
   */
  var quantizeDate = exports.quantizeDate =
        function quantizeDate(date) {
    if (date === null)
      return null;
    if (typeof(date) === 'number')
      date = new Date(date);
    return date.setUTCHours(0, 0, 0, 0).valueOf();
  };
  
  /**
   * If a date is already lined up with midnight of its day, then return that,
   * otherwise round up to the midnight of the next day.
   */
  var quantizeDateUp = exports.quantizeDateUp =
        function quantizeDateUp(date) {
    if (typeof(date) === 'number')
      date = new Date(date);
    var truncated = date.setUTCHours(0, 0, 0, 0).valueOf();
    if (date.valueOf()  === truncated)
      return truncated;
    return truncated + DAY_MILLIS;
  };
  
  }); // end define
  