diff --git a/HISTORY.rst b/HISTORY.rst
index 0bd3055e..eb7332a4 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -11,6 +11,7 @@ History
 * `ensembles.ensemble_percentiles` modified to compute along a `percentiles` dimension by default, instead of creating different variables.
 * Added indicator `first_day_below` and run length helper `first_run_after_date`.
 * Added ANUCLIM model climate indices mappings.
+* Split `checks.py` into `cfchecks.py`, `datachecks.py` and `missing.py`. This change will only affect users creating custom indices using utilities previously located in `checks.py`.
 
 0.17.0 (2020-05-15)
 -------------------
diff --git a/docs/checks.rst b/docs/checks.rst
index 4c5797ce..efc4ed5a 100644
--- a/docs/checks.rst
+++ b/docs/checks.rst
@@ -9,13 +9,13 @@ Missing values
 ==============
 The following functions can be used to mask missing values in :class:`Indicator` outputs.
 
-.. autofunction:: xclim.core.checks.missing_any
+.. autofunction:: xclim.core.missing.missing_any
    :noindex:
 
-.. autofunction:: xclim.core.checks.missing_pct
+.. autofunction:: xclim.core.missing.missing_pct
    :noindex:
-.. autofunction:: xclim.core.checks.missing_wmo
+.. autofunction:: xclim.core.missing.missing_wmo
    :noindex:
 
-.. autofunction:: xclim.core.checks.at_least_n_valid
+.. autofunction:: xclim.core.missing.at_least_n_valid
    :noindex:
diff --git a/docs/notebooks/options.ipynb b/docs/notebooks/options.ipynb
index 0cf164c1..0b82b54f 100644
--- a/docs/notebooks/options.ipynb
+++ b/docs/notebooks/options.ipynb
@@ -99,8 +99,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from xclim.core.checks import register_missing_method\n",
-    "from xclim.core.checks import MissingBase\n",
+    "from xclim.core.missing import register_missing_method\n",
+    "from xclim.core.missing import MissingBase\n",
     "from xclim.indices.run_length import longest_run\n",
     "\n",
     "@register_missing_method(\"consecutive\")\n",
