Paul Ganssle | 4c0ba12 | 2018-06-07 20:07:21 +0100 | [diff] [blame] | 1 | Exercises |
| 2 | ========= |
| 3 | |
| 4 | It is often useful to work through some examples in order to understand how a module works; on this page, there are several exercises of varying difficulty that you can use to learn how to use ``dateutil``. |
| 5 | |
| 6 | If you are interested in helping improve the documentation of ``dateutil``, it is recommended that you attempt to complete these exercises with no resources *other than dateutil's documentation*. If you find that the documentation is not clear enough to allow you to complete these exercises, open an issue on the `dateutil issue tracker <https://github.com/dateutil/dateutil/issues>`_ to let the developers know what part of the documentation needs improvement. |
| 7 | |
| 8 | |
| 9 | .. contents:: Table of Contents |
| 10 | :backlinks: top |
| 11 | :local: |
| 12 | |
Paul Ganssle | 404af91 | 2018-06-07 20:09:47 +0100 | [diff] [blame] | 13 | |
| 14 | Martin Luther King Day |
| 15 | -------------------------------- |
| 16 | |
| 17 | |
| 18 | `Martin Luther King, Jr Day <https://en.wikipedia.org/wiki/Martin_Luther_King_Jr._Day>`_ is a US holiday that occurs every year on the third Monday in January? |
| 19 | |
| 20 | How would you generate a `recurrence rule <../rrule.html>`_ that generates Martin Luther King Day, starting from its first observance in 1986? |
| 21 | |
| 22 | |
| 23 | **Test Script** |
| 24 | |
| 25 | To solve this exercise, copy-paste this script into a document, change anything between the ``--- YOUR CODE ---`` comment blocks. |
| 26 | |
| 27 | .. raw:: html |
| 28 | |
| 29 | <details> |
| 30 | |
| 31 | .. code-block:: python3 |
| 32 | |
| 33 | # ------- YOUR CODE -------------# |
| 34 | from dateutil import rrule |
| 35 | |
| 36 | MLK_DAY = <<YOUR CODE HERE>> |
| 37 | |
| 38 | # -------------------------------# |
| 39 | |
| 40 | from datetime import datetime |
| 41 | MLK_TEST_CASES = [ |
| 42 | ((datetime(1970, 1, 1), datetime(1980, 1, 1)), |
| 43 | []), |
| 44 | ((datetime(1980, 1, 1), datetime(1989, 1, 1)), |
| 45 | [datetime(1986, 1, 20), |
| 46 | datetime(1987, 1, 19), |
| 47 | datetime(1988, 1, 18)]), |
| 48 | ((datetime(2017, 2, 1), datetime(2022, 2, 1)), |
| 49 | [datetime(2018, 1, 15, 0, 0), |
| 50 | datetime(2019, 1, 21, 0, 0), |
| 51 | datetime(2020, 1, 20, 0, 0), |
| 52 | datetime(2021, 1, 18, 0, 0), |
| 53 | datetime(2022, 1, 17, 0, 0)] |
| 54 | ), |
| 55 | ] |
| 56 | |
| 57 | def test_mlk_day(): |
| 58 | for (between_args, expected) in MLK_TEST_CASES: |
| 59 | assert MLK_DAY.between(*between_args) == expected |
| 60 | |
| 61 | if __name__ == "__main__": |
| 62 | test_mlk_day() |
| 63 | print('Success!') |
| 64 | |
Paul Ganssle | a5c248b | 2018-06-08 07:26:25 +0100 | [diff] [blame] | 65 | .. raw:: html |
| 66 | |
| 67 | </details> |
| 68 | |
| 69 | |
| 70 | |
| 71 | Next Monday meeting |
| 72 | ------------------- |
| 73 | |
| 74 | A team has a meeting at 10 AM every Monday and wants a function that tells them, given a ``datetime.datetime`` object, what is the date and time of the *next* Monday meeting? This is probably best accomplished using a `relativedelta <../relativedelta.html>`_. |
| 75 | |
| 76 | **Test Script** |
| 77 | |
| 78 | To solve this exercise, copy-paste this script into a document, change anything between the ``--- YOUR CODE ---`` comment blocks. |
| 79 | |
| 80 | .. raw:: html |
| 81 | |
| 82 | <details> |
| 83 | |
| 84 | |
| 85 | .. code-block:: python3 |
| 86 | |
| 87 | # --------- YOUR CODE -------------- # |
| 88 | from dateutil import relativedelta |
| 89 | |
| 90 | def next_monday(dt): |
| 91 | <<YOUR CODE HERE>> |
| 92 | |
| 93 | # ---------------------------------- # |
| 94 | |
| 95 | from datetime import datetime |
| 96 | from dateutil import tz |
| 97 | |
| 98 | NEXT_MONDAY_CASES = [ |
| 99 | (datetime(2018, 4, 11, 14, 30, 15, 123456), |
| 100 | datetime(2018, 4, 16, 10, 0)), |
| 101 | (datetime(2018, 4, 16, 10, 0), |
| 102 | datetime(2018, 4, 16, 10, 0)), |
| 103 | (datetime(2018, 4, 16, 10, 30), |
| 104 | datetime(2018, 4, 23, 10, 0)), |
| 105 | (datetime(2018, 4, 14, 9, 30, tzinfo=tz.gettz('America/New_York')), |
| 106 | datetime(2018, 4, 16, 10, 0, tzinfo=tz.gettz('America/New_York'))), |
| 107 | ] |
| 108 | |
| 109 | def test_next_monday_1(): |
| 110 | for dt_in, dt_out in NEXT_MONDAY_CASES: |
| 111 | assert next_monday(dt_in) == dt_out |
| 112 | |
| 113 | if __name__ == "__main__": |
| 114 | test_next_monday_1() |
Paul Ganssle | 9435008 | 2018-06-08 07:14:30 +0100 | [diff] [blame] | 115 | print('Success!') |
Paul Ganssle | 404af91 | 2018-06-07 20:09:47 +0100 | [diff] [blame] | 116 | |
| 117 | .. raw:: html |
| 118 | |
| 119 | </details> |
| 120 | |
| 121 | |
Paul Ganssle | 9435008 | 2018-06-08 07:14:30 +0100 | [diff] [blame] | 122 | Parsing a local tzname |
| 123 | ---------------------- |
| 124 | |
daplantagenet | 121d5e0 | 2018-06-08 11:02:41 +0100 | [diff] [blame] | 125 | Three-character time zone abbreviations are *not* unique in that they do not explicitly map to a time zone. A list of time zone abbreviations in use can be found `here <https://www.timeanddate.com/time/zones/>`_. This means that parsing a datetime string such as ``'2018-01-01 12:30:30 CST'`` is ambiguous without context. Using `dateutil.parser <../parser.html>`_ and `dateutil.tz <../tz.html>`_, it is possible to provide a context such that these local names are converted to proper time zones. |
Paul Ganssle | 9435008 | 2018-06-08 07:14:30 +0100 | [diff] [blame] | 126 | |
| 127 | Problem 1 |
| 128 | ********* |
| 129 | Given the context that you will only be parsing dates coming from the continental United States, India and Japan, write a function that parses a datetime string and returns a timezone-aware ``datetime`` with an IANA-style timezone attached. |
| 130 | |
| 131 | Note: For the purposes of the experiment, you may ignore the portions of the United States like Arizona and parts of Indiana that do not observe daylight saving time. |
| 132 | |
| 133 | **Test Script** |
| 134 | |
| 135 | To solve this exercise, copy-paste this script into a document, change anything between the ``--- YOUR CODE ---`` comment blocks. |
| 136 | |
| 137 | .. raw:: html |
| 138 | |
| 139 | <details> |
| 140 | |
| 141 | |
| 142 | .. code-block:: python3 |
| 143 | |
| 144 | # --------- YOUR CODE -------------- # |
| 145 | from dateutil.parser import parse |
| 146 | from dateutil import tz |
| 147 | |
| 148 | def parse_func_us_jp_ind(): |
| 149 | <<YOUR CODE HERE>> |
| 150 | |
| 151 | # ---------------------------------- # |
| 152 | |
| 153 | from dateutil import tz |
| 154 | from datetime import datetime |
| 155 | |
| 156 | |
| 157 | PARSE_TZ_TEST_DATETIMES = [ |
| 158 | datetime(2018, 1, 1, 12, 0), |
| 159 | datetime(2018, 3, 20, 2, 0), |
| 160 | datetime(2018, 5, 12, 3, 30), |
| 161 | datetime(2014, 9, 1, 23) |
| 162 | ] |
| 163 | |
| 164 | PARSE_TZ_TEST_ZONES = [ |
| 165 | tz.gettz('America/New_York'), |
| 166 | tz.gettz('America/Chicago'), |
| 167 | tz.gettz('America/Denver'), |
| 168 | tz.gettz('America/Los_Angeles'), |
| 169 | tz.gettz('Asia/Kolkata'), |
| 170 | tz.gettz('Asia/Tokyo'), |
| 171 | ] |
| 172 | |
| 173 | def test_parse(): |
| 174 | for tzi in PARSE_TZ_TEST_ZONES: |
| 175 | for dt in PARSE_TZ_TEST_DATETIMES: |
| 176 | dt_exp = dt.replace(tzinfo=tzi) |
| 177 | dtstr = dt_exp.strftime('%Y-%m-%d %H:%M:%S %Z') |
| 178 | |
| 179 | dt_act = parse_func_us_jp_ind(dtstr) |
| 180 | assert dt_act == dt_exp |
| 181 | assert dt_act.tzinfo is dt_exp.tzinfo |
| 182 | |
| 183 | if __name__ == "__main__": |
| 184 | test_parse() |
| 185 | print('Success!') |
| 186 | |
| 187 | .. raw:: html |
| 188 | |
| 189 | </details> |
| 190 | |
| 191 | |
| 192 | Problem 2 |
| 193 | ********* |
| 194 | Given the context that you will *only* be passed dates from India or Ireland, write a function that correctly parses all *unambiguous* time zone strings to aware datetimes localized to the correct IANA zone, and for *ambiguous* time zone strings default to India. |
| 195 | |
| 196 | **Test Script** |
| 197 | |
| 198 | To solve this exercise, copy-paste this script into a document, change anything between the ``--- YOUR CODE ---`` comment blocks. |
| 199 | |
| 200 | |
| 201 | .. raw:: html |
| 202 | |
| 203 | <details> |
| 204 | |
| 205 | .. code-block:: python3 |
| 206 | |
| 207 | # --------- YOUR CODE -------------- # |
| 208 | from dateutil.parser import parse |
| 209 | from dateutil import tz |
| 210 | |
| 211 | def parse_func_ind_ire(): |
| 212 | <<YOUR CODE HERE>> |
| 213 | |
| 214 | # ---------------------------------- # |
| 215 | ISRAEL = tz.gettz('Asia/Jerusalem') |
| 216 | INDIA = tz.gettz('Asia/Kolkata') |
| 217 | PARSE_IXT_TEST_CASE = [ |
| 218 | ('2018-02-03 12:00 IST+02:00', datetime(2018, 2, 3, 12, tzinfo=ISRAEL)), |
| 219 | ('2018-06-14 12:00 IDT+03:00', datetime(2018, 6, 14, 12, tzinfo=ISRAEL)), |
| 220 | ('2018-06-14 12:00 IST', datetime(2018, 6, 14, 12, tzinfo=INDIA)), |
| 221 | ('2018-06-14 12:00 IST+05:30', datetime(2018, 6, 14, 12, tzinfo=INDIA)), |
| 222 | ('2018-02-03 12:00 IST', datetime(2018, 2, 3, 12, tzinfo=INDIA)), |
| 223 | ] |
| 224 | |
| 225 | |
| 226 | def test_parse_ixt(): |
| 227 | for dtstr, dt_exp in PARSE_IXT_TEST_CASE: |
| 228 | dt_act = parse_func_ind_ire(dtstr) |
| 229 | assert dt_act == dt_exp, (dt_act, dt_exp) |
| 230 | assert dt_act.tzinfo is dt_exp.tzinfo, (dt_act, dt_exp) |
| 231 | |
| 232 | if __name__ == "__main__": |
| 233 | test_parse_ixt() |
| 234 | print('Success!') |
| 235 | |
| 236 | .. raw:: html |
| 237 | |
| 238 | </details> |
| 239 | |