@@ -201,13 +201,15 @@ internal interface DateFieldContainer {
201
201
var monthNumber: Int?
202
202
var dayOfMonth: Int?
203
203
var isoDayOfWeek: Int?
204
+ var dayOfYear: Int?
204
205
}
205
206
206
207
private object DateFields {
207
208
val year = GenericFieldSpec (PropertyAccessor (DateFieldContainer ::year))
208
209
val month = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::monthNumber), minValue = 1 , maxValue = 12 )
209
210
val dayOfMonth = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::dayOfMonth), minValue = 1 , maxValue = 31 )
210
211
val isoDayOfWeek = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::isoDayOfWeek), minValue = 1 , maxValue = 7 )
212
+ val dayOfYear = UnsignedFieldSpec (PropertyAccessor (DateFieldContainer ::dayOfYear), minValue = 1 , maxValue = 366 )
211
213
}
212
214
213
215
/* *
@@ -217,14 +219,40 @@ internal class IncompleteLocalDate(
217
219
override var year : Int? = null ,
218
220
override var monthNumber : Int? = null ,
219
221
override var dayOfMonth : Int? = null ,
220
- override var isoDayOfWeek : Int? = null
222
+ override var isoDayOfWeek : Int? = null ,
223
+ override var dayOfYear : Int? = null ,
221
224
) : DateFieldContainer, Copyable<IncompleteLocalDate> {
222
225
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
+ }
228
256
isoDayOfWeek?.let {
229
257
if (it != date.dayOfWeek.isoDayNumber) {
230
258
throw DateTimeFormatException (
@@ -241,16 +269,21 @@ internal class IncompleteLocalDate(
241
269
monthNumber = date.monthNumber
242
270
dayOfMonth = date.dayOfMonth
243
271
isoDayOfWeek = date.dayOfWeek.isoDayNumber
272
+ dayOfYear = date.dayOfYear
244
273
}
245
274
246
- override fun copy (): IncompleteLocalDate = IncompleteLocalDate (year, monthNumber, dayOfMonth, isoDayOfWeek)
275
+ override fun copy (): IncompleteLocalDate =
276
+ IncompleteLocalDate (year, monthNumber, dayOfMonth, isoDayOfWeek, dayOfYear)
247
277
248
278
override fun equals (other : Any? ): Boolean =
249
279
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
251
281
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()
254
287
255
288
override fun toString (): String =
256
289
" ${year ? : " ??" } -${monthNumber ? : " ??" } -${dayOfMonth ? : " ??" } (day of week is ${isoDayOfWeek ? : " ??" } )"
@@ -375,6 +408,22 @@ private class DayDirective(private val padding: Padding) :
375
408
override fun hashCode (): Int = padding.hashCode()
376
409
}
377
410
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
+
378
427
private class DayOfWeekDirective (private val names : DayOfWeekNames ) :
379
428
NamedUnsignedIntFieldFormatDirective <DateFieldContainer >(DateFields .isoDayOfWeek, names.names, " dayOfWeekName" ) {
380
429
@@ -432,6 +481,9 @@ internal interface AbstractWithDateBuilder : DateTimeFormatBuilder.WithDate {
432
481
override fun dayOfWeek (names : DayOfWeekNames ) =
433
482
addFormatStructureForDate(BasicFormatStructure (DayOfWeekDirective (names)))
434
483
484
+ override fun dayOfYear (padding : Padding ) =
485
+ addFormatStructureForDate(BasicFormatStructure (DayOfYearDirective (padding)))
486
+
435
487
@Suppress(" NO_ELSE_IN_WHEN" )
436
488
override fun date (format : DateTimeFormat <LocalDate >) = when (format) {
437
489
is LocalDateFormat -> addFormatStructureForDate(format.actualFormat)
0 commit comments