306 |
317 |
307 //----------------------------------------------------------------------- |
318 //----------------------------------------------------------------------- |
308 private void resolveTimeFields() { |
319 private void resolveTimeFields() { |
309 // simplify fields |
320 // simplify fields |
310 if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) { |
321 if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) { |
|
322 // lenient allows anything, smart allows 0-24, strict allows 1-24 |
311 long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY); |
323 long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY); |
|
324 if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) { |
|
325 CLOCK_HOUR_OF_DAY.checkValidValue(ch); |
|
326 } |
312 updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch); |
327 updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch); |
313 } |
328 } |
314 if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) { |
329 if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) { |
|
330 // lenient allows anything, smart allows 0-12, strict allows 1-12 |
315 long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM); |
331 long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM); |
|
332 if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) { |
|
333 CLOCK_HOUR_OF_AMPM.checkValidValue(ch); |
|
334 } |
316 updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch); |
335 updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch); |
317 } |
336 } |
318 if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) { |
337 if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) { |
319 long ap = fieldValues.remove(AMPM_OF_DAY); |
338 long ap = fieldValues.remove(AMPM_OF_DAY); |
320 long hap = fieldValues.remove(HOUR_OF_AMPM); |
339 long hap = fieldValues.remove(HOUR_OF_AMPM); |
321 updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap); |
340 if (resolverStyle == ResolverStyle.LENIENT) { |
|
341 updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, Math.addExact(Math.multiplyExact(ap, 12), hap)); |
|
342 } else { // STRICT or SMART |
|
343 AMPM_OF_DAY.checkValidValue(ap); |
|
344 HOUR_OF_AMPM.checkValidValue(ap); |
|
345 updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap); |
|
346 } |
|
347 } |
|
348 if (fieldValues.containsKey(NANO_OF_DAY)) { |
|
349 long nod = fieldValues.remove(NANO_OF_DAY); |
|
350 if (resolverStyle != ResolverStyle.LENIENT) { |
|
351 NANO_OF_DAY.checkValidValue(nod); |
|
352 } |
|
353 updateCheckConflict(NANO_OF_DAY, HOUR_OF_DAY, nod / 3600_000_000_000L); |
|
354 updateCheckConflict(NANO_OF_DAY, MINUTE_OF_HOUR, (nod / 60_000_000_000L) % 60); |
|
355 updateCheckConflict(NANO_OF_DAY, SECOND_OF_MINUTE, (nod / 1_000_000_000L) % 60); |
|
356 updateCheckConflict(NANO_OF_DAY, NANO_OF_SECOND, nod % 1_000_000_000L); |
322 } |
357 } |
323 if (fieldValues.containsKey(MICRO_OF_DAY)) { |
358 if (fieldValues.containsKey(MICRO_OF_DAY)) { |
324 long cod = fieldValues.remove(MICRO_OF_DAY); |
359 long cod = fieldValues.remove(MICRO_OF_DAY); |
|
360 if (resolverStyle != ResolverStyle.LENIENT) { |
|
361 MICRO_OF_DAY.checkValidValue(cod); |
|
362 } |
325 updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L); |
363 updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L); |
326 updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L); |
364 updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L); |
327 } |
365 } |
328 if (fieldValues.containsKey(MILLI_OF_DAY)) { |
366 if (fieldValues.containsKey(MILLI_OF_DAY)) { |
329 long lod = fieldValues.remove(MILLI_OF_DAY); |
367 long lod = fieldValues.remove(MILLI_OF_DAY); |
|
368 if (resolverStyle != ResolverStyle.LENIENT) { |
|
369 MILLI_OF_DAY.checkValidValue(lod); |
|
370 } |
330 updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000); |
371 updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000); |
331 updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000); |
372 updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000); |
332 } |
373 } |
333 if (fieldValues.containsKey(SECOND_OF_DAY)) { |
374 if (fieldValues.containsKey(SECOND_OF_DAY)) { |
334 long sod = fieldValues.remove(SECOND_OF_DAY); |
375 long sod = fieldValues.remove(SECOND_OF_DAY); |
|
376 if (resolverStyle != ResolverStyle.LENIENT) { |
|
377 SECOND_OF_DAY.checkValidValue(sod); |
|
378 } |
335 updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600); |
379 updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600); |
336 updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60); |
380 updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60); |
337 updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60); |
381 updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60); |
338 } |
382 } |
339 if (fieldValues.containsKey(MINUTE_OF_DAY)) { |
383 if (fieldValues.containsKey(MINUTE_OF_DAY)) { |
340 long mod = fieldValues.remove(MINUTE_OF_DAY); |
384 long mod = fieldValues.remove(MINUTE_OF_DAY); |
|
385 if (resolverStyle != ResolverStyle.LENIENT) { |
|
386 MINUTE_OF_DAY.checkValidValue(mod); |
|
387 } |
341 updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60); |
388 updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60); |
342 updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60); |
389 updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60); |
343 } |
390 } |
344 |
391 |
345 // combine partial second fields strictly, leaving lenient expansion to later |
392 // combine partial second fields strictly, leaving lenient expansion to later |
346 if (fieldValues.containsKey(NANO_OF_SECOND)) { |
393 if (fieldValues.containsKey(NANO_OF_SECOND)) { |
347 long nos = fieldValues.get(NANO_OF_SECOND); |
394 long nos = fieldValues.get(NANO_OF_SECOND); |
|
395 if (resolverStyle != ResolverStyle.LENIENT) { |
|
396 NANO_OF_SECOND.checkValidValue(nos); |
|
397 } |
348 if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
398 if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
349 long cos = fieldValues.remove(MICRO_OF_SECOND); |
399 long cos = fieldValues.remove(MICRO_OF_SECOND); |
|
400 if (resolverStyle != ResolverStyle.LENIENT) { |
|
401 MICRO_OF_SECOND.checkValidValue(cos); |
|
402 } |
350 nos = cos * 1000 + (nos % 1000); |
403 nos = cos * 1000 + (nos % 1000); |
351 updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos); |
404 updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos); |
352 } |
405 } |
353 if (fieldValues.containsKey(MILLI_OF_SECOND)) { |
406 if (fieldValues.containsKey(MILLI_OF_SECOND)) { |
354 long los = fieldValues.remove(MILLI_OF_SECOND); |
407 long los = fieldValues.remove(MILLI_OF_SECOND); |
|
408 if (resolverStyle != ResolverStyle.LENIENT) { |
|
409 MILLI_OF_SECOND.checkValidValue(los); |
|
410 } |
355 updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L)); |
411 updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L)); |
356 } |
412 } |
357 } |
413 } |
358 |
414 |
359 // convert to time if possible |
415 // convert to time if all four fields available (optimization) |
360 if (fieldValues.containsKey(NANO_OF_DAY)) { |
|
361 long nod = fieldValues.remove(NANO_OF_DAY); |
|
362 updateCheckConflict(LocalTime.ofNanoOfDay(nod)); |
|
363 } |
|
364 if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) && |
416 if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) && |
365 fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) { |
417 fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) { |
366 int hodVal = HOUR_OF_DAY.checkValidIntValue(fieldValues.remove(HOUR_OF_DAY)); |
418 long hod = fieldValues.remove(HOUR_OF_DAY); |
367 int mohVal = MINUTE_OF_HOUR.checkValidIntValue(fieldValues.remove(MINUTE_OF_HOUR)); |
419 long moh = fieldValues.remove(MINUTE_OF_HOUR); |
368 int somVal = SECOND_OF_MINUTE.checkValidIntValue(fieldValues.remove(SECOND_OF_MINUTE)); |
420 long som = fieldValues.remove(SECOND_OF_MINUTE); |
369 int nosVal = NANO_OF_SECOND.checkValidIntValue(fieldValues.remove(NANO_OF_SECOND)); |
421 long nos = fieldValues.remove(NANO_OF_SECOND); |
370 updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal)); |
422 resolveTime(hod, moh, som, nos); |
371 } |
423 } |
372 } |
424 } |
373 |
425 |
374 private void resolveTimeLenient() { |
426 private void resolveTimeLenient() { |
375 // leniently create a time from incomplete information |
427 // leniently create a time from incomplete information |
376 // done after everything else as it creates information from nothing |
428 // done after everything else as it creates information from nothing |
377 // which would break updateCheckConflict(field) |
429 // which would break updateCheckConflict(field) |
378 |
430 |
379 if (time == null) { |
431 if (time == null) { |
380 // can only get here if NANO_OF_SECOND not present |
432 // NANO_OF_SECOND merged with MILLI/MICRO above |
381 if (fieldValues.containsKey(MILLI_OF_SECOND)) { |
433 if (fieldValues.containsKey(MILLI_OF_SECOND)) { |
382 long los = fieldValues.remove(MILLI_OF_SECOND); |
434 long los = fieldValues.remove(MILLI_OF_SECOND); |
383 if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
435 if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
384 // merge milli-of-second and micro-of-second for better error message |
436 // merge milli-of-second and micro-of-second for better error message |
385 long cos = los * 1_000 + (fieldValues.get(MICRO_OF_SECOND) % 1_000); |
437 long cos = los * 1_000 + (fieldValues.get(MICRO_OF_SECOND) % 1_000); |
393 } else if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
445 } else if (fieldValues.containsKey(MICRO_OF_SECOND)) { |
394 // convert micro-of-second to nano-of-second |
446 // convert micro-of-second to nano-of-second |
395 long cos = fieldValues.remove(MICRO_OF_SECOND); |
447 long cos = fieldValues.remove(MICRO_OF_SECOND); |
396 fieldValues.put(NANO_OF_SECOND, cos * 1_000L); |
448 fieldValues.put(NANO_OF_SECOND, cos * 1_000L); |
397 } |
449 } |
398 } |
450 |
399 |
451 // merge hour/minute/second/nano leniently |
400 // merge hour/minute/second/nano leniently |
452 Long hod = fieldValues.get(HOUR_OF_DAY); |
401 Long hod = fieldValues.get(HOUR_OF_DAY); |
453 if (hod != null) { |
402 if (hod != null) { |
454 Long moh = fieldValues.get(MINUTE_OF_HOUR); |
403 int hodVal = HOUR_OF_DAY.checkValidIntValue(hod); |
455 Long som = fieldValues.get(SECOND_OF_MINUTE); |
404 Long moh = fieldValues.get(MINUTE_OF_HOUR); |
456 Long nos = fieldValues.get(NANO_OF_SECOND); |
405 Long som = fieldValues.get(SECOND_OF_MINUTE); |
457 |
406 Long nos = fieldValues.get(NANO_OF_SECOND); |
458 // check for invalid combinations that cannot be defaulted |
407 |
|
408 // check for invalid combinations that cannot be defaulted |
|
409 if (time == null) { |
|
410 if ((moh == null && (som != null || nos != null)) || |
459 if ((moh == null && (som != null || nos != null)) || |
411 (moh != null && som == null && nos != null)) { |
460 (moh != null && som == null && nos != null)) { |
412 return; |
461 return; |
413 } |
462 } |
414 } |
463 |
415 |
464 // default as necessary and build time |
416 // default as necessary and build time |
465 long mohVal = (moh != null ? moh : 0); |
417 int mohVal = (moh != null ? MINUTE_OF_HOUR.checkValidIntValue(moh) : (time != null ? time.getMinute() : 0)); |
466 long somVal = (som != null ? som : 0); |
418 int somVal = (som != null ? SECOND_OF_MINUTE.checkValidIntValue(som) : (time != null ? time.getSecond() : 0)); |
467 long nosVal = (nos != null ? nos : 0); |
419 int nosVal = (nos != null ? NANO_OF_SECOND.checkValidIntValue(nos) : (time != null ? time.getNano() : 0)); |
468 resolveTime(hod, mohVal, somVal, nosVal); |
420 updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal)); |
469 fieldValues.remove(HOUR_OF_DAY); |
421 fieldValues.remove(HOUR_OF_DAY); |
470 fieldValues.remove(MINUTE_OF_HOUR); |
422 fieldValues.remove(MINUTE_OF_HOUR); |
471 fieldValues.remove(SECOND_OF_MINUTE); |
423 fieldValues.remove(SECOND_OF_MINUTE); |
472 fieldValues.remove(NANO_OF_SECOND); |
424 fieldValues.remove(NANO_OF_SECOND); |
473 } |
425 } |
474 } |
426 } |
475 |
427 |
476 // validate remaining |
428 private void updateCheckConflict(LocalTime lt) { |
477 if (resolverStyle != ResolverStyle.LENIENT && fieldValues.size() > 0) { |
|
478 for (Entry<TemporalField, Long> entry : fieldValues.entrySet()) { |
|
479 TemporalField field = entry.getKey(); |
|
480 if (field instanceof ChronoField && field.isTimeBased()) { |
|
481 ((ChronoField) field).checkValidValue(entry.getValue()); |
|
482 } |
|
483 } |
|
484 } |
|
485 } |
|
486 |
|
487 private void resolveTime(long hod, long moh, long som, long nos) { |
|
488 if (resolverStyle == ResolverStyle.LENIENT) { |
|
489 long totalNanos = Math.multiplyExact(hod, 3600_000_000_000L); |
|
490 totalNanos = Math.addExact(totalNanos, Math.multiplyExact(moh, 60_000_000_000L)); |
|
491 totalNanos = Math.addExact(totalNanos, Math.multiplyExact(som, 1_000_000_000L)); |
|
492 totalNanos = Math.addExact(totalNanos, nos); |
|
493 int excessDays = (int) Math.floorDiv(totalNanos, 86400_000_000_000L); // safe int cast |
|
494 long nod = Math.floorMod(totalNanos, 86400_000_000_000L); |
|
495 updateCheckConflict(LocalTime.ofNanoOfDay(nod), Period.ofDays(excessDays)); |
|
496 } else { // STRICT or SMART |
|
497 int mohVal = MINUTE_OF_HOUR.checkValidIntValue(moh); |
|
498 int nosVal = NANO_OF_SECOND.checkValidIntValue(nos); |
|
499 // handle 24:00 end of day |
|
500 if (resolverStyle == ResolverStyle.SMART && hod == 24 && mohVal == 0 && som == 0 && nosVal == 0) { |
|
501 updateCheckConflict(LocalTime.MIDNIGHT, Period.ofDays(1)); |
|
502 } else { |
|
503 int hodVal = HOUR_OF_DAY.checkValidIntValue(hod); |
|
504 int somVal = SECOND_OF_MINUTE.checkValidIntValue(som); |
|
505 updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal), Period.ZERO); |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 private void resolvePeriod() { |
|
511 // add whole days if we have both date and time |
|
512 if (date != null && time != null && excessDays.isZero() == false) { |
|
513 date = date.plus(excessDays); |
|
514 excessDays = Period.ZERO; |
|
515 } |
|
516 } |
|
517 |
|
518 private void updateCheckConflict(LocalTime timeToSet, Period periodToSet) { |
429 if (time != null) { |
519 if (time != null) { |
430 if (lt != null && time.equals(lt) == false) { |
520 if (time.equals(timeToSet) == false) { |
431 throw new DateTimeException("Conflict found: Fields resolved to two different times: " + time + " " + lt); |
521 throw new DateTimeException("Conflict found: Fields resolved to different times: " + time + " " + timeToSet); |
|
522 } |
|
523 if (excessDays.isZero() == false && periodToSet.isZero() == false && excessDays.equals(periodToSet) == false) { |
|
524 throw new DateTimeException("Conflict found: Fields resolved to different excess periods: " + excessDays + " " + periodToSet); |
|
525 } else { |
|
526 excessDays = periodToSet; |
432 } |
527 } |
433 } else { |
528 } else { |
434 time = lt; |
529 time = timeToSet; |
|
530 excessDays = periodToSet; |
435 } |
531 } |
436 } |
532 } |
437 |
533 |
438 //----------------------------------------------------------------------- |
534 //----------------------------------------------------------------------- |
439 private void crossCheck() { |
535 private void crossCheck() { |