• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Ouranosinc / miranda / 2260467866

pending completion
2260467866

Pull #33

github

GitHub
Merge c422e4a0b into d076d8475
Pull Request #33: Support CORDEX and CMIP5/6

32 of 224 new or added lines in 16 files covered. (14.29%)

8 existing lines in 5 files now uncovered.

660 of 3249 relevant lines covered (20.31%)

0.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.56
/miranda/decode/_time.py
1
import logging
3✔
2
from logging import config
3✔
3
from typing import Union
3✔
4

5
import cftime
3✔
6
import pandas as pd
3✔
7
from pandas._libs.tslibs import NaTType  # noqa
3✔
8

9
from miranda.scripting import LOGGING_CONFIG
3✔
10

11
logging.config.dictConfig(LOGGING_CONFIG)
3✔
12

13
__all__ = [
3✔
14
    "date_parser",
15
    "DecoderError",
16
    "TIME_UNITS_TO_FREQUENCY",
17
    "TIME_UNITS_TO_TIMEDELTA",
18
]
19

20
TIME_UNITS_TO_FREQUENCY = {
3✔
21
    "subhrPt": "sub-hr",
22
    "hourly": "1hr",
23
    "hours": "1hr",
24
    "1hr": "1hr",
25
    "3-hourly": "3hr",
26
    "3hr": "3hr",
27
    "6-hourly": "6hr",
28
    "6hr": "6hr",
29
    "daily": "day",
30
    "days": "day",
31
    "day": "day",
32
    "weekly": "sem",
33
    "weeks": "sem",
34
    "sem": "sem",
35
    "monthly": "mon",
36
    "months": "mon",
37
    "mon": "mon",
38
    "monC": "monC",
39
    "Amon": "mon",
40
    "Omon": "mon",
41
    "qtr": "3mon",
42
    "quarter": "3mon",
43
    "3mon": "3mon",
44
    "2qtr": "6mon",
45
    "semi-annual": "6mon",
46
    "half-yearly": "6mon",
47
    "6mon": "6mon",
48
    "yearly": "yr",
49
    "years": "yr",
50
    "annual": "yr",
51
    "yr": "yr",
52
    "yrPt": "yrPt",
53
    "decadal": "dec",
54
    "decades": "dec",
55
    "dec": "dec",
56
    "fixed": "fx",
57
    "fx": "fx",
58
}
59
TIME_UNITS_TO_TIMEDELTA = {
3✔
60
    "hourly": "1h",
61
    "hours": "1h",
62
    "1hr": "1h",
63
    "1hrCM": "1h",
64
    "1hrPt": "1h",
65
    "3hr": "3h",
66
    "3hrPt": "3h",
67
    "6-hourly": "6h",
68
    "6hr": "6h",
69
    "6hrPt": "6h",
70
    "daily": "1d",
71
    "day": "1d",
72
    "days": "1d",
73
    "weekly": "7d",
74
    "weeks": "7d",
75
    "sem": "7d",
76
    "mon": "30d",
77
    "monC": "30d",
78
    "monPt": "30d",
79
    "Amon": "30d",
80
    "qtr": "90d",
81
    "quarter": "90d",
82
    "3mon": "90d",
83
    "2qtr": "180d",
84
    "6mon": "180d",
85
    "half-yearly": "180d",
86
    "semi-annual": "180d",
87
    "annual": "365d",
88
    "yearly": "365d",
89
    "years": "365d",
90
    "year": "365d",
91
    "yr": "365d",
92
    "yrPt": "365d",
93
}
94

95

96
class DecoderError(Exception):
3✔
97
    pass
3✔
98

99

100
def date_parser(
3✔
101
    date: str,
102
    *,
103
    end_of_period: bool = False,
104
    output_type: str = "str",
105
    strftime_format: str = "%Y-%m-%d",
106
) -> Union[str, pd.Timestamp, NaTType]:
107
    """Returns a datetime from a string.
108

109
    Parameters
110
    ----------
111
    date : str
112
      Date to be converted.
113
    end_of_period : bool
114
      If True, the date will be the end of month or year depending on what's most appropriate.
115
    output_type: {"datetime", "str"}
116
      Returned object type.
117
    strftime_format: str
118
      If output_type=='str', this sets the strftime format.
119

120
    Returns
121
    -------
122
    pd.Timestamp or str or pd.NaT
123
      Parsed date.
124

125
    Notes
126
    -----
127
    Adapted from code written by Gabriel Rondeau-Genesse (@RondeauG)
128
    """
129

130
    # Formats, ordered depending on string length
131
    formats = {
×
132
        4: ["%Y"],
133
        6: ["%Y%m"],
134
        7: ["%Y-%m"],
135
        8: ["%Y%m%d"],
136
        10: ["%Y%m%d%H", "%Y-%m-%d"],
137
        12: ["%Y%m%d%H%M"],
138
        13: ["%Y%m-%Y%m"],
139
        17: ["%Y%m%d-%Y%m%d"],
140
        19: ["%Y-%m-%dT%H:%M:%S"],
141
        21: ["%Y%m%d%H-%Y%m%d%H"],
142
        25: ["%Y%m%d%H%M-%Y%m%d%H%M"],
143
    }
144
    end_date_found = False
×
145

146
    def _parse_date(d, fmts):
×
147
        for fmt in fmts:
×
148
            try:
×
149
                s = pd.to_datetime(d, format=fmt)
×
150
                match = fmt
×
151
                break
×
152
            except ValueError:
×
153
                pass
×
154
        else:
NEW
155
            raise DecoderError(f"Can't parse date {d} with supported formats {fmts}.")
×
156
        return s, match
×
157

158
    date_format = None
×
159
    if isinstance(date, str):
×
NEW
160
        if len(date) in [13, 17, 21, 25]:
×
161
            dates = date.split("-")
×
162
            if not end_of_period:
×
163
                date = dates[0]
×
164
            else:
165
                date = dates[1]
×
166
                end_date_found = True
×
167

168
        try:
×
169
            possible_formats = formats[len(date)]
×
170
            date, date_format = _parse_date(date, possible_formats)
×
171
        except KeyError:
×
172
            # Return NaT for fixed/missing/ill-formatted date strings
173
            return pd.NaT
×
174

175
    elif isinstance(date, cftime.datetime):
×
176
        for n in range(3):
×
177
            try:
×
178
                date = pd.Timestamp.fromisoformat((date - pd.Timedelta(n)).isoformat())
×
179
            except ValueError:  # We are NOT catching OutOfBoundsDatetime.
×
180
                pass
×
181
            else:
182
                break
×
183
        else:
184
            raise DecoderError(
×
185
                f"Unable to parse cftime date {date}, even when moving back 2 days."
186
            )
187
    elif not isinstance(date, pd.Timestamp):
×
188
        date = pd.Timestamp(date)  # noqa
×
189

190
    if end_of_period and date_format and not end_date_found:
×
191
        if "m" not in date_format:
×
NEW
192
            date = date + pd.tseries.offsets.YearEnd(1)  # noqa
×
193
        elif "d" not in date_format:
×
NEW
194
            date = date + pd.tseries.offsets.MonthEnd(1)  # noqa
×
195
        # TODO: Implement sub-daily?
196

197
    if output_type == "str":
×
NEW
198
        return date.strftime(strftime_format)
×
199

200
    return date
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc