@@ -6,7 +6,7 @@ use core::{cmp::Ordering, str::FromStr};
6
6
use tinystr:: TinyAsciiStr ;
7
7
8
8
use crate :: {
9
- iso:: IsoDate ,
9
+ iso:: { year_month_within_limits , IsoDate } ,
10
10
options:: { ArithmeticOverflow , DifferenceOperation , DifferenceSettings , DisplayCalendar } ,
11
11
parsers:: { FormattableCalendar , FormattableDate , FormattableYearMonth } ,
12
12
utils:: pad_iso_year,
@@ -81,10 +81,21 @@ impl PlainYearMonth {
81
81
overflow : ArithmeticOverflow ,
82
82
) -> TemporalResult < Self > {
83
83
let day = reference_day. unwrap_or ( 1 ) ;
84
- let iso = IsoDate :: new_with_overflow ( year, month, day, overflow) ?;
84
+ let iso = IsoDate :: regulate ( year, month, day, overflow) ?;
85
+ if !year_month_within_limits ( iso. year , iso. month ) {
86
+ return Err ( TemporalError :: range ( ) . with_message ( "Exceeded valid range." ) ) ;
87
+ }
85
88
Ok ( Self :: new_unchecked ( iso, calendar) )
86
89
}
87
90
91
+ /// Create a `PlainYearMonth` from a `PartialDate`
92
+ pub fn from_partial (
93
+ partial : PartialDate ,
94
+ overflow : ArithmeticOverflow ,
95
+ ) -> TemporalResult < Self > {
96
+ partial. calendar . year_month_from_partial ( & partial, overflow)
97
+ }
98
+
88
99
/// Returns the iso year value for this `YearMonth`.
89
100
#[ inline]
90
101
#[ must_use]
@@ -245,7 +256,6 @@ impl FromStr for PlainYearMonth {
245
256
246
257
fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
247
258
let record = crate :: parsers:: parse_year_month ( s) ?;
248
-
249
259
let calendar = record
250
260
. calendar
251
261
. map ( Calendar :: from_utf8)
@@ -254,12 +264,70 @@ impl FromStr for PlainYearMonth {
254
264
255
265
let date = record. date . temporal_unwrap ( ) ?;
256
266
257
- Self :: new_with_overflow (
258
- date. year ,
259
- date. month ,
260
- None ,
261
- calendar,
262
- ArithmeticOverflow :: Reject ,
263
- )
267
+ // The below steps are from `ToTemporalYearMonth`
268
+ // 10. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
269
+ let iso = IsoDate :: new_unchecked ( date. year , date. month , date. day ) ;
270
+
271
+ // 11. If ISOYearMonthWithinLimits(isoDate) is false, throw a RangeError exception.
272
+ if !year_month_within_limits ( iso. year , iso. month ) {
273
+ return Err ( TemporalError :: range ( ) . with_message ( "Exceeded valid range." ) ) ;
274
+ }
275
+
276
+ let intermediate = Self :: new_unchecked ( iso, calendar) ;
277
+ // 12. Set result to ISODateToFields(calendar, isoDate, year-month).
278
+ let partial = PartialDate :: try_from_year_month ( & intermediate) ?;
279
+ // 13. NOTE: The following operation is called with constrain regardless of the
280
+ // value of overflow, in order for the calendar to store a canonical value in the
281
+ // [[Day]] field of the [[ISODate]] internal slot of the result.
282
+ // 14. Set isoDate to ? CalendarYearMonthFromFields(calendar, result, constrain).
283
+ // 15. Return ! CreateTemporalYearMonth(isoDate, calendar).
284
+ PlainYearMonth :: from_partial ( partial, ArithmeticOverflow :: Constrain )
285
+ }
286
+ }
287
+
288
+ #[ cfg( test) ]
289
+ mod tests {
290
+ use core:: str:: FromStr ;
291
+
292
+ use super :: PlainYearMonth ;
293
+
294
+ #[ test]
295
+ fn basic_from_str ( ) {
296
+ let valid_strings = [
297
+ "-271821-04" ,
298
+ "-271821-04-01" ,
299
+ "-271821-04-01T00:00" ,
300
+ "+275760-09" ,
301
+ "+275760-09-30" ,
302
+ "+275760-09-30T23:59:59.999999999" ,
303
+ ] ;
304
+
305
+ for valid_case in valid_strings {
306
+ let ym = PlainYearMonth :: from_str ( valid_case) ;
307
+ assert ! ( ym. is_ok( ) ) ;
308
+ }
309
+ }
310
+
311
+ #[ test]
312
+ fn invalid_from_str ( ) {
313
+ let invalid_strings = [
314
+ "-271821-03-31" ,
315
+ "-271821-03-31T23:59:59.999999999" ,
316
+ "+275760-10" ,
317
+ "+275760-10-01" ,
318
+ "+275760-10-01T00:00" ,
319
+ ] ;
320
+
321
+ for invalid_case in invalid_strings {
322
+ let err = PlainYearMonth :: from_str ( invalid_case) ;
323
+ assert ! ( err. is_err( ) ) ;
324
+ }
325
+
326
+ let invalid_strings = [ "2019-10-01T09:00:00Z" , "2019-10-01T09:00:00Z[UTC]" ] ;
327
+
328
+ for invalid_case in invalid_strings {
329
+ let err = PlainYearMonth :: from_str ( invalid_case) ;
330
+ assert ! ( err. is_err( ) ) ;
331
+ }
264
332
}
265
333
}
0 commit comments