6
6
package kotlinx.datetime.internal.format.parser
7
7
8
8
import kotlinx.datetime.internal.isAsciiDigit
9
+ import kotlinx.datetime.internal.isAsciiLetter
9
10
10
11
internal interface ParserOperation <in Output > {
11
12
fun consume (storage : Output , input : CharSequence , startIndex : Int ): ParseResult
@@ -142,9 +143,8 @@ internal class UnconditionalModification<Output>(
142
143
internal class TimeZoneParserOperation <Output >(
143
144
private val setter : AssignableField <Output , String >
144
145
) : ParserOperation<Output> {
145
-
146
146
override fun consume (storage : Output , input : CharSequence , startIndex : Int ): ParseResult {
147
- val lastMatch = validateTimezone (input, startIndex)
147
+ val lastMatch = validateTimeZone (input, startIndex)
148
148
return if (lastMatch > startIndex) {
149
149
setter.setWithoutReassigning(storage, input.substring(startIndex, lastMatch), startIndex, lastMatch)
150
150
ParseResult .Ok (lastMatch)
@@ -158,95 +158,106 @@ internal class TimeZoneParserOperation<Output>(
158
158
START ,
159
159
AFTER_PREFIX ,
160
160
AFTER_SIGN ,
161
+ AFTER_INIT_SIGN ,
161
162
AFTER_HOUR ,
163
+ AFTER_INIT_HOUR ,
162
164
AFTER_MINUTE ,
163
165
AFTER_COLON_MINUTE ,
164
- END ,
165
- INVALID
166
+ IN_PART ,
167
+ AFTER_SLASH ,
168
+ END
166
169
}
167
170
168
- private fun validateTimezone (input : CharSequence , startIndex : Int ): Int {
171
+ private inline fun Boolean.onTrue (action : () -> Unit ): Boolean = if (this ) { action(); true } else false
172
+
173
+ private inline fun Boolean.onFalse (action : () -> Unit ): Boolean = if (this ) true else { action(); false }
174
+
175
+ private fun validateTimeZone (input : CharSequence , startIndex : Int ): Int {
169
176
var index = startIndex
170
- var lastValidIndex = startIndex
171
177
172
178
fun validatePrefix (validValues : List <String >): Boolean =
173
- validValues.firstOrNull { input.startsWith(it) }?.let {
174
- index + = it.length
175
- lastValidIndex = index
176
- true
177
- } ? : false
178
-
179
- fun validateTimeComponent (length : Int ): Boolean {
180
- if ((index.. < (index + length)).all { input.getOrNull(it)?.isAsciiDigit() ? : false }) {
181
- index + = length
182
- lastValidIndex = index
183
- return true
184
- }
185
- return false
186
- }
179
+ validValues.firstOrNull { input.startsWith(it, index) }?.also { index + = it.length } != null
180
+
181
+ fun validateSign (): Boolean = (input[index] in listOf (' +' , ' -' )).onTrue { index++ }
182
+
183
+ fun validateTimeComponent (length : Int ): Boolean =
184
+ (index.. < (index + length))
185
+ .all { input.getOrNull(it)?.isAsciiDigit() ? : false }
186
+ .onTrue { index + = length }
187
+
188
+ fun validateTimeComponentWithColon (): Boolean =
189
+ (input[index] == ' :' ).onTrue { index++ } && validateTimeComponent(2 ).onFalse { index-- }
190
+
191
+ fun Char.isTimeZoneInitial (): Boolean = isAsciiLetter() || this == ' .' || this == ' _'
192
+ fun Char.isTimeZoneChar (): Boolean = isTimeZoneInitial() || isAsciiDigit() || this == ' -' || this == ' +'
193
+
194
+ fun validateTimeZoneInitial (): Boolean = input[index].isTimeZoneInitial().onTrue { index++ }
195
+ fun validateTimeZoneChar (): Boolean = input[index].isTimeZoneChar().onTrue { index++ }
196
+ fun validateSlash (): Boolean = (input[index] == ' /' ).onTrue { index++ }
187
197
188
198
var state = State .START
189
199
while (index < input.length) {
190
200
state = when (state) {
191
201
State .START -> when {
192
- input[index] == ' Z' || input[index] == ' z' -> {
193
- index++
194
- State .END
195
- }
196
-
197
- input[index] in listOf (' +' , ' -' ) -> {
198
- index++
199
- State .AFTER_SIGN
200
- }
201
-
202
202
validatePrefix(listOf (" UTC" , " GMT" , " UT" )) -> State .AFTER_PREFIX
203
- else -> State .INVALID
203
+ validateSign() -> State .AFTER_INIT_SIGN
204
+ validateTimeZoneInitial() -> State .IN_PART
205
+ else -> break
204
206
}
205
207
206
208
State .AFTER_PREFIX -> when {
207
- input[index] in listOf (' +' , ' -' ) -> {
208
- index++
209
- State .AFTER_SIGN
210
- }
211
-
212
- else -> State .INVALID
209
+ validateSign() -> State .AFTER_SIGN
210
+ else -> State .IN_PART
213
211
}
214
212
215
213
State .AFTER_SIGN -> when {
216
214
validateTimeComponent(2 ) -> State .AFTER_HOUR
215
+ else -> State .IN_PART
216
+ }
217
+
218
+ State .AFTER_INIT_SIGN -> when {
219
+ validateTimeComponent(2 ) -> State .AFTER_INIT_HOUR
217
220
validateTimeComponent(1 ) -> State .END
218
- else -> State . INVALID
221
+ else -> break
219
222
}
220
223
221
224
State .AFTER_HOUR -> when {
222
- input[index] == ' :' -> {
223
- index++
224
- if (validateTimeComponent(2 )) State .AFTER_COLON_MINUTE else State .INVALID
225
- }
225
+ validateTimeComponentWithColon() -> State .AFTER_COLON_MINUTE
226
+ else -> State .IN_PART
227
+ }
226
228
229
+ State .AFTER_INIT_HOUR -> when {
230
+ validateTimeComponentWithColon() -> State .AFTER_COLON_MINUTE
227
231
validateTimeComponent(2 ) -> State .AFTER_MINUTE
228
- else -> State . INVALID
232
+ else -> break
229
233
}
230
234
231
235
State .AFTER_MINUTE -> when {
232
236
validateTimeComponent(2 ) -> State .END
233
- else -> State . INVALID
237
+ else -> break
234
238
}
235
239
236
240
State .AFTER_COLON_MINUTE -> when {
237
- input[index] == ' :' -> {
238
- index++
239
- if (validateTimeComponent(2 )) State .END else State .INVALID
240
- }
241
+ validateTimeComponentWithColon() -> State .END
242
+ else -> break
243
+ }
244
+
245
+ State .IN_PART -> when {
246
+ validateTimeZoneChar() -> State .IN_PART
247
+ validateSlash() -> State .AFTER_SLASH
248
+ else -> break
249
+ }
241
250
242
- else -> State .INVALID
251
+ State .AFTER_SLASH -> when {
252
+ validateTimeZoneInitial() -> State .IN_PART
253
+ else -> break
243
254
}
244
255
245
- State .END , State . INVALID -> break
256
+ State .END -> break
246
257
}
247
258
}
248
259
249
- return if (state == State .END ) index else lastValidIndex
260
+ return index - if (state == State .AFTER_SLASH || state == State . AFTER_INIT_SIGN ) 1 else 0
250
261
}
251
262
}
252
263
}
0 commit comments