• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf.util;
32 
33 import com.google.protobuf.Duration;
34 import com.google.protobuf.Timestamp;
35 
36 import junit.framework.TestCase;
37 
38 import org.junit.Assert;
39 
40 import java.text.ParseException;
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 /** Unit tests for {@link TimeUtil}. */
45 public class TimeUtilTest extends TestCase {
testTimestampStringFormat()46   public void testTimestampStringFormat() throws Exception {
47     Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
48     Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
49     assertEquals(TimeUtil.TIMESTAMP_SECONDS_MIN, start.getSeconds());
50     assertEquals(0, start.getNanos());
51     assertEquals(TimeUtil.TIMESTAMP_SECONDS_MAX, end.getSeconds());
52     assertEquals(999999999, end.getNanos());
53     assertEquals("0001-01-01T00:00:00Z", TimeUtil.toString(start));
54     assertEquals("9999-12-31T23:59:59.999999999Z", TimeUtil.toString(end));
55 
56     Timestamp value = TimeUtil.parseTimestamp("1970-01-01T00:00:00Z");
57     assertEquals(0, value.getSeconds());
58     assertEquals(0, value.getNanos());
59 
60     // Test negative timestamps.
61     value = TimeUtil.parseTimestamp("1969-12-31T23:59:59.999Z");
62     assertEquals(-1, value.getSeconds());
63     // Nano part is in the range of [0, 999999999] for Timestamp.
64     assertEquals(999000000, value.getNanos());
65 
66     // Test that 3, 6, or 9 digits are used for the fractional part.
67     value = Timestamp.newBuilder().setNanos(10).build();
68     assertEquals("1970-01-01T00:00:00.000000010Z", TimeUtil.toString(value));
69     value = Timestamp.newBuilder().setNanos(10000).build();
70     assertEquals("1970-01-01T00:00:00.000010Z", TimeUtil.toString(value));
71     value = Timestamp.newBuilder().setNanos(10000000).build();
72     assertEquals("1970-01-01T00:00:00.010Z", TimeUtil.toString(value));
73 
74     // Test that parsing accepts timezone offsets.
75     value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010+08:00");
76     assertEquals("1969-12-31T16:00:00.010Z", TimeUtil.toString(value));
77     value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010-08:00");
78     assertEquals("1970-01-01T08:00:00.010Z", TimeUtil.toString(value));
79   }
80 
81   private volatile boolean stopParsingThreads = false;
82   private volatile String errorMessage = "";
83 
84   private class ParseTimestampThread extends Thread {
85     private final String[] strings;
86     private final Timestamp[] values;
ParseTimestampThread(String[] strings, Timestamp[] values)87     public ParseTimestampThread(String[] strings, Timestamp[] values) {
88       this.strings = strings;
89       this.values = values;
90     }
91 
92     @Override
run()93     public void run() {
94       int index = 0;
95       while (!stopParsingThreads) {
96         Timestamp result;
97         try {
98           result = TimeUtil.parseTimestamp(strings[index]);
99         } catch (ParseException e) {
100           errorMessage = "Failed to parse timestamp: " + strings[index];
101           break;
102         }
103         if (result.getSeconds() != values[index].getSeconds()
104             || result.getNanos() != values[index].getNanos()) {
105           errorMessage = "Actual result: " + result.toString() + ", expected: "
106               + values[index].toString();
107           break;
108         }
109         index = (index + 1) % strings.length;
110       }
111     }
112   }
113 
testTimestampConcurrentParsing()114   public void testTimestampConcurrentParsing() throws Exception {
115     String[] timestampStrings = new String[]{
116       "0001-01-01T00:00:00Z",
117       "9999-12-31T23:59:59.999999999Z",
118       "1970-01-01T00:00:00Z",
119       "1969-12-31T23:59:59.999Z",
120     };
121     Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
122     for (int i = 0; i < timestampStrings.length; i++) {
123       timestampValues[i] = TimeUtil.parseTimestamp(timestampStrings[i]);
124     }
125 
126     final int THREAD_COUNT = 16;
127     final int RUNNING_TIME = 5000;  // in milliseconds.
128     final List<Thread> threads = new ArrayList<Thread>();
129 
130     stopParsingThreads = false;
131     errorMessage = "";
132     for (int i = 0; i < THREAD_COUNT; i++) {
133       Thread thread = new ParseTimestampThread(
134           timestampStrings, timestampValues);
135       thread.start();
136       threads.add(thread);
137     }
138     Thread.sleep(RUNNING_TIME);
139     stopParsingThreads = true;
140     for (Thread thread : threads) {
141       thread.join();
142     }
143     Assert.assertEquals("", errorMessage);
144   }
145 
testTimetampInvalidFormat()146   public void testTimetampInvalidFormat() throws Exception {
147     try {
148       // Value too small.
149       Timestamp value = Timestamp.newBuilder()
150         .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build();
151       TimeUtil.toString(value);
152       Assert.fail("Exception is expected.");
153     } catch (IllegalArgumentException e) {
154       // Expected.
155     }
156 
157     try {
158       // Value too large.
159       Timestamp value = Timestamp.newBuilder()
160         .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build();
161       TimeUtil.toString(value);
162       Assert.fail("Exception is expected.");
163     } catch (IllegalArgumentException e) {
164       // Expected.
165     }
166 
167     try {
168       // Invalid nanos value.
169       Timestamp value = Timestamp.newBuilder().setNanos(-1).build();
170       TimeUtil.toString(value);
171       Assert.fail("Exception is expected.");
172     } catch (IllegalArgumentException e) {
173       // Expected.
174     }
175 
176     try {
177       // Invalid nanos value.
178       Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build();
179       TimeUtil.toString(value);
180       Assert.fail("Exception is expected.");
181     } catch (IllegalArgumentException e) {
182       // Expected.
183     }
184 
185     try {
186       // Value to small.
187       TimeUtil.parseTimestamp("0000-01-01T00:00:00Z");
188       Assert.fail("Exception is expected.");
189     } catch (ParseException e) {
190       // Expected.
191     }
192 
193     try {
194       // Value to large.
195       TimeUtil.parseTimestamp("10000-01-01T00:00:00Z");
196       Assert.fail("Exception is expected.");
197     } catch (ParseException e) {
198       // Expected.
199     }
200 
201     try {
202       // Missing 'T'.
203       TimeUtil.parseTimestamp("1970-01-01 00:00:00Z");
204       Assert.fail("Exception is expected.");
205     } catch (ParseException e) {
206       // Expected.
207     }
208 
209     try {
210       // Missing 'Z'.
211       TimeUtil.parseTimestamp("1970-01-01T00:00:00");
212       Assert.fail("Exception is expected.");
213     } catch (ParseException e) {
214       // Expected.
215     }
216 
217     try {
218       // Invalid offset.
219       TimeUtil.parseTimestamp("1970-01-01T00:00:00+0000");
220       Assert.fail("Exception is expected.");
221     } catch (ParseException e) {
222       // Expected.
223     }
224 
225     try {
226       // Trailing text.
227       TimeUtil.parseTimestamp("1970-01-01T00:00:00Z0");
228       Assert.fail("Exception is expected.");
229     } catch (ParseException e) {
230       // Expected.
231     }
232 
233     try {
234       // Invalid nanosecond value.
235       TimeUtil.parseTimestamp("1970-01-01T00:00:00.ABCZ");
236       Assert.fail("Exception is expected.");
237     } catch (ParseException e) {
238       // Expected.
239     }
240   }
241 
testDurationStringFormat()242   public void testDurationStringFormat() throws Exception {
243     Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
244     Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
245     Duration duration = TimeUtil.distance(start, end);
246     assertEquals("315537897599.999999999s", TimeUtil.toString(duration));
247     duration = TimeUtil.distance(end, start);
248     assertEquals("-315537897599.999999999s", TimeUtil.toString(duration));
249 
250     // Generated output should contain 3, 6, or 9 fractional digits.
251     duration = Duration.newBuilder().setSeconds(1).build();
252     assertEquals("1s", TimeUtil.toString(duration));
253     duration = Duration.newBuilder().setNanos(10000000).build();
254     assertEquals("0.010s", TimeUtil.toString(duration));
255     duration = Duration.newBuilder().setNanos(10000).build();
256     assertEquals("0.000010s", TimeUtil.toString(duration));
257     duration = Duration.newBuilder().setNanos(10).build();
258     assertEquals("0.000000010s", TimeUtil.toString(duration));
259 
260     // Parsing accepts an fractional digits as long as they fit into nano
261     // precision.
262     duration = TimeUtil.parseDuration("0.1s");
263     assertEquals(100000000, duration.getNanos());
264     duration = TimeUtil.parseDuration("0.0001s");
265     assertEquals(100000, duration.getNanos());
266     duration = TimeUtil.parseDuration("0.0000001s");
267     assertEquals(100, duration.getNanos());
268 
269     // Duration must support range from -315,576,000,000s to +315576000000s
270     // which includes negative values.
271     duration = TimeUtil.parseDuration("315576000000.999999999s");
272     assertEquals(315576000000L, duration.getSeconds());
273     assertEquals(999999999, duration.getNanos());
274     duration = TimeUtil.parseDuration("-315576000000.999999999s");
275     assertEquals(-315576000000L, duration.getSeconds());
276     assertEquals(-999999999, duration.getNanos());
277   }
278 
testDurationInvalidFormat()279   public void testDurationInvalidFormat() throws Exception {
280     try {
281       // Value too small.
282       Duration value = Duration.newBuilder()
283         .setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
284       TimeUtil.toString(value);
285       Assert.fail("Exception is expected.");
286     } catch (IllegalArgumentException e) {
287       // Expected.
288     }
289 
290     try {
291       // Value too large.
292       Duration value = Duration.newBuilder()
293         .setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build();
294       TimeUtil.toString(value);
295       Assert.fail("Exception is expected.");
296     } catch (IllegalArgumentException e) {
297       // Expected.
298     }
299 
300     try {
301       // Invalid nanos value.
302       Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1)
303           .build();
304       TimeUtil.toString(value);
305       Assert.fail("Exception is expected.");
306     } catch (IllegalArgumentException e) {
307       // Expected.
308     }
309 
310     try {
311       // Invalid nanos value.
312       Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1)
313           .build();
314       TimeUtil.toString(value);
315       Assert.fail("Exception is expected.");
316     } catch (IllegalArgumentException e) {
317       // Expected.
318     }
319 
320     try {
321       // Value too small.
322       TimeUtil.parseDuration("-315576000001s");
323       Assert.fail("Exception is expected.");
324     } catch (ParseException e) {
325       // Expected.
326     }
327 
328     try {
329       // Value too large.
330       TimeUtil.parseDuration("315576000001s");
331       Assert.fail("Exception is expected.");
332     } catch (ParseException e) {
333       // Expected.
334     }
335 
336     try {
337       // Empty.
338       TimeUtil.parseDuration("");
339       Assert.fail("Exception is expected.");
340     } catch (ParseException e) {
341       // Expected.
342     }
343 
344     try {
345       // Missing "s".
346       TimeUtil.parseDuration("0");
347       Assert.fail("Exception is expected.");
348     } catch (ParseException e) {
349       // Expected.
350     }
351 
352     try {
353       // Invalid trailing data.
354       TimeUtil.parseDuration("0s0");
355       Assert.fail("Exception is expected.");
356     } catch (ParseException e) {
357       // Expected.
358     }
359 
360     try {
361       // Invalid prefix.
362       TimeUtil.parseDuration("--1s");
363       Assert.fail("Exception is expected.");
364     } catch (ParseException e) {
365       // Expected.
366     }
367   }
368 
testTimestampConversion()369   public void testTimestampConversion() throws Exception {
370     Timestamp timestamp =
371       TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z");
372     assertEquals(1111111111, TimeUtil.toNanos(timestamp));
373     assertEquals(1111111, TimeUtil.toMicros(timestamp));
374     assertEquals(1111, TimeUtil.toMillis(timestamp));
375     timestamp = TimeUtil.createTimestampFromNanos(1111111111);
376     assertEquals("1970-01-01T00:00:01.111111111Z", TimeUtil.toString(timestamp));
377     timestamp = TimeUtil.createTimestampFromMicros(1111111);
378     assertEquals("1970-01-01T00:00:01.111111Z", TimeUtil.toString(timestamp));
379     timestamp = TimeUtil.createTimestampFromMillis(1111);
380     assertEquals("1970-01-01T00:00:01.111Z", TimeUtil.toString(timestamp));
381 
382     timestamp = TimeUtil.parseTimestamp("1969-12-31T23:59:59.111111111Z");
383     assertEquals(-888888889, TimeUtil.toNanos(timestamp));
384     assertEquals(-888889, TimeUtil.toMicros(timestamp));
385     assertEquals(-889, TimeUtil.toMillis(timestamp));
386     timestamp = TimeUtil.createTimestampFromNanos(-888888889);
387     assertEquals("1969-12-31T23:59:59.111111111Z", TimeUtil.toString(timestamp));
388     timestamp = TimeUtil.createTimestampFromMicros(-888889);
389     assertEquals("1969-12-31T23:59:59.111111Z", TimeUtil.toString(timestamp));
390     timestamp = TimeUtil.createTimestampFromMillis(-889);
391     assertEquals("1969-12-31T23:59:59.111Z", TimeUtil.toString(timestamp));
392   }
393 
testDurationConversion()394   public void testDurationConversion() throws Exception {
395     Duration duration = TimeUtil.parseDuration("1.111111111s");
396     assertEquals(1111111111, TimeUtil.toNanos(duration));
397     assertEquals(1111111, TimeUtil.toMicros(duration));
398     assertEquals(1111, TimeUtil.toMillis(duration));
399     duration = TimeUtil.createDurationFromNanos(1111111111);
400     assertEquals("1.111111111s", TimeUtil.toString(duration));
401     duration = TimeUtil.createDurationFromMicros(1111111);
402     assertEquals("1.111111s", TimeUtil.toString(duration));
403     duration = TimeUtil.createDurationFromMillis(1111);
404     assertEquals("1.111s", TimeUtil.toString(duration));
405 
406     duration = TimeUtil.parseDuration("-1.111111111s");
407     assertEquals(-1111111111, TimeUtil.toNanos(duration));
408     assertEquals(-1111111, TimeUtil.toMicros(duration));
409     assertEquals(-1111, TimeUtil.toMillis(duration));
410     duration = TimeUtil.createDurationFromNanos(-1111111111);
411     assertEquals("-1.111111111s", TimeUtil.toString(duration));
412     duration = TimeUtil.createDurationFromMicros(-1111111);
413     assertEquals("-1.111111s", TimeUtil.toString(duration));
414     duration = TimeUtil.createDurationFromMillis(-1111);
415     assertEquals("-1.111s", TimeUtil.toString(duration));
416   }
417 
testTimeOperations()418   public void testTimeOperations() throws Exception {
419     Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
420     Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
421 
422     Duration duration = TimeUtil.distance(start, end);
423     assertEquals("315537897599.999999999s", TimeUtil.toString(duration));
424     Timestamp value = TimeUtil.add(start, duration);
425     assertEquals(end, value);
426     value = TimeUtil.subtract(end, duration);
427     assertEquals(start, value);
428 
429     duration = TimeUtil.distance(end, start);
430     assertEquals("-315537897599.999999999s", TimeUtil.toString(duration));
431     value = TimeUtil.add(end, duration);
432     assertEquals(start, value);
433     value = TimeUtil.subtract(start, duration);
434     assertEquals(end, value);
435 
436     // Result is larger than Long.MAX_VALUE.
437     try {
438       duration = TimeUtil.parseDuration("315537897599.999999999s");
439       duration = TimeUtil.multiply(duration, 315537897599.999999999);
440       Assert.fail("Exception is expected.");
441     } catch (IllegalArgumentException e) {
442       // Expected.
443     }
444 
445     // Result is lesser than Long.MIN_VALUE.
446     try {
447       duration = TimeUtil.parseDuration("315537897599.999999999s");
448       duration = TimeUtil.multiply(duration, -315537897599.999999999);
449       Assert.fail("Exception is expected.");
450     } catch (IllegalArgumentException e) {
451       // Expected.
452     }
453 
454     duration = TimeUtil.parseDuration("-1.125s");
455     duration = TimeUtil.divide(duration, 2.0);
456     assertEquals("-0.562500s", TimeUtil.toString(duration));
457     duration = TimeUtil.multiply(duration, 2.0);
458     assertEquals("-1.125s", TimeUtil.toString(duration));
459 
460     duration = TimeUtil.add(duration, duration);
461     assertEquals("-2.250s", TimeUtil.toString(duration));
462 
463     duration = TimeUtil.subtract(duration, TimeUtil.parseDuration("-1s"));
464     assertEquals("-1.250s", TimeUtil.toString(duration));
465 
466     // Multiplications (with results larger than Long.MAX_VALUE in nanoseconds).
467     duration = TimeUtil.parseDuration("0.999999999s");
468     assertEquals("315575999684.424s",
469       TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
470     duration = TimeUtil.parseDuration("-0.999999999s");
471     assertEquals("-315575999684.424s",
472       TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
473     assertEquals("315575999684.424s",
474       TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L)));
475 
476     // Divisions (with values larger than Long.MAX_VALUE in nanoseconds).
477     Duration d1 = TimeUtil.parseDuration("315576000000s");
478     Duration d2 = TimeUtil.subtract(d1, TimeUtil.createDurationFromNanos(1));
479     assertEquals(1, TimeUtil.divide(d1, d2));
480     assertEquals(0, TimeUtil.divide(d2, d1));
481     assertEquals("0.000000001s", TimeUtil.toString(TimeUtil.remainder(d1, d2)));
482     assertEquals("315575999999.999999999s",
483       TimeUtil.toString(TimeUtil.remainder(d2, d1)));
484 
485     // Divisions involving negative values.
486     //
487     // (-5) / 2 = -2, remainder = -1
488     d1 = TimeUtil.parseDuration("-5s");
489     d2 = TimeUtil.parseDuration("2s");
490     assertEquals(-2, TimeUtil.divide(d1, d2));
491     assertEquals(-2, TimeUtil.divide(d1, 2).getSeconds());
492     assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds());
493     // (-5) / (-2) = 2, remainder = -1
494     d1 = TimeUtil.parseDuration("-5s");
495     d2 = TimeUtil.parseDuration("-2s");
496     assertEquals(2, TimeUtil.divide(d1, d2));
497     assertEquals(2, TimeUtil.divide(d1, -2).getSeconds());
498     assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds());
499     // 5 / (-2) = -2, remainder = 1
500     d1 = TimeUtil.parseDuration("5s");
501     d2 = TimeUtil.parseDuration("-2s");
502     assertEquals(-2, TimeUtil.divide(d1, d2));
503     assertEquals(-2, TimeUtil.divide(d1, -2).getSeconds());
504     assertEquals(1, TimeUtil.remainder(d1, d2).getSeconds());
505   }
506 }
507