Simple pass-through of <Ruby> tags in subtitles.
[libdcp.git] / test / local_time_test.cc
1 /*
2     Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 #include "exceptions.h"
36 #include "local_time.h"
37 #include <boost/test/unit_test.hpp>
38
39
40 /** Check that dcp::LocalTime works */
41 BOOST_AUTO_TEST_CASE (local_time_basic_test)
42 {
43         /* Badly-formatted times */
44         BOOST_CHECK_THROW (dcp::LocalTime (""), dcp::TimeFormatError);
45         BOOST_CHECK_THROW (dcp::LocalTime ("XXX"), dcp::TimeFormatError);
46         BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59+04:0"), dcp::TimeFormatError);
47         BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59X04:00"), dcp::TimeFormatError);
48         BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18-06:59+04:00"), dcp::TimeFormatError);
49         BOOST_CHECK_THROW (dcp::LocalTime ("2013!01-05T18:06:59+04:00"), dcp::TimeFormatError);
50
51         /* Correctly-formatted */
52
53         {
54                 dcp::LocalTime t("2013-01-05T18:06:59");
55                 BOOST_CHECK_EQUAL(t._year, 2013);
56                 BOOST_CHECK_EQUAL(t._month, 1);
57                 BOOST_CHECK_EQUAL(t._day, 5);
58                 BOOST_CHECK_EQUAL(t._hour, 18);
59                 BOOST_CHECK_EQUAL(t._minute, 6);
60                 BOOST_CHECK_EQUAL(t._second, 59);
61                 BOOST_CHECK(t._offset == dcp::UTCOffset(0, 0));
62                 BOOST_CHECK_EQUAL(t.as_string(), "2013-01-05T18:06:59+00:00");
63         }
64
65         {
66                 dcp::LocalTime t ("2013-01-05T18:06:59+04:00");
67                 BOOST_CHECK_EQUAL (t._year, 2013);
68                 BOOST_CHECK_EQUAL (t._month, 1);
69                 BOOST_CHECK_EQUAL (t._day, 5);
70                 BOOST_CHECK_EQUAL (t._hour, 18);
71                 BOOST_CHECK_EQUAL (t._minute, 6);
72                 BOOST_CHECK_EQUAL (t._second, 59);
73                 BOOST_CHECK(t._offset == dcp::UTCOffset(4, 0));
74                 BOOST_CHECK_EQUAL (t.as_string(), "2013-01-05T18:06:59+04:00");
75         }
76
77         {
78                 dcp::LocalTime t ("2011-11-20T01:06:59-09:30");
79                 BOOST_CHECK_EQUAL (t._year, 2011);
80                 BOOST_CHECK_EQUAL (t._month, 11);
81                 BOOST_CHECK_EQUAL (t._day, 20);
82                 BOOST_CHECK_EQUAL (t._hour, 1);
83                 BOOST_CHECK_EQUAL (t._minute, 6);
84                 BOOST_CHECK_EQUAL (t._second, 59);
85                 BOOST_CHECK(t._offset == dcp::UTCOffset(-9, -30));
86                 BOOST_CHECK_EQUAL (t.as_string(), "2011-11-20T01:06:59-09:30");
87         }
88
89         {
90                 dcp::LocalTime t ("2011-11-20T01:06:59.456-09:30");
91                 BOOST_CHECK_EQUAL (t._year, 2011);
92                 BOOST_CHECK_EQUAL (t._month, 11);
93                 BOOST_CHECK_EQUAL (t._day, 20);
94                 BOOST_CHECK_EQUAL (t._hour, 1);
95                 BOOST_CHECK_EQUAL (t._minute, 6);
96                 BOOST_CHECK_EQUAL (t._second, 59);
97                 BOOST_CHECK_EQUAL (t._millisecond, 456);
98                 BOOST_CHECK(t._offset == dcp::UTCOffset(-9, -30));
99                 BOOST_CHECK_EQUAL (t.as_string(true), "2011-11-20T01:06:59.456-09:30");
100         }
101
102         {
103                 dcp::LocalTime t ("2011-11-20T01:06:59.456-09:30");
104                 BOOST_CHECK_EQUAL (t._year, 2011);
105                 BOOST_CHECK_EQUAL (t._month, 11);
106                 BOOST_CHECK_EQUAL (t._day, 20);
107                 BOOST_CHECK_EQUAL (t._hour, 1);
108                 BOOST_CHECK_EQUAL (t._minute, 6);
109                 BOOST_CHECK_EQUAL (t._second, 59);
110                 BOOST_CHECK_EQUAL (t._millisecond, 456);
111                 BOOST_CHECK(t._offset == dcp::UTCOffset(-9, -30));
112                 BOOST_CHECK_EQUAL (t.as_string(true, false), "2011-11-20T01:06:59.456");
113         }
114
115         {
116                 dcp::LocalTime t ("2011-11-20T01:06:59.456-09:30");
117                 BOOST_CHECK_EQUAL (t._year, 2011);
118                 BOOST_CHECK_EQUAL (t._month, 11);
119                 BOOST_CHECK_EQUAL (t._day, 20);
120                 BOOST_CHECK_EQUAL (t._hour, 1);
121                 BOOST_CHECK_EQUAL (t._minute, 6);
122                 BOOST_CHECK_EQUAL (t._second, 59);
123                 BOOST_CHECK_EQUAL (t._millisecond, 456);
124                 BOOST_CHECK(t._offset == dcp::UTCOffset(-9, -30));
125                 BOOST_CHECK_EQUAL (t.as_string(false, false), "2011-11-20T01:06:59");
126         }
127
128         {
129                 dcp::LocalTime t("2011-11-20T01:06:59.45678901-09:30");
130                 BOOST_CHECK_EQUAL(t._year, 2011);
131                 BOOST_CHECK_EQUAL(t._month, 11);
132                 BOOST_CHECK_EQUAL(t._day, 20);
133                 BOOST_CHECK_EQUAL(t._hour, 1);
134                 BOOST_CHECK_EQUAL(t._minute, 6);
135                 BOOST_CHECK_EQUAL(t._second, 59);
136                 /* The fractional seconds here is truncated rather than rounded, for better or worse */
137                 BOOST_CHECK_EQUAL(t._millisecond, 456);
138                 BOOST_CHECK(t._offset == dcp::UTCOffset(-9, -30));
139                 BOOST_CHECK_EQUAL(t.as_string(false, false), "2011-11-20T01:06:59");
140         }
141
142         {
143                 /* Construction from boost::posix_time::ptime */
144                 dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56"));
145                 BOOST_CHECK_EQUAL (b._year, 2002);
146                 BOOST_CHECK_EQUAL (b._month, 1);
147                 BOOST_CHECK_EQUAL (b._day, 20);
148                 BOOST_CHECK_EQUAL (b._hour, 19);
149                 BOOST_CHECK_EQUAL (b._minute, 3);
150                 BOOST_CHECK_EQUAL (b._second, 56);
151         }
152
153         {
154                 /* Construction from boost::posix_time::ptime with milliseconds */
155                 dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56.491"));
156                 BOOST_CHECK_EQUAL (b._year, 2002);
157                 BOOST_CHECK_EQUAL (b._month, 1);
158                 BOOST_CHECK_EQUAL (b._day, 20);
159                 BOOST_CHECK_EQUAL (b._hour, 19);
160                 BOOST_CHECK_EQUAL (b._minute, 3);
161                 BOOST_CHECK_EQUAL (b._second, 56);
162                 BOOST_CHECK_EQUAL (b._millisecond, 491);
163         }
164
165         {
166                 dcp::LocalTime b ("2015-11-18T19:26:45");
167                 BOOST_CHECK_EQUAL (b._year, 2015);
168                 BOOST_CHECK_EQUAL (b._month, 11);
169                 BOOST_CHECK_EQUAL (b._day, 18);
170                 BOOST_CHECK_EQUAL (b._hour, 19);
171                 BOOST_CHECK_EQUAL (b._minute, 26);
172                 BOOST_CHECK_EQUAL (b._second, 45);
173                 BOOST_CHECK_EQUAL (b._millisecond, 0);
174                 BOOST_CHECK(b._offset == dcp::UTCOffset());
175         }
176
177         /* Check negative times with non-zero timezone offset minutes */
178         {
179                 dcp::LocalTime t ("2013-01-05T18:06:59-04:30");
180                 BOOST_CHECK_EQUAL (t._year, 2013);
181                 BOOST_CHECK_EQUAL (t._month, 1);
182                 BOOST_CHECK_EQUAL (t._day, 5);
183                 BOOST_CHECK_EQUAL (t._hour, 18);
184                 BOOST_CHECK_EQUAL (t._minute, 6);
185                 BOOST_CHECK_EQUAL (t._second, 59);
186                 BOOST_CHECK(t._offset == dcp::UTCOffset(-4, -30));
187                 BOOST_CHECK_EQUAL (t.as_string(), "2013-01-05T18:06:59-04:30");
188         }
189
190         /* KDM seen with a TZ offset of -07:59, which we used to reject because
191          * we only accepted minutes between -30 and +30 (for some reason that I
192          * now can't find).
193          */
194         {
195                 dcp::LocalTime t("2023-11-30T23:59:00-07:59");
196                 BOOST_CHECK_EQUAL(t._year, 2023);
197                 BOOST_CHECK_EQUAL(t._month, 11);
198                 BOOST_CHECK_EQUAL(t._day, 30);
199                 BOOST_CHECK_EQUAL(t._hour, 23);
200                 BOOST_CHECK_EQUAL(t._minute, 59);
201                 BOOST_CHECK_EQUAL(t._second, 00);
202                 BOOST_CHECK(t._offset == dcp::UTCOffset(-7, -59));
203                 BOOST_CHECK_EQUAL(t.as_string(), "2023-11-30T23:59:00-07:59");
204         }
205 }
206
207
208 BOOST_AUTO_TEST_CASE (local_time_add_minutes_test)
209 {
210         {
211                 dcp::LocalTime t("2018-01-01T10:00:00+01:00");
212                 t.add_minutes (3);
213                 BOOST_CHECK_EQUAL (t.as_string(), "2018-01-01T10:03:00+01:00");
214         }
215
216         {
217                 dcp::LocalTime t("2018-01-01T10:00:15+01:00");
218                 t.add_minutes (3);
219                 BOOST_CHECK_EQUAL (t.as_string(), "2018-01-01T10:03:15+01:00");
220         }
221
222         {
223                 dcp::LocalTime t("2018-01-01T10:40:20+01:00");
224                 t.add_minutes (23);
225                 BOOST_CHECK_EQUAL (t.as_string(), "2018-01-01T11:03:20+01:00");
226         }
227
228         {
229                 dcp::LocalTime t("2018-01-01T10:40:20+01:00");
230                 t.add_minutes (123);
231                 BOOST_CHECK_EQUAL (t.as_string(), "2018-01-01T12:43:20+01:00");
232         }
233
234         {
235                 dcp::LocalTime t("2018-01-01T23:55:00+01:00");
236                 t.add_minutes (7);
237                 BOOST_CHECK_EQUAL (t.as_string(), "2018-01-02T00:02:00+01:00");
238         }
239
240         {
241                 dcp::LocalTime t("2018-01-31T23:55:00+01:00");
242                 t.add_minutes (7);
243                 BOOST_CHECK_EQUAL (t.as_string(), "2018-02-01T00:02:00+01:00");
244         }
245
246         {
247                 dcp::LocalTime t("2018-01-31T23:55:00.123");
248                 t.add_minutes (7);
249                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2018-02-01T00:02:00.123"));
250         }
251 }
252
253
254 BOOST_AUTO_TEST_CASE (local_time_add_months_test)
255 {
256         {
257                 dcp::LocalTime t("2013-06-23T18:06:59.123");
258                 t.add_months(-1);
259                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2013-05-23T18:06:59.123"));
260                 t.add_months(1);
261                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2013-06-23T18:06:59.123"));
262                 t.add_months(1);
263                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2013-07-23T18:06:59.123"));
264                 t.add_months(4);
265                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2013-11-23T18:06:59.123"));
266                 t.add_months(2);
267                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2014-01-23T18:06:59.123"));
268                 t.add_months(-14);
269                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2012-11-23T18:06:59.123"));
270                 t.add_months(14);
271                 BOOST_CHECK_EQUAL (t, dcp::LocalTime("2014-01-23T18:06:59.123"));
272         }
273
274         {
275                 dcp::LocalTime t("2018-01-30T11:00:00+01:00");
276                 t.add_months (1);
277                 BOOST_CHECK_EQUAL (t.as_string(), "2018-02-28T11:00:00+01:00");
278         }
279 }
280
281
282 BOOST_AUTO_TEST_CASE (local_time_from_asn1_utctime_test)
283 {
284         BOOST_CHECK_EQUAL (dcp::LocalTime::from_asn1_utc_time("991231235952").as_string(), "1999-12-31T23:59:52+00:00");
285         BOOST_CHECK_EQUAL (dcp::LocalTime::from_asn1_utc_time("210215165952").as_string(), "2021-02-15T16:59:52+00:00");
286 }
287
288
289 BOOST_AUTO_TEST_CASE (local_time_from_asn1_generalized_time_test)
290 {
291         BOOST_CHECK_EQUAL (dcp::LocalTime::from_asn1_generalized_time("19991231235952").as_string(), "1999-12-31T23:59:52+00:00");
292         BOOST_CHECK_EQUAL (dcp::LocalTime::from_asn1_generalized_time("20210215165952").as_string(), "2021-02-15T16:59:52+00:00");
293 }
294
295
296 BOOST_AUTO_TEST_CASE(local_time_comparison_test)
297 {
298         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") < dcp::LocalTime("2014-01-01T10:05:00"));
299         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") < dcp::LocalTime("2015-01-01T10:00:00"));
300         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") < dcp::LocalTime("2014-01-01T11:00:00"));
301         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00") < dcp::LocalTime("2014-10-10T10:00:01"));
302         BOOST_CHECK(!(dcp::LocalTime("2014-10-10T10:00:00") < dcp::LocalTime("2014-10-10T10:00:00")));
303         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00+01:00") < dcp::LocalTime("2014-10-10T10:00:00"));
304         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00+01:30") < dcp::LocalTime("2014-10-10T10:00:00"));
305         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00+01:00") < dcp::LocalTime("2014-10-10T10:00:01+01:00"));
306         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") < dcp::LocalTime("2014-01-01T10:05:00"));
307         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00") < dcp::LocalTime("2014-10-10T10:00:00-01:30"));
308
309         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:05:00") > dcp::LocalTime("2014-01-01T10:00:00"));
310         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00-01:30") > dcp::LocalTime("2014-10-10T10:00:00"));
311         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:05:00") > dcp::LocalTime("2014-01-01T10:00:00"));
312         BOOST_CHECK(dcp::LocalTime("2015-01-01T10:00:00") > dcp::LocalTime("2014-01-01T10:00:00"));
313         BOOST_CHECK(dcp::LocalTime("2014-01-01T11:00:00") > dcp::LocalTime("2014-01-01T10:00:00"));
314         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:01") > dcp::LocalTime("2014-10-10T10:00:00"));
315         BOOST_CHECK(!(dcp::LocalTime("2014-10-10T10:00:00") > dcp::LocalTime("2014-10-10T10:00:00")));
316         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00") > dcp::LocalTime("2014-10-10T10:00:00+01:00"));
317         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:00") > dcp::LocalTime("2014-10-10T10:00:00+01:30"));
318         BOOST_CHECK(dcp::LocalTime("2014-10-10T10:00:01+01:00") > dcp::LocalTime("2014-10-10T10:00:00+01:00"));
319
320         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") != dcp::LocalTime("2014-01-01T10:05:00"));
321         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00") == dcp::LocalTime("2014-01-01T10:00:00"));
322         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00+02:00") == dcp::LocalTime("2014-01-01T08:00:00"));
323         BOOST_CHECK(dcp::LocalTime("2014-01-01T10:00:00+02:00") == dcp::LocalTime("2014-01-01T11:00:00+03:00"));
324 }