-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite date processing in API v2 #1006
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #1006 +/- ##
===========================================
+ Coverage 82.93% 82.94% +0.01%
===========================================
Files 154 154
Lines 18589 18681 +92
Branches 1775 1781 +6
===========================================
+ Hits 15416 15495 +79
- Misses 3173 3186 +13
Continue to review full report at Codecov.
|
@@ -318,6 +319,8 @@ lazy val library = | |||
|
|||
// Java EE modules which are deprecated in Java SE 9, 10 and will be removed in Java SE 11 | |||
val jaxbApi = "javax.xml.bind" % "jaxb-api" % "2.2.12" | |||
|
|||
val icu4j = "com.ibm.icu" % "icu4j" % "62.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get rid of val jodd = "org.jodd" % "jodd" % "3.2.6"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or do you want to leave DateUtilV1
as it is? Could we make call v1 v2 methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DateUtilV1
uses jodd. I could refactor it to use the v2 code, but that would be more work. My idea was to avoid changing v1 if possible, because the priority is to finish v2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could refactor it to use the v2 code, but that would be more work. My idea was to avoid changing v1 if possible, because the priority is to finish v2.
I understand. My concern is that both versions of the API have to behave identically when converting from and to JDN and that this might be more difficult to assure if we use two different libraries for v1 and v2.
But of course, there could be a version update of one of those libraries and then we would have the same issue if the new version behaves differently from the old one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests for v1 and v2 are identical, so if that happens, I think we'll know. I'd be willing to change v1 to use v2, but I'd rather do it later, when v2 is done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, could you create a separate issue for that so we can do it later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @param endCalendarDate the end of the range. | ||
*/ | ||
case class CalendarDateRangeV2(startCalendarDate: CalendarDateV2, endCalendarDate: CalendarDateV2) { | ||
if (startCalendarDate.calendarName != endCalendarDate.calendarName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also check if startCalendarDate
is before or equals endCalendarDate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in c1977f8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great, thx.
/** | ||
* Represents the name of the Julian calendar. | ||
*/ | ||
case object CalendarNameJulian extends CalendarNameGregorianOrJulian { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that be extended once we support more calendars than Gregorian and Julian?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case object CalendarNameIslamic extends CalendarNameV2
case object CalendarNameHebrew extends CalendarNameV2
And so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it
throw AssertionException(s"Era is required in calendar $calendarName") | ||
} | ||
|
||
case _ => () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, we would have a non supported calendar, right?
Could you explain why there is a CalendarNameV2
and a trait CalendarNameGregorianOrJulian
?
Is your rationale that you would add additional traits for other calendars and include them in the match case statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CalendarNameGregorianOrJulian
is for things like this:
def getGregorianCalendarChangeDate(calendarName: CalendarNameGregorianOrJulian): Date
That method should only accept a Gregorian or Julian calendar, and using the type CalendarNameGregorianOrJulian
guarantees that this is the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, we would have a non supported calendar, right?
That's actually not possible with this code. The case _
is just there for the future, when we have more calendars. Until then, I could remove that line, because it can never be reached.
The design pattern here is called sealed case objects, which is an alternative to using Scala enumerations. See:
https://pedrorijo.com/blog/scala-enums/
With sealed case objects, the compiler can guarantee that the pattern matching is exhaustive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is your rationale that you would add additional traits for other calendars and include them in the match case statement?
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're already using sealed case objects for ontology schemas:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other advantage of sealed case objects is that you can make a hierarchy of subtypes, like CalendarNameV2
-> CalendarNameGregorianOrJulian
-> CalendarNameGregorian
, which you can't do with enumerations.
|
||
/** | ||
* Parses a string representing a single date, without the calendar. This method does does not check that the | ||
* date is valid in its calendar; to do that, call `toJulianDayRange` on it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual validity will only be checked if a GregorianCalendar
is instantiated and leniency is set to false, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and this happens in toJulianDayRange
.
|
||
package org.knora.webapi.util.date | ||
|
||
import java.util.Date |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the Java date class (GregorianCalendar) used anywhere in this code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http://icu-project.org/apiref/icu4j/com/ibm/icu/util/GregorianCalendar.html
ICU's replacement for java.util.GregorianCalendar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using the ICU GregorianCalendar
class instead of the one in the Java standard library, because the ICU class supports Julian Day Numbers.
OK to merge now? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approved on 2458404
Thanks! :) |
This rewrites date processing in API v2 to use ICU4J, which supports many different calendars as well as Julian Day Numbers. This PR doesn't include support for calendars other than Gregorian and Julian, but now it should be much easier to add them. It removes the remaining dependencies of API v2 code on API v1 code, and eliminates the need to convert dates to strings when converting them between JSON-LD and Julian Day Numbers.
org.knora.webapi.util.date.CalendarDateUtilV2
.Resolves #803.
Resolves #928.