diff --git a/xclim/core/cfchecks.py b/xclim/core/cfchecks.py
new file mode 100644
index 00000000..80645177
--- /dev/null
+++ b/xclim/core/cfchecks.py
@@ -0,0 +1,57 @@
+"""
+CF-Convention checks
+====================
+
+Utilities designed to verify the compliance of metadata with the CF-Convention.
+"""
+import fnmatch
+
+from .options import cfcheck
+from .utils import ValidationError
+
+# TODO: Implement pandas infer_freq in xarray with CFTimeIndex. >> PR pydata/xarray#4033
+
+
+@cfcheck
+def check_valid(var, key, expected):
+    r"""Check that a variable's attribute has the expected value. Warn user otherwise."""
+
+    att = getattr(var, key, None)
+    if att is None:
+        raise ValidationError(f"Variable does not have a `{key}` attribute.")
+    if not fnmatch.fnmatch(att, expected):
+        raise ValidationError(
+            f"Variable has a non-conforming {key}. Got `{att}`, expected `{expected}`",
+        )
+
+
+def check_valid_temperature(var, units):
+    r"""Check that variable is air temperature."""
+    check_valid(var, "standard_name", "air_temperature")
+    check_valid(var, "units", units)
+
+
+def check_valid_discharge(var):
+    r"""Check that the variable is a discharge."""
+    check_valid(var, "standard_name", "water_volume_transport_in_river_channel")
+    check_valid(var, "units", "m3 s-1")
+
+
+def check_valid_min_temperature(var, units="K"):
+    r"""Check that a variable is a valid daily minimum temperature."""
+
+    check_valid_temperature(var, units)
+    check_valid(var, "cell_methods", "time: minimum within days")
+
+
+def check_valid_mean_temperature(var, units="K"):
+    r"""Check that a variable is a valid daily mean temperature."""
+
+    check_valid_temperature(var, units)
+    check_valid(var, "cell_methods", "time: mean within days")
+
+
+def check_valid_max_temperature(var, units="K"):
+    r"""Check that a variable is a valid daily maximum temperature."""
+    check_valid_temperature(var, units)
+    check_valid(var, "cell_methods", "time: maximum within days")
diff --git a/xclim/core/checks.py b/xclim/core/checks.py
index a7da46db..3f8c4dcb 100644
--- a/xclim/core/checks.py
+++ b/xclim/core/checks.py
@@ -1,461 +1,4 @@
-# -*- coding: utf-8 -*-
-"""
-Health checks
-=============
-
-Functions performing basic health checks on xarray.DataArrays.
-"""
-import datetime as dt
-import fnmatch
-
-import numpy as np
-import pandas as pd
-import xarray as xr
-from boltons.funcutils import wraps
-
-from .options import cfcheck
-from .options import CHECK_MISSING
-from .options import datacheck
-from .options import MISSING_METHODS
-from .options import MISSING_OPTIONS
-from .options import OPTIONS
-from .options import register_missing_method
-from .utils import ValidationError
-
-# Dev notes
-# ---------
-#
-# I suggest we use `check` for weak checking, and `assert` for strong checking.
-# Weak checking would log problems in a log, while strong checking would raise an error.
-#
-# ANSWER to the above:
-#   The use of set_options with a switch for raising vs warning vs logging would render these terms useless.
-#
-# `functools.wraps` is used to copy the docstring and the function's original name from the source
-# function to the decorated function. This allows sphinx to correctly find and document functions.
-
-
-# TODO: Implement pandas infer_freq in xarray with CFTimeIndex. >> PR pydata/xarray#4033
-@cfcheck
-def check_valid(var, key, expected):
-    r"""Check that a variable's attribute has the expected value. Warn user otherwise."""
-
-    att = getattr(var, key, None)
-    if att is None:
-        raise ValidationError(f"Variable does not have a `{key}` attribute.")
-    if not fnmatch.fnmatch(att, expected):
-        raise ValidationError(
-            f"Variable has a non-conforming {key}. Got `{att}`, expected `{expected}`",
-        )
-
-
-@datacheck
-def check_daily(var):
-    r"""Assert that the series is daily and monotonic (no jumps in time index).
-
-    A ValueError is raised otherwise."""
-
-    t0, t1 = var.time[:2]
-
-    # This won't work for non-standard calendars. Needs to be implemented in xarray. Comment for now
-    if isinstance(t0.values, np.datetime64):
-        if pd.infer_freq(var.time.to_pandas()) != "D":
-            raise ValidationError("time series is not recognized as daily.")
-
-    # Check that the first time step is one day.
-    if np.timedelta64(dt.timedelta(days=1)) != (t1 - t0).data:
-        raise ValidationError("time series is not daily.")
-
-    # Check that the series does not go backward in time
-    if not var.time.to_pandas().is_monotonic_increasing:
-        raise ValidationError("time index is not monotonically increasing.")
-
-
-def check_valid_temperature(var, units):
-    r"""Check that variable is air temperature."""
-
-    check_valid(var, "standard_name", "air_temperature")
-    check_valid(var, "units", units)
-    check_daily(var)
-
-
-def check_valid_discharge(var):
-    r"""Check that the variable is a discharge."""
-    #
-    check_valid(var, "standard_name", "water_volume_transport_in_river_channel")
-    check_valid(var, "units", "m3 s-1")
-
-
-def valid_daily_min_temperature(comp, units="K"):
-    r"""Decorator to check that a computation runs on a valid temperature dataset."""
-
-    @wraps(comp)
-    def func(tasmin, *args, **kwds):
-        check_valid_temperature(tasmin, units)
-        check_valid(tasmin, "cell_methods", "time: minimum within days")
-        return comp(tasmin, **kwds)
-
-    return func
-
-
-def valid_daily_mean_temperature(comp, units="K"):
-    r"""Decorator to check that a computation runs on a valid temperature dataset."""
-
-    @wraps(comp)
-    def func(tas, *args, **kwds):
-        check_valid_temperature(tas, units)
-        check_valid(tas, "cell_methods", "time: mean within days")
-        return comp(tas, *args, **kwds)
-
-    return func
-
-
-def valid_daily_max_temperature(comp, units="K"):
-    r"""Decorator to check that a computation runs on a valid temperature dataset."""
-
-    @wraps(comp)
-    def func(tasmax, *args, **kwds):
-        check_valid_temperature(tasmax, units)
-        check_valid(tasmax, "cell_methods", "time: maximum within days")
-        return comp(tasmax, *args, **kwds)
-
-    return func
-
-
-def valid_daily_max_min_temperature(comp, units="K"):
-    r"""Decorator to check that a computation runs on valid min and max temperature datasets."""
-
-    @wraps(comp)
-    def func(tasmax, tasmin, **kwds):
-        valid_daily_max_temperature(tasmax, units)
-        valid_daily_min_temperature(tasmin, units)
-
-        return comp(tasmax, tasmin, **kwds)
-
-    return func
-
-
-def valid_daily_mean_discharge(comp):
-    r"""Decorator to check that a computation runs on valid discharge data."""
-
-    @wraps(comp)
-    def func(q, **kwds):
-        check_valid_discharge(q)
-        return comp(q, **kwds)
-
-    return func
-
-
-def valid_missing_data_threshold(comp, threshold=0):
-    r"""Check that the relative number of missing data points does not exceed a threshold."""
-    # TODO
-    raise NotImplementedError
-
-
-def check_is_dataarray(comp):
-    r"""Decorator to check that a computation has an instance of xarray.DataArray
-     as first argument."""
-
-    @wraps(comp)
-    def func(data_array, *args, **kwds):
-        assert isinstance(data_array, xr.DataArray)
-        return comp(data_array, *args, **kwds)
-
-    return func
-
-
-# This function can probably be made simpler once CFPeriodIndex is implemented.
-class MissingBase:
-    def __init__(self, da, freq, **indexer):
-        self.null, self.count = self.prepare(da, freq, **indexer)
-
-    @staticmethod
-    def split_freq(freq):
-        if freq is None:
-            return "", None
-
-        if "-" in freq:
-            return freq.split("-")
-
-        return freq, None
-
-    @staticmethod
-    def is_null(da, freq, **indexer):
-        """Return a boolean array indicating which values are null."""
-        from xclim.indices import generic
-
-        selected = generic.select_time(da, **indexer)
-        if selected.time.size == 0:
-            raise ValueError("No data for selected period.")
-
-        null = selected.isnull()
-        if freq:
-            return null.resample(time=freq)
-
-        return null
-
-    def prepare(self, da, freq, **indexer):
-        """Prepare arrays to be fed to the `is_missing` function.
-
-        Parameters
-        ----------
-        da : xr.DataArray
-          Input data.
-        freq : str
-          Resampling frequency defining the periods defined in
-          http://pandas.pydata.org/pandas-docs/stable/timeseries.html#resampling.
-        **indexer : {dim: indexer, }, optional
-          Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
-          values, month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given,
-          all values are considered.
-
-        Returns
-        -------
-        xr.DataArray, xr.DataArray
-          Boolean array indicating which values are null, array of expected number of valid values.
-
-        Notes
-        -----
-        If `freq=None` and an indexer is given, then missing values during period at the start or end of array won't be
-        flagged.
-        """
-        from xclim.indices import generic
-
-        null = self.is_null(da, freq, **indexer)
-
-        pfreq, anchor = self.split_freq(freq)
-
-        c = null.sum(dim="time")
-
-        # Otherwise simply use the start and end dates to find the expected number of days.
-        if pfreq.endswith("S"):
-            start_time = c.indexes["time"]
-            end_time = start_time.shift(1, freq=freq)
-        elif pfreq:
-            end_time = c.indexes["time"]
-            start_time = end_time.shift(-1, freq=freq)
-        else:
-            i = da.time.to_index()
-            start_time = i[:1]
-            end_time = i[-1:]
-
-        if indexer:
-            # Create a full synthetic time series and compare the number of days with the original series.
-            t0 = str(start_time[0].date())
-            t1 = str(end_time[-1].date())
-            if isinstance(da.indexes["time"], xr.CFTimeIndex):
-                cal = da.time.encoding.get("calendar")
-                t = xr.cftime_range(t0, t1, freq="D", calendar=cal)
-            else:
-                t = pd.date_range(t0, t1, freq="D")
-
-            sda = xr.DataArray(data=np.ones(len(t)), coords={"time": t}, dims=("time",))
-            st = generic.select_time(sda, **indexer)
-            if freq:
-                count = st.notnull().resample(time=freq).sum(dim="time")
-            else:
-                count = st.notnull().sum(dim="time")
-
-        else:
-            n = (end_time - start_time).days
-            if freq:
-                count = xr.DataArray(n.values, coords={"time": c.time}, dims="time")
-            else:
-                count = xr.DataArray(n.values[0] + 1)
-
-        return null, count
-
-    def is_missing(self, null, count, **kwargs):
-        """Return whether or not the values within each period should be considered missing or not."""
-        raise NotImplementedError
-
-    @staticmethod
-    def validate(**kwargs):
-        """Return whether or not arguments are valid."""
-        return True
-
-    def __call__(self, **kwargs):
-        if not self.validate(**kwargs):
-            raise ValueError("Invalid arguments")
-        return self.is_missing(self.null, self.count, **kwargs)
-
-
-@register_missing_method("any")
-class MissingAny(MissingBase):
-    def is_missing(self, null, count, **kwargs):
-        cond0 = null.count(dim="time") != count  # Check total number of days
-        cond1 = null.sum(dim="time") > 0  # Check if any is missing
-        return cond0 | cond1
-
-
-@register_missing_method("wmo")
-class MissingWMO(MissingAny):
-    def __init__(self, da, freq, **indexer):
-        # Force computation on monthly frequency
-        if not freq.startswith("M"):
-            raise ValueError
-        super().__init__(da, freq, **indexer)
-
-    def is_missing(self, null, count, nm=11, nc=5):
-        import xclim.indices.run_length as rl
-
-        # Check total number of days
-        cond0 = null.count(dim="time") != count
-
-        # Check if more than threshold is missing
-        cond1 = null.sum(dim="time") >= nm
-
-        # Check for consecutive missing values
-        cond2 = null.map(rl.longest_run, dim="time") >= nc
-
-        return cond0 | cond1 | cond2
-
-    @staticmethod
-    def validate(nm, nc):
-        return nm < 31 and nc < 31
-
-
-@register_missing_method("pct")
-class MissingPct(MissingBase):
-    def is_missing(self, null, count, tolerance=0.1):
-        if tolerance < 0 or tolerance > 1:
-            raise ValueError("tolerance should be between 0 and 1.")
-
-        n = count - null.count(dim="time") + null.sum(dim="time")
-        return n / count >= tolerance
-
-    @staticmethod
-    def validate(tolerance):
-        return 0 <= tolerance <= 1
-
-
-@register_missing_method("at_least_n")
-class AtLeastNValid(MissingBase):
-    def is_missing(self, null, count, n=20):
-        """The result of a reduction operation is considered missing if less than `n` values are valid."""
-        nvalid = null.count(dim="time") - null.sum(dim="time")
-        return nvalid < n
-
-    @staticmethod
-    def validate(n):
-        return n > 0
-
-
-def missing_any(da, freq, **indexer):
-    r"""Return whether there are missing days in the array.
-
-    Parameters
-    ----------
-    da : DataArray
-      Input array at daily frequency.
-    freq : str
-      Resampling frequency.
-    **indexer : {dim: indexer, }, optional
-      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter values,
-      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
-      considered.
-
-    Returns
-    -------
-    out : DataArray
-      A boolean array set to True if period has missing values.
-    """
-    return MissingAny(da, freq, **indexer)()
-
-
-def missing_wmo(da, freq, nm=11, nc=5, **indexer):
-    r"""Return whether a series fails WMO criteria for missing days.
-
-    The World Meteorological Organisation recommends that where monthly means are computed from daily values,
-    it should considered missing if either of these two criteria are met:
-
-      – observations are missing for 11 or more days during the month;
-      – observations are missing for a period of 5 or more consecutive days during the month.
-
-    Stricter criteria are sometimes used in practice, with a tolerance of 5 missing values or 3 consecutive missing
-    values.
-
-    Parameters
-    ----------
-    da : DataArray
-      Input array at daily frequency.
-    freq : str
-      Resampling frequency.
-    nm : int
-      Number of missing values per month that should not be exceeded.
-    nc : int
-      Number of consecutive missing values per month that should not be exceeded.
-    **indexer : {dim: indexer, }, optional
-      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter values,
-      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
-      considered.
-
-    Returns
-    -------
-    out : DataArray
-      A boolean array set to True if period has missing values.
-    """
-    missing = MissingWMO(da, "M", **indexer)(nm=nm, nc=nc)
-    return missing.resample(time=freq).any()
-
-
-def missing_pct(da, freq, tolerance, **indexer):
-    r"""Return whether there are more missing days in the array than a given percentage.
-
-    Parameters
-    ----------
-    da : DataArray
-      Input array at daily frequency.
-    freq : str
-      Resampling frequency.
-    tolerance : float
-      Fraction of missing values that is tolerated.
-    **indexer : {dim: indexer, }, optional
-      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
-      values,
-      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
-      considered.
-
-    Returns
-    -------
-    out : DataArray
-      A boolean array set to True if period has missing values.
-    """
-    return MissingPct(da, freq, **indexer)(tolerance=tolerance)
-
-
-def at_least_n_valid(da, freq, n=1, **indexer):
-    r"""Return whether there are at least a given number of valid values.
-
-        Parameters
-        ----------
-        da : DataArray
-          Input array at daily frequency.
-        freq : str
-          Resampling frequency.
-        n : int
-          Minimum of valid values required.
-        **indexer : {dim: indexer, }, optional
-          Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
-          values, month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given,
-          all values are considered.
-
-        Returns
-        -------
-        out : DataArray
-          A boolean array set to True if period has missing values.
-        """
-    return AtLeastNValid(da, freq, **indexer)(n=n)
-
-
-def missing_from_context(da, freq, **indexer):
-    """Return whether each element of the resampled da should be considered missing according
-    to the currently set options in `xclim.set_options`.
-
-    See `xclim.set_options` and `xclim.core.options.register_missing_method`.
-    """
-    name = OPTIONS[CHECK_MISSING]
-    cls = MISSING_METHODS[name]
-    opts = OPTIONS[MISSING_OPTIONS][name]
-
-    return cls(da, freq, **indexer)(**opts)
+# This file is kept for backward compatibility. It will be removed in a later version.
+from .cfchecks import *
+from .datachecks import *
+from .missing import *
diff --git a/xclim/core/datachecks.py b/xclim/core/datachecks.py
new file mode 100644
index 00000000..0da55888
--- /dev/null
+++ b/xclim/core/datachecks.py
@@ -0,0 +1,35 @@
+"""
+Data checks
+===========
+
+Utilities designed to check the validity of data inputs.
+"""
+import datetime as dt
+
+import numpy as np
+import pandas as pd
+
+from .options import datacheck
+from .utils import ValidationError
+
+
+@datacheck
+def check_daily(var):
+    r"""Assert that the series is daily and monotonic (no jumps in time index).
+
+    A ValueError is raised otherwise."""
+
+    t0, t1 = var.time[:2]
+
+    # This won't work for non-standard calendars. Needs to be implemented in xarray. Comment for now
+    if isinstance(t0.values, np.datetime64):
+        if pd.infer_freq(var.time.to_pandas()) != "D":
+            raise ValidationError("time series is not recognized as daily.")
+
+    # Check that the first time step is one day.
+    if np.timedelta64(dt.timedelta(days=1)) != (t1 - t0).data:
+        raise ValidationError("time series is not daily.")
+
+    # Check that the series does not go backward in time
+    if not var.time.to_pandas().is_monotonic_increasing:
+        raise ValidationError("time index is not monotonically increasing.")
diff --git a/xclim/core/formatting.py b/xclim/core/formatting.py
index ccdcae9d..3f32130b 100644
--- a/xclim/core/formatting.py
+++ b/xclim/core/formatting.py
@@ -139,24 +139,23 @@ def merge_attributes(
     Parameters
     ----------
     attribute : str
-        The attribute to merge.
-    *inputs_list : Union[xr.DataArray, xr.Dataset]
-        The datasets or variables that were used to produce the new object.
-        Inputs given that way will be prefixed by their "name" attribute if available.
+      The attribute to merge.
+    inputs_list : Union[xr.DataArray, xr.Dataset]
+      The datasets or variables that were used to produce the new object. Inputs given that way will be prefixed by their `name` attribute if available.
     new_line : str
-        The character to put between each instance of the attributes. Usually, in CF-conventions,
-        the history attributes uses "\n" while cell_methods uses " ".
+      The character to put between each instance of the attributes. Usually, in CF-conventions,
+      the history attributes uses "\n" while cell_methods uses " ".
     missing_str : str
-        A string that is printed if an input doesn't have the attribute. Defaults to None, in which
-        case the input is simply skipped.
-    **inputs_kws : Union[xr.DataArray, xr.Dataset]
-        Mapping from names to the datasets or variables that were used to produce the new object.
-        Inputs given that way will be prefixes by the passed name.
+      A string that is printed if an input doesn't have the attribute. Defaults to None, in which
+      case the input is simply skipped.
+    inputs_kws : Union[xr.DataArray, xr.Dataset]
+      Mapping from names to the datasets or variables that were used to produce the new object.
+      Inputs given that way will be prefixes by the passed name.
 
     Returns
     -------
     str
-        The new attribute made from the combination of the ones from all the inputs.
+      The new attribute made from the combination of the ones from all the inputs.
     """
     inputs = []
     for in_ds in inputs_list:
@@ -191,20 +190,20 @@ def update_history(
     Parameters
     ----------
     hist_str : str
-        The string describing what has been done on the data.
+      The string describing what has been done on the data.
     new_name : Optional[str]
-        The name of the newly created variable or dataset to prefix hist_msg.
+      The name of the newly created variable or dataset to prefix hist_msg.
     *inputs_list : Union[xr.DataArray, xr.Dataset]
-        The datasets or variables that were used to produce the new object.
-        Inputs given that way will be prefixed by their "name" attribute if available.
+      The datasets or variables that were used to produce the new object.
+      Inputs given that way will be prefixed by their "name" attribute if available.
     **inputs_kws : Union[xr.DataArray, xr.Dataset]
-        Mapping from names to the datasets or variables that were used to produce the new object.
-        Inputs given that way will be prefixes by the passed name.
+      Mapping from names to the datasets or variables that were used to produce the new object.
+      Inputs given that way will be prefixes by the passed name.
 
     Returns
     -------
     str
-        The combine history of all inputs starting with `hist_str`.
+      The combine history of all inputs starting with `hist_str`.
 
     See also
     --------
diff --git a/xclim/core/indicator.py b/xclim/core/indicator.py
index 9c7ebc81..cde96441 100644
--- a/xclim/core/indicator.py
+++ b/xclim/core/indicator.py
@@ -15,8 +15,7 @@ from typing import Union
 import numpy as np
 from boltons.funcutils import wraps
 
-from .checks import check_daily
-from .checks import missing_from_context
+from .datachecks import check_daily
 from .formatting import AttrFormatter
 from .formatting import default_formatter
 from .formatting import merge_attributes
@@ -24,6 +23,7 @@ from .formatting import parse_doc
 from .formatting import update_history
 from .locales import get_local_attrs
 from .locales import get_local_formatter
+from .missing import missing_from_context
 from .options import OPTIONS
 from .units import convert_units_to
 from .units import units
diff --git a/xclim/core/missing.py b/xclim/core/missing.py
new file mode 100644
index 00000000..d758e168
--- /dev/null
+++ b/xclim/core/missing.py
@@ -0,0 +1,339 @@
+"""
+Missing values identification
+=============================
+
+Algorithms identifying missing values using different criteria:
+
+- missing_any: A result is missing if any input value is missing.
+- missing_wmo: A result is missing if 11 days are missing, or 5 consecutive values are missing in a month.
+- missing_pct: A result is missing if more than a given fraction of values are missing.
+- at_least_n_valid: A result is missing if less than a given number of valid values are present.
+
+"""
+import numpy as np
+import pandas as pd
+import xarray as xr
+from boltons.funcutils import wraps
+
+from .options import CHECK_MISSING
+from .options import MISSING_METHODS
+from .options import MISSING_OPTIONS
+from .options import OPTIONS
+from .options import register_missing_method
+
+__all__ = [
+    "missing_wmo",
+    "missing_any",
+    "missing_pct",
+    "missing_from_context",
+    "register_missing_method",
+]
+
+
+def check_is_dataarray(comp):
+    r"""Decorator to check that a computation has an instance of xarray.DataArray
+     as first argument."""
+
+    @wraps(comp)
+    def func(data_array, *args, **kwds):
+        assert isinstance(data_array, xr.DataArray)
+        return comp(data_array, *args, **kwds)
+
+    return func
+
+
+# This function can probably be made simpler once CFPeriodIndex is implemented.
+class MissingBase:
+    def __init__(self, da, freq, **indexer):
+        self.null, self.count = self.prepare(da, freq, **indexer)
+
+    @staticmethod
+    def split_freq(freq):
+        if freq is None:
+            return "", None
+
+        if "-" in freq:
+            return freq.split("-")
+
+        return freq, None
+
+    @staticmethod
+    def is_null(da, freq, **indexer):
+        """Return a boolean array indicating which values are null."""
+        from xclim.indices import generic
+
+        selected = generic.select_time(da, **indexer)
+        if selected.time.size == 0:
+            raise ValueError("No data for selected period.")
+
+        null = selected.isnull()
+        if freq:
+            return null.resample(time=freq)
+
+        return null
+
+    def prepare(self, da, freq, **indexer):
+        """Prepare arrays to be fed to the `is_missing` function.
+
+        Parameters
+        ----------
+        da : xr.DataArray
+          Input data.
+        freq : str
+          Resampling frequency defining the periods defined in
+          http://pandas.pydata.org/pandas-docs/stable/timeseries.html#resampling.
+        **indexer : {dim: indexer, }, optional
+          Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
+          values, month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given,
+          all values are considered.
+
+        Returns
+        -------
+        xr.DataArray, xr.DataArray
+          Boolean array indicating which values are null, array of expected number of valid values.
+
+        Notes
+        -----
+        If `freq=None` and an indexer is given, then missing values during period at the start or end of array won't be
+        flagged.
+        """
+        from xclim.indices import generic
+
+        null = self.is_null(da, freq, **indexer)
+
+        pfreq, anchor = self.split_freq(freq)
+
+        c = null.sum(dim="time")
+
+        # Otherwise simply use the start and end dates to find the expected number of days.
+        if pfreq.endswith("S"):
+            start_time = c.indexes["time"]
+            end_time = start_time.shift(1, freq=freq)
+        elif pfreq:
+            end_time = c.indexes["time"]
+            start_time = end_time.shift(-1, freq=freq)
+        else:
+            i = da.time.to_index()
+            start_time = i[:1]
+            end_time = i[-1:]
+
+        if indexer:
+            # Create a full synthetic time series and compare the number of days with the original series.
+            t0 = str(start_time[0].date())
+            t1 = str(end_time[-1].date())
+            if isinstance(da.indexes["time"], xr.CFTimeIndex):
+                cal = da.time.encoding.get("calendar")
+                t = xr.cftime_range(t0, t1, freq="D", calendar=cal)
+            else:
+                t = pd.date_range(t0, t1, freq="D")
+
+            sda = xr.DataArray(data=np.ones(len(t)), coords={"time": t}, dims=("time",))
+            st = generic.select_time(sda, **indexer)
+            if freq:
+                count = st.notnull().resample(time=freq).sum(dim="time")
+            else:
+                count = st.notnull().sum(dim="time")
+
+        else:
+            n = (end_time - start_time).days
+            if freq:
+                count = xr.DataArray(n.values, coords={"time": c.time}, dims="time")
+            else:
+                count = xr.DataArray(n.values[0] + 1)
+
+        return null, count
+
+    def is_missing(self, null, count, **kwargs):
+        """Return whether or not the values within each period should be considered missing or not."""
+        raise NotImplementedError
+
+    @staticmethod
+    def validate(**kwargs):
+        """Return whether or not arguments are valid."""
+        return True
+
+    def __call__(self, **kwargs):
+        if not self.validate(**kwargs):
+            raise ValueError("Invalid arguments")
+        return self.is_missing(self.null, self.count, **kwargs)
+
+
+@register_missing_method("any")
+class MissingAny(MissingBase):
+    def is_missing(self, null, count, **kwargs):
+        cond0 = null.count(dim="time") != count  # Check total number of days
+        cond1 = null.sum(dim="time") > 0  # Check if any is missing
+        return cond0 | cond1
+
+
+@register_missing_method("wmo")
+class MissingWMO(MissingAny):
+    def __init__(self, da, freq, **indexer):
+        # Force computation on monthly frequency
+        if not freq.startswith("M"):
+            raise ValueError
+        super().__init__(da, freq, **indexer)
+
+    def is_missing(self, null, count, nm=11, nc=5):
+        import xclim.indices.run_length as rl
+
+        # Check total number of days
+        cond0 = null.count(dim="time") != count
+
+        # Check if more than threshold is missing
+        cond1 = null.sum(dim="time") >= nm
+
+        # Check for consecutive missing values
+        cond2 = null.map(rl.longest_run, dim="time") >= nc
+
+        return cond0 | cond1 | cond2
+
+    @staticmethod
+    def validate(nm, nc):
+        return nm < 31 and nc < 31
+
+
+@register_missing_method("pct")
+class MissingPct(MissingBase):
+    def is_missing(self, null, count, tolerance=0.1):
+        if tolerance < 0 or tolerance > 1:
+            raise ValueError("tolerance should be between 0 and 1.")
+
+        n = count - null.count(dim="time") + null.sum(dim="time")
+        return n / count >= tolerance
+
+    @staticmethod
+    def validate(tolerance):
+        return 0 <= tolerance <= 1
+
+
+@register_missing_method("at_least_n")
+class AtLeastNValid(MissingBase):
+    def is_missing(self, null, count, n=20):
+        """The result of a reduction operation is considered missing if less than `n` values are valid."""
+        nvalid = null.count(dim="time") - null.sum(dim="time")
+        return nvalid < n
+
+    @staticmethod
+    def validate(n):
+        return n > 0
+
+
+def missing_any(da, freq, **indexer):
+    r"""Return whether there are missing days in the array.
+
+    Parameters
+    ----------
+    da : DataArray
+      Input array at daily frequency.
+    freq : str
+      Resampling frequency.
+    **indexer : {dim: indexer, }, optional
+      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter values,
+      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
+      considered.
+
+    Returns
+    -------
+    out : DataArray
+      A boolean array set to True if period has missing values.
+    """
+    return MissingAny(da, freq, **indexer)()
+
+
+def missing_wmo(da, freq, nm=11, nc=5, **indexer):
+    r"""Return whether a series fails WMO criteria for missing days.
+
+    The World Meteorological Organisation recommends that where monthly means are computed from daily values,
+    it should considered missing if either of these two criteria are met:
+
+      – observations are missing for 11 or more days during the month;
+      – observations are missing for a period of 5 or more consecutive days during the month.
+
+    Stricter criteria are sometimes used in practice, with a tolerance of 5 missing values or 3 consecutive missing
+    values.
+
+    Parameters
+    ----------
+    da : DataArray
+      Input array at daily frequency.
+    freq : str
+      Resampling frequency.
+    nm : int
+      Number of missing values per month that should not be exceeded.
+    nc : int
+      Number of consecutive missing values per month that should not be exceeded.
+    **indexer : {dim: indexer, }, optional
+      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter values,
+      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
+      considered.
+
+    Returns
+    -------
+    out : DataArray
+      A boolean array set to True if period has missing values.
+    """
+    missing = MissingWMO(da, "M", **indexer)(nm=nm, nc=nc)
+    return missing.resample(time=freq).any()
+
+
+def missing_pct(da, freq, tolerance, **indexer):
+    r"""Return whether there are more missing days in the array than a given percentage.
+
+    Parameters
+    ----------
+    da : DataArray
+      Input array at daily frequency.
+    freq : str
+      Resampling frequency.
+    tolerance : float
+      Fraction of missing values that is tolerated.
+    **indexer : {dim: indexer, }, optional
+      Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
+      values,
+      month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given, all values are
+      considered.
+
+    Returns
+    -------
+    out : DataArray
+      A boolean array set to True if period has missing values.
+    """
+    return MissingPct(da, freq, **indexer)(tolerance=tolerance)
+
+
+def at_least_n_valid(da, freq, n=1, **indexer):
+    r"""Return whether there are at least a given number of valid values.
+
+        Parameters
+        ----------
+        da : DataArray
+          Input array at daily frequency.
+        freq : str
+          Resampling frequency.
+        n : int
+          Minimum of valid values required.
+        **indexer : {dim: indexer, }, optional
+          Time attribute and values over which to subset the array. For example, use season='DJF' to select winter
+          values, month=1 to select January, or month=[6,7,8] to select summer months. If not indexer is given,
+          all values are considered.
+
+        Returns
+        -------
+        out : DataArray
+          A boolean array set to True if period has missing values.
+        """
+    return AtLeastNValid(da, freq, **indexer)(n=n)
+
+
+def missing_from_context(da, freq, **indexer):
+    """Return whether each element of the resampled da should be considered missing according
+    to the currently set options in `xclim.set_options`.
+
+    See `xclim.set_options` and `xclim.core.options.register_missing_method`.
+    """
+    name = OPTIONS[CHECK_MISSING]
+    cls = MISSING_METHODS[name]
+    opts = OPTIONS[MISSING_OPTIONS][name]
+
+    return cls(da, freq, **indexer)(**opts)
diff --git a/xclim/indicators/atmos/_temperature.py b/xclim/indicators/atmos/_temperature.py
index aeefbf9f..a37e2b81 100644
--- a/xclim/indicators/atmos/_temperature.py
+++ b/xclim/indicators/atmos/_temperature.py
@@ -2,7 +2,7 @@
 import abc
 
 from xclim import indices
-from xclim.core import checks
+from xclim.core import cfchecks
 from xclim.core.indicator import Indicator
 from xclim.core.indicator import Indicator2D
 from xclim.core.units import check_units
@@ -60,8 +60,8 @@ class Tas(Indicator):
     """Class for univariate indices using mean daily temperature as the input."""
 
     def cfprobe(self, tas):
-        checks.check_valid(tas, "cell_methods", "**time: mean within days*")
-        checks.check_valid(tas, "standard_name", "air_temperature")
+        cfchecks.check_valid(tas, "cell_methods", "**time: mean within days*")
+        cfchecks.check_valid(tas, "standard_name", "air_temperature")
 
     @abc.abstractmethod
     def compute(*args, **kwds):
@@ -72,8 +72,8 @@ class Tasmin(Indicator):
     """Class for univariate indices using min daily temperature as the input."""
 
     def cfprobe(self, tasmin):
-        checks.check_valid(tasmin, "cell_methods", "*time: minimum within days*")
-        checks.check_valid(tasmin, "standard_name", "air_temperature")
+        cfchecks.check_valid(tasmin, "cell_methods", "*time: minimum within days*")
+        cfchecks.check_valid(tasmin, "standard_name", "air_temperature")
 
     @abc.abstractmethod
     def compute(*args, **kwds):
@@ -84,8 +84,8 @@ class Tasmax(Indicator):
     """Class for univariate indices using max daily temperature as the input."""
 
     def cfprobe(self, tasmax):
-        checks.check_valid(tasmax, "cell_methods", "*time: maximum within days*")
-        checks.check_valid(tasmax, "standard_name", "air_temperature")
+        cfchecks.check_valid(tasmax, "cell_methods", "*time: maximum within days*")
+        cfchecks.check_valid(tasmax, "standard_name", "air_temperature")
 
     @abc.abstractmethod
     def compute(*args, **kwds):
@@ -95,9 +95,9 @@ class Tasmax(Indicator):
 class TasminTasmax(Indicator2D):
     def cfprobe(self, tasmin, tasmax):
         for da in (tasmin, tasmax):
-            checks.check_valid(da, "standard_name", "air_temperature")
-        checks.check_valid(tasmin, "cell_methods", "*time: minimum within days*")
-        checks.check_valid(tasmax, "cell_methods", "*time: maximum within days*")
+            cfchecks.check_valid(da, "standard_name", "air_temperature")
+        cfchecks.check_valid(tasmin, "cell_methods", "*time: minimum within days*")
+        cfchecks.check_valid(tasmax, "cell_methods", "*time: maximum within days*")
         check_units(tasmax, tasmin.attrs["units"])
 
     @abc.abstractmethod
diff --git a/xclim/indicators/land/_streamflow.py b/xclim/indicators/land/_streamflow.py
index c3ee6454..1c11a7ed 100644
--- a/xclim/indicators/land/_streamflow.py
+++ b/xclim/indicators/land/_streamflow.py
@@ -1,14 +1,13 @@
 # -*- coding: utf-8 -*-
 import numpy as np
 
-from xclim.core import checks
+from xclim.core import cfchecks
+from xclim.core import missing
 from xclim.core.indicator import Indicator
 from xclim.core.utils import wrapped_partial
 from xclim.indices import base_flow_index
 from xclim.indices import generic
 
-# from boltons.funcutils import FunctionBuilder
-# import calendar
 
 __all__ = ["base_flow_index", "freq_analysis", "stats", "fit", "doy_qmax", "doy_qmin"]
 
@@ -23,7 +22,7 @@ class Streamflow(Indicator):
         pass
 
     def cfprobe(self, q):
-        checks.check_valid(q, "standard_name", "streamflow")
+        cfchecks.check_valid(q, "standard_name", "streamflow")
 
 
 class Stats(Streamflow):
@@ -34,7 +33,7 @@ class Stats(Streamflow):
         indexer = kwds["indexer"]
         freq = kwds["freq"] or generic.default_freq(**indexer)
 
-        miss = (checks.missing_any(da, freq, **indexer) for da in args)
+        miss = (missing.missing_any(da, freq, **indexer) for da in args)
         return reduce(np.logical_or, miss)
 
 
diff --git a/xclim/indicators/seaIce/_seaice.py b/xclim/indicators/seaIce/_seaice.py
index 3ae486d5..f4623be8 100644
--- a/xclim/indicators/seaIce/_seaice.py
+++ b/xclim/indicators/seaIce/_seaice.py
@@ -5,7 +5,7 @@ Sea ice indicators
 import abc
 
 from xclim import indices
-from xclim.core import checks
+from xclim.core import cfchecks
 from xclim.core.indicator import Indicator2D
 
 __all__ = ["sea_ice_area", "sea_ice_extent"]
@@ -15,7 +15,7 @@ class SicArea(Indicator2D):
     """Class for indicators having sea ice concentration and grid cell area inputs."""
 
     def cfprobe(self, sic, area):
-        checks.check_valid(sic, "standard_name", "sea_ice_area_fraction")
+        cfchecks.check_valid(sic, "standard_name", "sea_ice_area_fraction")
 
     @abc.abstractmethod
     def compute(*args, **kwds):

Split cfchecks, datachecks and missing functions into their own module. 
# Description
<!--Describe what you were trying to get done.
Tell us what happened, what went wrong, and what you expected to happen.-->

As part of the refactoring effort for a 1.0 release, I suggest we clearly split cfchecks, datachecks and missing functions into their own module
diff --git a/tests/test_checks.py b/tests/test_checks.py
index 2977dd15..1e21fa31 100644
--- a/tests/test_checks.py
+++ b/tests/test_checks.py
@@ -8,7 +8,8 @@ import pytest
 import xarray as xr
 
 from xclim import set_options
-from xclim.core import checks
+from xclim.core import cfchecks
+from xclim.core import datachecks
 from xclim.core.utils import ValidationError
 from xclim.indicators.atmos import tg_mean
 
@@ -25,7 +26,7 @@ TestObj = namedtuple("TestObj", ["test"])
 )
 def test_check_valid_ok(value, expected):
     d = TestObj(value)
-    checks.check_valid(d, "test", expected)
+    cfchecks.check_valid(d, "test", expected)
 
 
 @pytest.mark.parametrize(
@@ -34,7 +35,7 @@ def test_check_valid_ok(value, expected):
 def test_check_valid_raise(value, expected):
     d = TestObj(value)
     with pytest.raises(ValidationError):
-        checks.check_valid(d, "test", expected)
+        cfchecks.check_valid(d, "test", expected)
 
 
 class TestDateHandling:
@@ -83,7 +84,7 @@ def test_cf_compliance_options(tas_series, caplog):
 
     with caplog.at_level(logging.INFO):
         with set_options(cf_compliance="log"):
-            checks.check_valid_temperature(tas, "degK")
+            cfchecks.check_valid_temperature(tas, "degK")
 
             assert all([rec.levelname == "INFO" for rec in caplog.records])
             assert (
@@ -93,154 +94,10 @@ def test_cf_compliance_options(tas_series, caplog):
 
     with pytest.warns(UserWarning, match="Variable has a non-conforming"):
         with set_options(cf_compliance="warn"):
-            checks.check_valid_temperature(tas, "degK")
+            cfchecks.check_valid_temperature(tas, "degK")
 
     with pytest.raises(
         ValidationError, match="Variable has a non-conforming standard_name"
     ):
         with set_options(cf_compliance="raise"):
-            checks.check_valid_temperature(tas, "degK")
-
-
-class TestMissingAnyFills:
-    def test_missing_days(self, tas_series):
-        a = np.arange(360.0)
-        a[5:10] = np.nan
-        ts = tas_series(a)
-        out = checks.missing_any(ts, freq="MS")
-        assert out[0]
-        assert not out[1]
-
-    def test_missing_months(self):
-        n = 66
-        times = pd.date_range("2001-12-30", freq="1D", periods=n)
-        da = xr.DataArray(np.arange(n), [("time", times)])
-        miss = checks.missing_any(da, "MS")
-        np.testing.assert_array_equal(miss, [True, False, False, True])
-
-    def test_missing_years(self):
-        n = 378
-        times = pd.date_range("2001-12-31", freq="1D", periods=n)
-        da = xr.DataArray(np.arange(n), [("time", times)])
-        miss = checks.missing_any(da, "YS")
-        np.testing.assert_array_equal(miss, [True, False, True])
-
-    def test_missing_season(self):
-        n = 378
-        times = pd.date_range("2001-12-31", freq="1D", periods=n)
-        da = xr.DataArray(np.arange(n), [("time", times)])
-        miss = checks.missing_any(da, "Q-NOV")
-        np.testing.assert_array_equal(miss, [True, False, False, False, True])
-
-    def test_to_period_start(self, tasmin_series):
-        a = np.zeros(365) + K2C + 5.0
-        a[2] -= 20
-        ts = tasmin_series(a)
-        miss = checks.missing_any(ts, freq="AS-JUL")
-        np.testing.assert_equal(miss, [False])
-
-    def test_to_period_end(self, tasmin_series):
-        a = np.zeros(365) + K2C + 5.0
-        a[2] -= 20
-        ts = tasmin_series(a)
-        miss = checks.missing_any(ts, freq="A-JUN")
-        np.testing.assert_equal(miss, [False])
-
-    def test_month(self, tasmin_series):
-        ts = tasmin_series(np.zeros(36))
-        miss = checks.missing_any(ts, freq="YS", month=7)
-        np.testing.assert_equal(miss, [False])
-
-        miss = checks.missing_any(ts, freq="YS", month=8)
-        np.testing.assert_equal(miss, [True])
-
-        with pytest.raises(ValueError, match=r"No data for selected period."):
-            checks.missing_any(ts, freq="YS", month=1)
-
-        miss = checks.missing_any(ts, freq="YS", month=[7, 8])
-        np.testing.assert_equal(miss, [True])
-
-        ts = tasmin_series(np.zeros(76))
-        miss = checks.missing_any(ts, freq="YS", month=[7, 8])
-        np.testing.assert_equal(miss, [False])
-
-    def test_season(self, tasmin_series):
-        ts = tasmin_series(np.zeros(360))
-        miss = checks.missing_any(ts, freq="YS", season="MAM")
-        np.testing.assert_equal(miss, [False])
-
-        miss = checks.missing_any(ts, freq="YS", season="JJA")
-        np.testing.assert_array_equal(miss, [True, True])
-
-        miss = checks.missing_any(ts, freq="YS", season="SON")
-        np.testing.assert_equal(miss, [False])
-
-    def test_no_freq(self, tasmin_series):
-        ts = tasmin_series(np.zeros(360))
-
-        miss = checks.missing_any(ts, freq=None)
-        np.testing.assert_array_equal(miss, False)
-
-        t = list(range(31))
-        t.pop(5)
-        ts2 = ts.isel(time=t)
-        miss = checks.missing_any(ts2, freq=None)
-        np.testing.assert_array_equal(miss, True)
-
-        # With indexer
-        miss = checks.missing_any(ts, freq=None, month=[7])
-        np.testing.assert_array_equal(miss, False)
-
-        miss = checks.missing_any(ts2, freq=None, month=[7])
-        np.testing.assert_array_equal(miss, True)
-
-    def test_hydro(self):
-        fn = Path(TESTS_DATA, "Raven", "q_sim.nc")
-        ds = xr.open_dataset(fn)
-        miss = checks.missing_any(ds.q_sim, freq="YS")
-        np.testing.assert_array_equal(miss[:-1], False)
-        np.testing.assert_array_equal(miss[-1], True)
-
-        miss = checks.missing_any(ds.q_sim, freq="YS", season="JJA")
-        np.testing.assert_array_equal(miss, False)
-
-
-class TestMissingWMO:
-    def test_missing_days(self, tas_series):
-        a = np.arange(360.0)
-        a[5:7] = np.nan  # Number of missing values under acceptable limit in a month
-        a[40:45] = np.nan  # Too many consecutive missing values
-        a[70:92:2] = np.nan  # Too many non-consecutive missing values
-        ts = tas_series(a)
-        out = checks.missing_wmo(ts, freq="MS")
-        assert not out[0]
-        assert out[1]
-        assert out[2]
-
-    def test_missing_days_in_quarter(self, tas_series):
-        a = np.arange(360.0)
-        a[5:16] = np.nan  # Number of missing values under acceptable limit in a month
-        ts = tas_series(a)
-        out = checks.missing_wmo(ts, freq="QS-JAN")
-        np.testing.assert_array_equal(out, [True, False, False, True])
-
-
-class TestMissingPct:
-    def test_missing_days(self, tas_series):
-        a = np.arange(360.0)
-        a[5:7] = np.nan  # Number of missing values under acceptable limit in a month
-        a[40:45] = np.nan  # Too many missing values
-        ts = tas_series(a)
-        out = checks.missing_pct(ts, freq="MS", tolerance=0.1)
-        assert not out[0]
-        assert out[1]
-
-
-class TestAtLeastNValid:
-    def test_at_least_n_valid(self, tas_series):
-        a = np.arange(360.0)
-        a[5:10] = np.nan  # Number of missing values under acceptable limit in a month
-        a[40:55] = np.nan  # Too many missing values
-        ts = tas_series(a)
-        out = checks.at_least_n_valid(ts, freq="MS", n=20)
-        np.testing.assert_array_equal(out[:2], [False, True])
+            cfchecks.check_valid_temperature(tas, "degK")
diff --git a/tests/test_missing.py b/tests/test_missing.py
new file mode 100644
index 00000000..836efd6d
--- /dev/null
+++ b/tests/test_missing.py
@@ -0,0 +1,157 @@
+from pathlib import Path
+
+import numpy as np
+import pandas as pd
+import pytest
+import xarray as xr
+
+from xclim.core import missing
+
+K2C = 273.15
+
+TESTS_HOME = Path(__file__).absolute().parent
+TESTS_DATA = Path(TESTS_HOME, "testdata")
+
+
+class TestMissingAnyFills:
+    def test_missing_days(self, tas_series):
+        a = np.arange(360.0)
+        a[5:10] = np.nan
+        ts = tas_series(a)
+        out = missing.missing_any(ts, freq="MS")
+        assert out[0]
+        assert not out[1]
+
+    def test_missing_months(self):
+        n = 66
+        times = pd.date_range("2001-12-30", freq="1D", periods=n)
+        da = xr.DataArray(np.arange(n), [("time", times)])
+        miss = missing.missing_any(da, "MS")
+        np.testing.assert_array_equal(miss, [True, False, False, True])
+
+    def test_missing_years(self):
+        n = 378
+        times = pd.date_range("2001-12-31", freq="1D", periods=n)
+        da = xr.DataArray(np.arange(n), [("time", times)])
+        miss = missing.missing_any(da, "YS")
+        np.testing.assert_array_equal(miss, [True, False, True])
+
+    def test_missing_season(self):
+        n = 378
+        times = pd.date_range("2001-12-31", freq="1D", periods=n)
+        da = xr.DataArray(np.arange(n), [("time", times)])
+        miss = missing.missing_any(da, "Q-NOV")
+        np.testing.assert_array_equal(miss, [True, False, False, False, True])
+
+    def test_to_period_start(self, tasmin_series):
+        a = np.zeros(365) + K2C + 5.0
+        a[2] -= 20
+        ts = tasmin_series(a)
+        miss = missing.missing_any(ts, freq="AS-JUL")
+        np.testing.assert_equal(miss, [False])
+
+    def test_to_period_end(self, tasmin_series):
+        a = np.zeros(365) + K2C + 5.0
+        a[2] -= 20
+        ts = tasmin_series(a)
+        miss = missing.missing_any(ts, freq="A-JUN")
+        np.testing.assert_equal(miss, [False])
+
+    def test_month(self, tasmin_series):
+        ts = tasmin_series(np.zeros(36))
+        miss = missing.missing_any(ts, freq="YS", month=7)
+        np.testing.assert_equal(miss, [False])
+
+        miss = missing.missing_any(ts, freq="YS", month=8)
+        np.testing.assert_equal(miss, [True])
+
+        with pytest.raises(ValueError, match=r"No data for selected period."):
+            missing.missing_any(ts, freq="YS", month=1)
+
+        miss = missing.missing_any(ts, freq="YS", month=[7, 8])
+        np.testing.assert_equal(miss, [True])
+
+        ts = tasmin_series(np.zeros(76))
+        miss = missing.missing_any(ts, freq="YS", month=[7, 8])
+        np.testing.assert_equal(miss, [False])
+
+    def test_season(self, tasmin_series):
+        ts = tasmin_series(np.zeros(360))
+        miss = missing.missing_any(ts, freq="YS", season="MAM")
+        np.testing.assert_equal(miss, [False])
+
+        miss = missing.missing_any(ts, freq="YS", season="JJA")
+        np.testing.assert_array_equal(miss, [True, True])
+
+        miss = missing.missing_any(ts, freq="YS", season="SON")
+        np.testing.assert_equal(miss, [False])
+
+    def test_no_freq(self, tasmin_series):
+        ts = tasmin_series(np.zeros(360))
+
+        miss = missing.missing_any(ts, freq=None)
+        np.testing.assert_array_equal(miss, False)
+
+        t = list(range(31))
+        t.pop(5)
+        ts2 = ts.isel(time=t)
+        miss = missing.missing_any(ts2, freq=None)
+        np.testing.assert_array_equal(miss, True)
+
+        # With indexer
+        miss = missing.missing_any(ts, freq=None, month=[7])
+        np.testing.assert_array_equal(miss, False)
+
+        miss = missing.missing_any(ts2, freq=None, month=[7])
+        np.testing.assert_array_equal(miss, True)
+
+    def test_hydro(self):
+        fn = Path(TESTS_DATA, "Raven", "q_sim.nc")
+        ds = xr.open_dataset(fn)
+        miss = missing.missing_any(ds.q_sim, freq="YS")
+        np.testing.assert_array_equal(miss[:-1], False)
+        np.testing.assert_array_equal(miss[-1], True)
+
+        miss = missing.missing_any(ds.q_sim, freq="YS", season="JJA")
+        np.testing.assert_array_equal(miss, False)
+
+
+class TestMissingWMO:
+    def test_missing_days(self, tas_series):
+        a = np.arange(360.0)
+        a[5:7] = np.nan  # Number of missing values under acceptable limit in a month
+        a[40:45] = np.nan  # Too many consecutive missing values
+        a[70:92:2] = np.nan  # Too many non-consecutive missing values
+        ts = tas_series(a)
+        out = missing.missing_wmo(ts, freq="MS")
+        assert not out[0]
+        assert out[1]
+        assert out[2]
+
+    def test_missing_days_in_quarter(self, tas_series):
+        a = np.arange(360.0)
+        a[5:16] = np.nan  # Number of missing values under acceptable limit in a month
+        ts = tas_series(a)
+        out = missing.missing_wmo(ts, freq="QS-JAN")
+        np.testing.assert_array_equal(out, [True, False, False, True])
+
+
+class TestMissingPct:
+    def test_missing_days(self, tas_series):
+        a = np.arange(360.0)
+        a[5:7] = np.nan  # Number of missing values under acceptable limit in a month
+        a[40:45] = np.nan  # Too many missing values
+        ts = tas_series(a)
+        out = missing.missing_pct(ts, freq="MS", tolerance=0.1)
+        assert not out[0]
+        assert out[1]
+
+
+class TestAtLeastNValid:
+    def test_at_least_n_valid(self, tas_series):
+        a = np.arange(360.0)
+        a[5:10] = np.nan  # Number of missing values under acceptable limit in a month
+        a[40:55] = np.nan  # Too many missing values
+        ts = tas_series(a)
+        out = missing.at_least_n_valid(ts, freq="MS", n=20)
+        np.testing.assert_array_equal(out[:2], [False, True])
diff --git a/tests/test_options.py b/tests/test_options.py
index 3634e4e6..5fc83254 100644
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -4,7 +4,7 @@
 import pytest
 
 from xclim import set_options
-from xclim.core.checks import MissingBase
+from xclim.core.missing import MissingBase
 from xclim.core.options import OPTIONS
 from xclim.core.options import register_missing_method
 

environment.yml
pip install -e .[dev]
parse_log_pytest
environment.yml
pytest pytest-cov sphinx sphinx-rtd-theme nbsphinx pandoc ipython ipykernel jupyter_client matplotlib distributed>=2.0
apt-get update apt-get install -y libspatialindex-dev
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
async-generator==1.10
attrs @ file:///home/conda/feedstock_root/build_artifacts/attrs_1671632566681/work
Babel==2.11.0
backcall==0.2.0
bleach==4.1.0
bokeh @ file:///home/conda/feedstock_root/build_artifacts/bokeh_1625756939897/work
boltons @ file:///home/conda/feedstock_root/build_artifacts/boltons_1622121478442/work
Bottleneck @ file:///home/conda/feedstock_root/build_artifacts/bottleneck_1629826550175/work
bump2version==1.0.1
certifi==2021.5.30
cffi==1.15.1
cfgv==3.3.1
cftime @ file:///home/conda/feedstock_root/build_artifacts/cftime_1632539733990/work
charset-normalizer==2.0.12
click==7.1.2
click-plugins==1.1.1
cligj @ file:///home/conda/feedstock_root/build_artifacts/cligj_1633637764473/work
cloudpickle @ file:///home/conda/feedstock_root/build_artifacts/cloudpickle_1674202310934/work
colorama==0.4.5
contextvars==2.4
coverage==6.2
cryptography==40.0.2
cycler @ file:///home/conda/feedstock_root/build_artifacts/cycler_1635519461629/work
cytoolz==0.11.0
dask @ file:///home/conda/feedstock_root/build_artifacts/dask-core_1614995065708/work
decorator==5.1.1
defusedxml==0.7.1
distlib==0.3.9
distributed @ file:///home/conda/feedstock_root/build_artifacts/distributed_1615002625500/work
docutils==0.18.1
entrypoints==0.4
filelock==3.4.1
Fiona==1.8.20
flake8==5.0.4
fsspec @ file:///home/conda/feedstock_root/build_artifacts/fsspec_1674184942191/work
GDAL==3.3.0
geojson==2.5.0
geopandas @ file:///home/conda/feedstock_root/build_artifacts/geopandas_1623249625470/work
HeapDict==1.0.1
identify==2.4.4
idna==3.10
imagesize==1.4.1
immutables @ file:///home/conda/feedstock_root/build_artifacts/immutables_1628601257972/work
importlib-metadata==4.2.0
importlib-resources==5.2.3
iniconfig==1.1.1
ipykernel==5.5.6
ipython==7.16.3
ipython-genutils==0.2.0
jedi==0.17.2
jeepney==0.7.1
Jinja2 @ file:///home/conda/feedstock_root/build_artifacts/jinja2_1636510082894/work
joblib @ file:///home/conda/feedstock_root/build_artifacts/joblib_1663332044897/work
jsonschema==3.2.0
jupyter-client==7.1.2
jupyter-core==4.9.2
jupyterlab-pygments==0.1.2
keyring==23.4.1
kiwisolver @ file:///home/conda/feedstock_root/build_artifacts/kiwisolver_1610099771815/work
llvmlite==0.36.0
locket @ file:///home/conda/feedstock_root/build_artifacts/locket_1650660393415/work
mapclassify @ file:///home/conda/feedstock_root/build_artifacts/mapclassify_1673861555770/work
MarkupSafe @ file:///home/conda/feedstock_root/build_artifacts/markupsafe_1621455668064/work
matplotlib @ file:///home/conda/feedstock_root/build_artifacts/matplotlib-suite_1611858699142/work
mccabe==0.7.0
mistune==0.8.4
msgpack @ file:///home/conda/feedstock_root/build_artifacts/msgpack-python_1610121702224/work
munch==2.5.0
nbclient==0.5.9
nbconvert==6.0.7
nbformat==5.1.3
nbsphinx==0.8.8
nest-asyncio==1.6.0
netCDF4 @ file:///home/conda/feedstock_root/build_artifacts/netcdf4_1624308175279/work
networkx @ file:///home/conda/feedstock_root/build_artifacts/networkx_1646092782768/work
nodeenv==1.6.0
numba @ file:///home/conda/feedstock_root/build_artifacts/numba_1623568542230/work
numpy @ file:///home/conda/feedstock_root/build_artifacts/numpy_1626681920064/work
olefile @ file:///home/conda/feedstock_root/build_artifacts/olefile_1602866521163/work
packaging @ file:///home/conda/feedstock_root/build_artifacts/packaging_1637239678211/work
pandas==1.1.5
pandoc==2.4
pandocfilters==1.5.1
parso==0.7.1
partd @ file:///home/conda/feedstock_root/build_artifacts/partd_1617910651905/work
pexpect==4.9.0
pickleshare==0.7.5
Pillow @ file:///home/conda/feedstock_root/build_artifacts/pillow_1621287993989/work
Pint @ file:///home/conda/feedstock_root/build_artifacts/pint_1630435029884/work
pkginfo==1.10.0
platformdirs==2.4.0
pluggy==1.0.0
plumbum==1.8.3
ply==3.11
pre-commit==2.17.0
prompt-toolkit==3.0.36
psutil @ file:///home/conda/feedstock_root/build_artifacts/psutil_1610127101219/work
ptyprocess==0.7.0
py==1.11.0
pycodestyle==2.9.1
pycparser==2.21
pyflakes==2.5.0
Pygments==2.14.0
pyparsing @ file:///home/conda/feedstock_root/build_artifacts/pyparsing_1724616129934/work
pyproj @ file:///home/conda/feedstock_root/build_artifacts/pyproj_1614934904503/work
pyrsistent==0.18.0
pytest==7.0.1
pytest-cov==4.0.0
pytest-runner==5.3.2
python-dateutil @ file:///home/conda/feedstock_root/build_artifacts/python-dateutil_1626286286081/work
pytz @ file:///home/conda/feedstock_root/build_artifacts/pytz_1693930252784/work
PyYAML==5.4.1
pyzmq==25.1.2
readme-renderer==34.0
requests==2.27.1
requests-toolbelt==1.0.0
rfc3986==1.5.0
Rtree @ file:///home/conda/feedstock_root/build_artifacts/rtree_1610147153210/work
scikit-learn @ file:///home/conda/feedstock_root/build_artifacts/scikit-learn_1630910533947/work
scipy @ file:///home/conda/feedstock_root/build_artifacts/scipy_1629411471490/work
SecretStorage==3.3.3
Shapely @ file:///home/conda/feedstock_root/build_artifacts/shapely_1628205367507/work
six @ file:///home/conda/feedstock_root/build_artifacts/six_1620240208055/work
snowballstemmer==2.2.0
sortedcontainers @ file:///home/conda/feedstock_root/build_artifacts/sortedcontainers_1621217038088/work
Sphinx==5.3.0
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tblib @ file:///home/conda/feedstock_root/build_artifacts/tblib_1616261298899/work
testpath==0.6.0
threadpoolctl @ file:///home/conda/feedstock_root/build_artifacts/threadpoolctl_1643647933166/work
toml==0.10.2
tomli==1.2.3
toolz @ file:///home/conda/feedstock_root/build_artifacts/toolz_1657485559105/work
tornado @ file:///home/conda/feedstock_root/build_artifacts/tornado_1610094701020/work
tox==3.28.0
tqdm==4.64.1
traitlets==4.3.3
twine==3.8.0
typing_extensions @ file:///home/conda/feedstock_root/build_artifacts/typing_extensions_1644850595256/work
urllib3==1.26.20
virtualenv==20.16.2
watchdog==2.3.1
wcwidth==0.2.13
webencodings==0.5.1
xarray @ file:///home/conda/feedstock_root/build_artifacts/xarray_1621474818012/work
-e git+https://github.com/Ouranosinc/xclim.git@47d4aa4d1b90f0c6c3e037d8887652f8541f19bb#egg=xclim
zict==2.0.0
zipp @ file:///home/conda/feedstock_root/build_artifacts/zipp_1633302054558/work

name: xclim
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.2.0=pyh71513ae_0
  - bokeh=2.3.3=py36h5fab9bb_0
  - boltons=21.0.0=pyhd8ed1ab_0
  - boost-cpp=1.74.0=hc6e9bd1_3
  - bottleneck=1.3.2=py36he33b4a0_4
  - bzip2=1.0.8=h7f98852_4
  - c-ares=1.18.1=h7f98852_0
  - ca-certificates=2025.2.25=h06a4308_0
  - cairo=1.16.0=h6cf1ce9_1008
  - certifi=2021.5.30=py36h06a4308_0
  - cfitsio=3.470=hb418390_7
  - cftime=1.5.1=py36he33b4a0_0
  - click=7.1.2=pyh9f0ad1d_0
  - click-plugins=1.1.1=py_0
  - cligj=0.7.2=pyhd8ed1ab_1
  - cloudpickle=2.2.1=pyhd8ed1ab_0
  - contextvars=2.4=py_0
  - curl=7.79.1=h2574ce0_1
  - cycler=0.11.0=pyhd8ed1ab_0
  - cytoolz=0.11.0=py36h8f6f2f9_3
  - dask=2021.3.0=pyhd8ed1ab_0
  - dask-core=2021.3.0=pyhd8ed1ab_0
  - distributed=2021.3.0=py36h5fab9bb_0
  - expat=2.4.8=h27087fc_0
  - fiona=1.8.20=py36he9df7c8_1
  - fontconfig=2.14.0=h8e229c2_0
  - freetype=2.10.4=h0708190_1
  - freexl=1.0.6=h7f98852_0
  - fsspec=2023.1.0=pyhd8ed1ab_0
  - gdal=3.3.0=py36h5febb71_1
  - geojson=2.5.0=py_0
  - geopandas=0.9.0=pyhd8ed1ab_1
  - geopandas-base=0.9.0=pyhd8ed1ab_1
  - geos=3.9.1=h9c3ff4c_2
  - geotiff=1.6.0=hcf90da6_5
  - gettext=0.19.8.1=h0b5b191_1005
  - giflib=5.2.1=h36c2ea0_2
  - hdf4=4.2.15=h10796ff_3
  - hdf5=1.10.6=nompi_h6a2412b_1114
  - heapdict=1.0.1=py_0
  - icu=68.2=h9c3ff4c_0
  - immutables=0.16=py36h8f6f2f9_0
  - importlib_metadata=4.8.1=hd8ed1ab_1
  - jinja2=3.0.3=pyhd8ed1ab_0
  - joblib=1.2.0=pyhd8ed1ab_0
  - jpeg=9e=h166bdaf_1
  - json-c=0.15=h98cffda_0
  - kealib=1.4.14=hcc255d8_2
  - keyutils=1.6.1=h166bdaf_0
  - kiwisolver=1.3.1=py36h605e78d_1
  - krb5=1.19.3=h3790be6_0
  - lcms2=2.12=hddcbb42_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libblas=3.9.0=16_linux64_openblas
  - libcblas=3.9.0=16_linux64_openblas
  - libcurl=7.79.1=h2574ce0_1
  - libdap4=3.20.6=hd7c4107_2
  - libedit=3.1.20191231=he28a2e2_2
  - libev=4.33=h516909a_1
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgdal=3.3.0=h679344c_1
  - libgfortran-ng=13.2.0=h69a702a_0
  - libgfortran5=13.2.0=ha4646dd_0
  - libglib=2.68.4=h3e27bee_0
  - libgomp=11.2.0=h1234567_1
  - libiconv=1.17=h166bdaf_0
  - libkml=1.3.0=h238a007_1014
  - liblapack=3.9.0=16_linux64_openblas
  - libllvm10=10.0.1=he513fc3_3
  - libnetcdf=4.8.0=nompi_hcd642e3_103
  - libnghttp2=1.43.0=h812cca2_1
  - libnsl=2.0.0=h7f98852_0
  - libopenblas=0.3.21=h043d6bf_0
  - libpng=1.6.37=h21135ba_2
  - libpq=13.3=hd57d9b9_0
  - librttopo=1.1.0=h1185371_6
  - libspatialindex=1.9.3=h9c3ff4c_4
  - libspatialite=5.0.1=h20cb978_4
  - libssh2=1.10.0=ha56f1ee_2
  - libstdcxx-ng=11.2.0=h1234567_1
  - libtiff=4.2.0=hbd63e13_2
  - libuuid=2.32.1=h7f98852_1000
  - libwebp-base=1.2.2=h7f98852_1
  - libxcb=1.13=h7f98852_1004
  - libxml2=2.9.12=h72842e0_0
  - libzip=1.8.0=h4de3113_1
  - llvmlite=0.36.0=py36h05121d2_0
  - locket=1.0.0=pyhd8ed1ab_0
  - lz4-c=1.9.3=h9c3ff4c_1
  - mapclassify=2.5.0=pyhd8ed1ab_1
  - markupsafe=2.0.1=py36h8f6f2f9_0
  - matplotlib-base=3.3.4=py36hd391965_0
  - msgpack-python=1.0.2=py36h605e78d_1
  - munch=2.5.0=py_0
  - ncurses=6.4=h6a678d5_0
  - netcdf4=1.5.7=nompi_py36h3d597d4_100
  - networkx=2.7=pyhd8ed1ab_0
  - numba=0.53.1=py36h1328434_1
  - numpy=1.19.5=py36hfc0c790_2
  - olefile=0.46=pyh9f0ad1d_1
  - openjpeg=2.4.0=hb52868f_1
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd8ed1ab_0
  - pandas=1.1.5=py36h284efc9_0
  - partd=1.2.0=pyhd8ed1ab_0
  - pcre=8.45=h9c3ff4c_0
  - pillow=8.2.0=py36ha6010c0_1
  - pint=0.17=pyhd8ed1ab_1
  - pip=21.2.2=py36h06a4308_0
  - pixman=0.40.0=h36c2ea0_0
  - poppler=21.03.0=h93df280_0
  - poppler-data=0.4.12=hd8ed1ab_0
  - postgresql=13.3=h2510834_0
  - proj=8.0.0=h277dcde_0
  - psutil=5.8.0=py36h8f6f2f9_1
  - pthread-stubs=0.4=h36c2ea0_1001
  - pyparsing=3.1.4=pyhd8ed1ab_0
  - pyproj=3.0.1=py36h6e9aebd_1
  - python=3.6.13=h12debd9_1
  - python-dateutil=2.8.2=pyhd8ed1ab_0
  - python_abi=3.6=2_cp36m
  - pytz=2023.3.post1=pyhd8ed1ab_0
  - pyyaml=5.4.1=py36h8f6f2f9_1
  - readline=8.2=h5eee18b_0
  - rtree=0.9.7=py36hb415506_1
  - scikit-learn=0.24.2=py36hc89565f_1
  - scipy=1.5.3=py36h81d768a_1
  - setuptools=58.0.4=py36h06a4308_0
  - shapely=1.7.1=py36hff28ebb_5
  - six=1.16.0=pyh6c4a22f_0
  - sortedcontainers=2.4.0=pyhd8ed1ab_0
  - sqlite=3.45.3=h5eee18b_0
  - tblib=1.7.0=pyhd8ed1ab_0
  - threadpoolctl=3.1.0=pyh8a188c0_0
  - tiledb=2.2.9=h91fcb0e_0
  - tk=8.6.14=h39e8969_0
  - toolz=0.12.0=pyhd8ed1ab_0
  - tornado=6.1=py36h8f6f2f9_1
  - typing-extensions=4.1.1=hd8ed1ab_0
  - typing_extensions=4.1.1=pyha770c72_0
  - tzcode=2022a=h166bdaf_0
  - tzdata=2025b=h78e105d_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xarray=0.18.2=pyhd8ed1ab_0
  - xerces-c=3.2.3=h9d8b166_3
  - xorg-kbproto=1.0.7=h7f98852_1002
  - xorg-libice=1.0.10=h7f98852_0
  - xorg-libsm=1.2.3=hd9c2040_1000
  - xorg-libx11=1.7.2=h7f98852_0
  - xorg-libxau=1.0.9=h7f98852_0
  - xorg-libxdmcp=1.1.3=h7f98852_0
  - xorg-libxext=1.3.4=h7f98852_1
  - xorg-libxrender=0.9.10=h7f98852_1003
  - xorg-renderproto=0.11.1=h7f98852_1002
  - xorg-xextproto=7.3.0=h7f98852_1002
  - xorg-xproto=7.0.31=h7f98852_1007
  - xz=5.6.4=h5eee18b_1
  - yaml=0.2.5=h7f98852_2
  - zict=2.0.0=py_0
  - zipp=3.6.0=pyhd8ed1ab_0
  - zlib=1.2.13=h5eee18b_1
  - zstd=1.4.9=ha95c52a_0
  - pip:
      - alabaster==0.7.13
      - async-generator==1.10
      - babel==2.11.0
      - backcall==0.2.0
      - bleach==4.1.0
      - bump2version==1.0.1
      - cffi==1.15.1
      - cfgv==3.3.1
      - charset-normalizer==2.0.12
      - colorama==0.4.5
      - coverage==6.2
      - cryptography==40.0.2
      - decorator==5.1.1
      - defusedxml==0.7.1
      - distlib==0.3.9
      - docutils==0.18.1
      - entrypoints==0.4
      - filelock==3.4.1
      - flake8==5.0.4
      - identify==2.4.4
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.2.0
      - importlib-resources==5.2.3
      - iniconfig==1.1.1
      - ipykernel==5.5.6
      - ipython==7.16.3
      - ipython-genutils==0.2.0
      - jedi==0.17.2
      - jeepney==0.7.1
      - jsonschema==3.2.0
      - jupyter-client==7.1.2
      - jupyter-core==4.9.2
      - jupyterlab-pygments==0.1.2
      - keyring==23.4.1
      - mccabe==0.7.0
      - mistune==0.8.4
      - nbclient==0.5.9
      - nbconvert==6.0.7
      - nbformat==5.1.3
      - nbsphinx==0.8.8
      - nest-asyncio==1.6.0
      - nodeenv==1.6.0
      - pandoc==2.4
      - pandocfilters==1.5.1
      - parso==0.7.1
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pkginfo==1.10.0
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - plumbum==1.8.3
      - ply==3.11
      - pre-commit==2.17.0
      - prompt-toolkit==3.0.36
      - ptyprocess==0.7.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pyflakes==2.5.0
      - pygments==2.14.0
      - pyrsistent==0.18.0
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-runner==5.3.2
      - pyzmq==25.1.2
      - readme-renderer==34.0
      - requests==2.27.1
      - requests-toolbelt==1.0.0
      - rfc3986==1.5.0
      - secretstorage==3.3.3
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - testpath==0.6.0
      - toml==0.10.2
      - tomli==1.2.3
      - tox==3.28.0
      - tqdm==4.64.1
      - traitlets==4.3.3
      - twine==3.8.0
      - urllib3==1.26.20
      - virtualenv==20.16.2
      - watchdog==2.3.1
      - wcwidth==0.2.13
      - webencodings==0.5.1
prefix: /opt/conda/envs/xclim

tests/test_checks.py::test_check_valid_ok[a tests/test_checks.py::test_check_valid_raise[None-a tests/test_checks.py::test_check_valid_raise[a tests/test_checks.py::TestDateHandling::test_assert_daily tests/test_checks.py::TestDateHandling::test_bad_frequency tests/test_checks.py::TestDateHandling::test_missing_one_day_between_two_years tests/test_checks.py::TestDateHandling::test_duplicate_dates tests/test_checks.py::test_cf_compliance_options tests/test_missing.py::TestMissingAnyFills::test_missing_days tests/test_missing.py::TestMissingAnyFills::test_missing_months tests/test_missing.py::TestMissingAnyFills::test_missing_years tests/test_missing.py::TestMissingAnyFills::test_missing_season tests/test_missing.py::TestMissingAnyFills::test_to_period_start tests/test_missing.py::TestMissingAnyFills::test_to_period_end tests/test_missing.py::TestMissingAnyFills::test_month tests/test_missing.py::TestMissingAnyFills::test_season tests/test_missing.py::TestMissingAnyFills::test_no_freq tests/test_missing.py::TestMissingAnyFills::test_hydro tests/test_missing.py::TestMissingWMO::test_missing_days tests/test_missing.py::TestMissingWMO::test_missing_days_in_quarter tests/test_missing.py::TestMissingPct::test_missing_days tests/test_missing.py::TestAtLeastNValid::test_at_least_n_valid tests/test_options.py::test_set_options_valid[metadata_locales-value0] tests/test_options.py::test_set_options_valid[data_validation-log] tests/test_options.py::test_set_options_valid[data_validation-raise] tests/test_options.py::test_set_options_valid[cf_compliance-log] tests/test_options.py::test_set_options_valid[cf_compliance-raise] tests/test_options.py::test_set_options_valid[check_missing-wmo] tests/test_options.py::test_set_options_valid[check_missing-any] tests/test_options.py::test_set_options_valid[missing_options-value7] tests/test_options.py::test_set_options_valid[missing_options-value8] tests/test_options.py::test_set_options_valid[missing_options-value9] tests/test_options.py::test_set_options_invalid[metadata_locales-value0] tests/test_options.py::test_set_options_invalid[metadata_locales-value1] tests/test_options.py::test_set_options_invalid[data_validation-True] tests/test_options.py::test_set_options_invalid[cf_compliance-False] tests/test_options.py::test_set_options_invalid[missing_options-value4] tests/test_options.py::test_set_options_invalid[missing_options-value5] tests/test_options.py::test_set_options_invalid[missing_options-value6] tests/test_options.py::test_set_options_invalid[missing_options-value7] tests/test_options.py::test_register_missing_method
xclim/core/indicator.py xclim/core/formatting.py HISTORY.rst docs/checks.rst xclim/indicators/seaIce/_seaice.py xclim/core/datachecks.py xclim/core/cfchecks.py xclim/indicators/land/_streamflow.py xclim/indicators/atmos/_temperature.py xclim/core/missing.py xclim/core/checks.py docs/notebooks/options.ipynb
xclim/core/indicator.py xclim/core/formatting.py HISTORY.rst docs/checks.rst xclim/indicators/seaIce/_seaice.py xclim/core/datachecks.py xclim/core/cfchecks.py xclim/indicators/land/_streamflow.py xclim/indicators/atmos/_temperature.py xclim/core/missing.py xclim/core/checks.py docs/notebooks/options.ipynb