1 /**
2  * Duration parsing functions.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.time.parsedur;
15 
16 import core.time;
17 
18 import std.algorithm.iteration : filter;
19 import std.array;
20 import std.ascii;
21 import std.conv;
22 import std.exception;
23 import std.string;
24 
25 /// Parse a duration string in the form returned by Duration.toString
26 /// (e.g. "1 day, 1 hour and 30 minutes")
27 Duration parseDuration(string s)
28 {
29 	s = s.replace(" and ", " ");
30 	s = s.replace(", ", " ");
31 	auto words = std..string.split(s).filter!(word => word.length);
32 	enforce(!words.empty, "No duration given");
33 
34 	Duration result;
35 
36 	while (!words.empty)
37 	{
38 		auto word = words.front;
39 		words.popFront();
40 		assert(word.length);
41 		enforce(word[0].isDigit || word[0] == '-', "Digit expected: " ~ s);
42 
43 		auto amount = std.conv.parse!real(word);
44 
45 		if (!word.length)
46 		{
47 			if (words.empty)
48 			{
49 				if (amount == 0)
50 					break;
51 				throw new Exception("Unit expected");
52 			}
53 			word = words.front;
54 			words.popFront();
55 		}
56 
57 		Duration unit;
58 
59 		word = word.toLower().replace("-", "");
60 		switch (word)
61 		{
62 			case "nanoseconds":
63 			case "nanosecond":
64 			case "nsecs":
65 			case "nsec":
66 				amount /= 100;
67 				unit = 1.hnsecs;
68 				break;
69 			case "hectonanoseconds":
70 			case "hectonanosecond":
71 			case "hnsecs":
72 			case "hns":
73 				unit = 1.hnsecs;
74 				break;
75 			case "microseconds":
76 			case "microsecond":
77 			case "usecs":
78 			case "usec":
79 			case "us":
80 			case "μsecs":
81 			case "μsec":
82 			case "μs":
83 				unit = 1.usecs;
84 				break;
85 			case "milliseconds":
86 			case "millisecond":
87 			case "msecs":
88 			case "msec":
89 			case "ms":
90 				unit = 1.msecs;
91 				break;
92 			case "seconds":
93 			case "second":
94 			case "secs":
95 			case "sec":
96 			case "s":
97 				unit = 1.seconds;
98 				break;
99 			case "minutes":
100 			case "minute":
101 			case "mins":
102 			case "min":
103 			case "m":
104 				unit = 1.minutes;
105 				break;
106 			case "hours":
107 			case "hour":
108 			case "h":
109 				unit = 1.hours;
110 				break;
111 			case "days":
112 			case "day":
113 			case "d":
114 				unit = 1.days;
115 				break;
116 			case "weeks":
117 			case "week":
118 			case "w":
119 				unit = 1.weeks;
120 				break;
121 			default:
122 				throw new Exception("Unknown unit: " ~ word);
123 		}
124 
125 		result += dur!"hnsecs"(cast(long)(unit.total!"hnsecs" * amount));
126 	}
127 
128 	return result;
129 }
130 
131 unittest
132 {
133 	assert(parseDuration("1 day, 1 hour and 30 minutes") == 1.days + 1.hours + 30.minutes);
134 	assert(parseDuration("0.5 hours") == 30.minutes);
135 	assert(parseDuration("0") == Duration.init);
136 }