code: def get_timestamp_floor(ts, freq):
      """Returns the largest timestamp that is smaller than or equal to `ts`
      and is also a multiple of the `freq`.
      Assume hourly frequency i.e. `freq` = "H". Then
      If `ts` = 1:30, this function returns 1:00.
      If `ts` = 1:00, this function returns 1:00.
  
      Parameters
      ----------
      ts: `str`
          Timestamp in `str` format.
      freq: `str`
          Pandas timeseries frequency string.
  
      Returns
      -------
      dt_floor: `pd.Timestamp`
          The largest timestamp that is smaller than or equal to `ts`
          and is also a multiple of the `freq`.
      """
      dt_ceil = get_timestamp_ceil(ts, freq)
      # If input `ts` is not on the `freq` offset, `dt_ceil` > `ts`.
      # e.g. Assume `freq` = "H". If `ts` = 1:30, then `dt_ceil` = 2:00.
      # Then `dt_ceil` is reduced one `freq` offset to get `dt_floor`.
      if dt_ceil > pd.to_datetime(ts):
          return dt_ceil - pd.tseries.frequencies.to_offset(freq)
      else:
          return dt_ceil
  
file_path: greykite/greykite/detection/detector/ad_utils.py
imports: from functools import reduce
  import pandas
  from greykite.common.constants import ANOMALY_COL
  from greykite.common.constants import END_TIME_COL
  from greykite.common.constants import START_TIME_COL
  from greykite.common.constants import TIME_COL
  from greykite.common.features.outlier import ZScoreOutlierDetector
  from greykite.common.logging import LoggingLevelEnum
  from greykite.common.logging import log_message
  from greykite.detection.detector.constants import Z_SCORE_CUTOFF
label_desc: Use image node_id 9 for calling a global function with example usage: get_timestamp_floor(ts, freq) and returns: unknown, dt_ceil