@@ -201,13 +201,15 @@ internal interface DateFieldContainer {
201201 var monthNumber: Int?
202202 var dayOfMonth: Int?
203203 var isoDayOfWeek: Int?
204+ var dayOfYear: Int?
204205}
205206
206207private object DateFields {
207208 val year = GenericFieldSpec (PropertyAccessor (DateFieldContainer ::year))
208209 val month = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::monthNumber), minValue = 1 , maxValue = 12 )
209210 val dayOfMonth = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::dayOfMonth), minValue = 1 , maxValue = 31 )
210211 val isoDayOfWeek = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::isoDayOfWeek), minValue = 1 , maxValue = 7 )
212+ val dayOfYear = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::dayOfYear), minValue = 1 , maxValue = 366 )
211213}
212214
213215/* *
@@ -217,14 +219,40 @@ internal class IncompleteLocalDate(
217219 override var year : Int? = null ,
218220 override var monthNumber : Int? = null ,
219221 override var dayOfMonth : Int? = null ,
220- override var isoDayOfWeek : Int? = null
222+ override var isoDayOfWeek : Int? = null ,
223+ override var dayOfYear : Int? = null ,
221224) : DateFieldContainer, Copyable<IncompleteLocalDate> {
222225 fun toLocalDate (): LocalDate {
223- val date = LocalDate (
224- requireParsedField(year, " year" ),
225- requireParsedField(monthNumber, " monthNumber" ),
226- requireParsedField(dayOfMonth, " dayOfMonth" )
227- )
226+ val year = requireParsedField(year, " year" )
227+ val date = when (val dayOfYear = dayOfYear) {
228+ null -> LocalDate (
229+ year,
230+ requireParsedField(monthNumber, " monthNumber" ),
231+ requireParsedField(dayOfMonth, " dayOfMonth" )
232+ )
233+ else -> LocalDate (year, 1 , 1 ).plus(dayOfYear - 1 , DateTimeUnit .DAY ).also {
234+ if (it.year != year) {
235+ throw DateTimeFormatException (
236+ " Can not create a LocalDate from the given input: " +
237+ " the day of year is $dayOfYear , which is not a valid day of year for the year $year "
238+ )
239+ }
240+ if (monthNumber != null && it.monthNumber != monthNumber) {
241+ throw DateTimeFormatException (
242+ " Can not create a LocalDate from the given input: " +
243+ " the day of year is $dayOfYear , which is ${it.month} , " +
244+ " but $monthNumber was specified as the month number"
245+ )
246+ }
247+ if (dayOfMonth != null && it.dayOfMonth != dayOfMonth) {
248+ throw DateTimeFormatException (
249+ " Can not create a LocalDate from the given input: " +
250+ " the day of year is $dayOfYear , which is the day ${it.dayOfMonth} of ${it.month} , " +
251+ " but $dayOfMonth was specified as the day of month"
252+ )
253+ }
254+ }
255+ }
228256 isoDayOfWeek?.let {
229257 if (it != date.dayOfWeek.isoDayNumber) {
230258 throw DateTimeFormatException (
@@ -241,16 +269,21 @@ internal class IncompleteLocalDate(
241269 monthNumber = date.monthNumber
242270 dayOfMonth = date.dayOfMonth
243271 isoDayOfWeek = date.dayOfWeek.isoDayNumber
272+ dayOfYear = date.dayOfYear
244273 }
245274
246- override fun copy (): IncompleteLocalDate = IncompleteLocalDate (year, monthNumber, dayOfMonth, isoDayOfWeek)
275+ override fun copy (): IncompleteLocalDate =
276+ IncompleteLocalDate (year, monthNumber, dayOfMonth, isoDayOfWeek, dayOfYear)
247277
248278 override fun equals (other : Any? ): Boolean =
249279 other is IncompleteLocalDate && year == other.year && monthNumber == other.monthNumber &&
250- dayOfMonth == other.dayOfMonth && isoDayOfWeek == other.isoDayOfWeek
280+ dayOfMonth == other.dayOfMonth && isoDayOfWeek == other.isoDayOfWeek && dayOfYear == other.dayOfYear
251281
252- override fun hashCode (): Int =
253- year.hashCode() * 31 + monthNumber.hashCode() * 31 + dayOfMonth.hashCode() * 31 + isoDayOfWeek.hashCode() * 31
282+ override fun hashCode (): Int = year.hashCode() * 923521 +
283+ monthNumber.hashCode() * 29791 +
284+ dayOfMonth.hashCode() * 961 +
285+ isoDayOfWeek.hashCode() * 31 +
286+ dayOfYear.hashCode()
254287
255288 override fun toString (): String =
256289 " ${year ? : " ??" } -${monthNumber ? : " ??" } -${dayOfMonth ? : " ??" } (day of week is ${isoDayOfWeek ? : " ??" } )"
@@ -375,6 +408,22 @@ private class DayDirective(private val padding: Padding) :
375408 override fun hashCode (): Int = padding.hashCode()
376409}
377410
411+ private class DayOfYearDirective (private val padding : Padding ) :
412+ UnsignedIntFieldFormatDirective <DateFieldContainer >(
413+ DateFields .dayOfYear,
414+ minDigits = padding.minDigits(3 ),
415+ spacePadding = padding.spaces(3 ),
416+ ) {
417+ override val builderRepresentation: String
418+ get() = when (padding) {
419+ Padding .ZERO -> " ${DateTimeFormatBuilder .WithDate ::dayOfYear.name} ()"
420+ else -> " ${DateTimeFormatBuilder .WithDate ::dayOfYear.name} (${padding.toKotlinCode()} )"
421+ }
422+
423+ override fun equals (other : Any? ): Boolean = other is DayOfYearDirective && padding == other.padding
424+ override fun hashCode (): Int = padding.hashCode()
425+ }
426+
378427private class DayOfWeekDirective (private val names : DayOfWeekNames ) :
379428 NamedUnsignedIntFieldFormatDirective <DateFieldContainer >(DateFields .isoDayOfWeek, names.names, " dayOfWeekName" ) {
380429
@@ -432,6 +481,9 @@ internal interface AbstractWithDateBuilder : DateTimeFormatBuilder.WithDate {
432481 override fun dayOfWeek (names : DayOfWeekNames ) =
433482 addFormatStructureForDate(BasicFormatStructure (DayOfWeekDirective (names)))
434483
484+ override fun dayOfYear (padding : Padding ) =
485+ addFormatStructureForDate(BasicFormatStructure (DayOfYearDirective (padding)))
486+
435487 @Suppress(" NO_ELSE_IN_WHEN" )
436488 override fun date (format : DateTimeFormat <LocalDate >) = when (format) {
437489 is LocalDateFormat -> addFormatStructureForDate(format.actualFormat)
0 commit comments