1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.dbunit.util;
23
24 import java.time.Clock;
25 import java.time.Instant;
26 import java.time.LocalDateTime;
27 import java.time.LocalTime;
28 import java.time.ZoneId;
29 import java.time.temporal.ChronoUnit;
30 import java.time.temporal.TemporalUnit;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class RelativeDateTimeParser
68 {
69 private static final Pattern inputPattern = Pattern.compile(
70 "^\\[[nN][oO][wW]\\s*(([-+][0-9]+[yMdhms]\\s*)*)([0-9:]*)?\\]$");
71 private static final int GROUP_DIFFS = 1;
72 private static final int GROUP_TIME = 3;
73 private static final Pattern diffPattern =
74 Pattern.compile("([+-][0-9]+[yMdhms])");
75
76 private Clock clock;
77 private LocalDateTime now;
78
79 public RelativeDateTimeParser()
80 {
81
82 this(Clock.fixed(Instant.now(), ZoneId.systemDefault()));
83 }
84
85 public RelativeDateTimeParser(Clock clock)
86 {
87 this.clock = clock;
88 cacheLocalDateTime(clock);
89 }
90
91 public LocalDateTime parse(String input)
92 {
93 if (input == null || input.isEmpty())
94 {
95 throw new IllegalArgumentException(
96 "Relative datetime input must not be null or empty.");
97 }
98
99 Matcher matcher = inputPattern.matcher(input);
100 if (!matcher.matches())
101 {
102 throw new IllegalArgumentException("'" + input
103 + "' does not match the expected pattern [now{diff}{time}]. "
104 + "Please see the data types documentation for the details. "
105 + "http://dbunit.sourceforge.net/datatypes.html#relativedatetime");
106 }
107
108 LocalDateTime datetime = initLocalDateTime(matcher);
109
110 String diffStr = matcher.group(GROUP_DIFFS);
111 if (diffStr.isEmpty())
112 {
113 return datetime;
114 }
115
116 Matcher diffMatcher = diffPattern.matcher(diffStr);
117 while (diffMatcher.find())
118 {
119 String diff = diffMatcher.group();
120 int amountLength = diff.length() - 1;
121 TemporalUnit unit = resolveUnit(diff.charAt(amountLength));
122 long amount = Long.parseLong(diff.substring(0, amountLength));
123 datetime = datetime.plus(amount, unit);
124 }
125 return datetime;
126 }
127
128 public Clock getClock()
129 {
130 return clock;
131 }
132
133 public void setClock(Clock clock)
134 {
135 this.clock = clock;
136 cacheLocalDateTime(clock);
137 }
138
139 private LocalDateTime initLocalDateTime(Matcher matcher)
140 {
141 String timeStr = matcher.group(GROUP_TIME);
142 if (timeStr.isEmpty())
143 {
144 return now;
145 } else
146 {
147 LocalTime time = LocalTime.parse(timeStr);
148 return LocalDateTime.of(now.toLocalDate(), time);
149 }
150 }
151
152 private static TemporalUnit resolveUnit(char c)
153 {
154 switch (c)
155 {
156 case 'y':
157 return ChronoUnit.YEARS;
158 case 'M':
159 return ChronoUnit.MONTHS;
160 case 'd':
161 return ChronoUnit.DAYS;
162 case 'h':
163 return ChronoUnit.HOURS;
164 case 'm':
165 return ChronoUnit.MINUTES;
166 case 's':
167 return ChronoUnit.SECONDS;
168 default:
169 throw new IllegalArgumentException("'" + c
170 + "' is not a valid unit. It has to be one of 'yMdhms'.");
171 }
172 }
173
174 private void cacheLocalDateTime(Clock clock)
175 {
176 this.now = LocalDateTime.now(clock);
177 }
178 }