Hour angle in local solar time. Zero at local solar noon.

    Parameters
    ----------
    times : :class:`pandas.DatetimeIndex`
        Corresponding timestamps, must be localized to the timezone for the
        ``longitude``.
    longitude : numeric
        Longitude in degrees
    equation_of_time : numeric
        Equation of time in minutes.

    Returns
    -------
    hour_angle : numeric
        Hour angle in local solar time in degrees.

    References
    ----------
    [1] J. A. Duffie and W. A. Beckman,  "Solar Engineering of Thermal
    Processes, 3rd Edition" pp. 13, J. Wiley and Sons, New York (2006)

    [2] J. H. Seinfeld and S. N. Pandis, "Atmospheric Chemistry and Physics"
    p. 132, J. Wiley (1998)

    [3] Daryl R. Myers, "Solar Radiation: Practical Modeling for Renewable
    Energy Applications", p. 5 CRC Press (2013)

    See Also
    --------
    equation_of_time_Spencer71
    equation_of_time_pvcdrom

def hour_angle(times, longitude, equation_of_time):
    """
    Hour angle in local solar time. Zero at local solar noon.

    Parameters
    ----------
    times : :class:`pandas.DatetimeIndex`
        Corresponding timestamps, must be localized to the timezone for the
        ``longitude``.
    longitude : numeric
        Longitude in degrees
    equation_of_time : numeric
        Equation of time in minutes.

    Returns
    -------
    hour_angle : numeric
        Hour angle in local solar time in degrees.

    References
    ----------
    [1] J. A. Duffie and W. A. Beckman,  "Solar Engineering of Thermal
    Processes, 3rd Edition" pp. 13, J. Wiley and Sons, New York (2006)

    [2] J. H. Seinfeld and S. N. Pandis, "Atmospheric Chemistry and Physics"
    p. 132, J. Wiley (1998)

    [3] Daryl R. Myers, "Solar Radiation: Practical Modeling for Renewable
    Energy Applications", p. 5 CRC Press (2013)

    See Also
    --------
    equation_of_time_Spencer71
    equation_of_time_pvcdrom
    """
    naive_times = times.tz_localize(None)  # naive but still localized
    # hours - timezone = (times - normalized_times) - (naive_times - times)
    hrs_minus_tzs = 1 / NS_PER_HR * (
        2 * times.astype(np.int64) - times.normalize().astype(np.int64) -
        naive_times.astype(np.int64))
    # ensure array return instead of a version-dependent pandas <T>Index
    return np.asarray(
        15. * (hrs_minus_tzs - 12.) + longitude + equation_of_time / 4.)