22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System . Diagnostics ;
5+ using System . Diagnostics . CodeAnalysis ;
56using System . Globalization ;
67using System . Runtime . CompilerServices ;
78using System . Text ;
@@ -3299,25 +3300,56 @@ private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormat
32993300 result = - 1 ;
33003301 if ( str . GetNext ( ) )
33013302 {
3302- //
3303- // Scan the month names (note that some calendars has 13 months) and find
3304- // the matching month name which has the max string length.
3305- // We need to do this because some cultures (e.g. "cs-CZ") which have
3306- // abbreviated month names with the same prefix.
3307- //
3308- int monthsInYear = ( dtfi . GetMonthName ( 13 ) . Length == 0 ? 12 : 13 ) ;
3309- for ( int i = 1 ; i <= monthsInYear ; i ++ )
3303+ if ( ReferenceEquals ( dtfi , DateTimeFormat . InvariantFormatInfo ) )
33103304 {
3311- string searchStr = dtfi . GetAbbreviatedMonthName ( i ) ;
3312- int matchStrLen = searchStr . Length ;
3313- if ( dtfi . HasSpacesInMonthNames
3314- ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3315- : str . MatchSpecifiedWord ( searchStr ) )
3305+ // Invariant data. Do a fast lookup on the known abbreviated month names.
3306+ ReadOnlySpan < char > span = str . Value . Slice ( str . Index ) ;
3307+ if ( span . Length >= 3 )
33163308 {
3317- if ( matchStrLen > maxMatchStrLen )
3309+ uint m0 = span [ 0 ] , m1 = span [ 1 ] , m2 = span [ 2 ] ;
3310+ if ( ( m0 | m1 | m2 ) <= 0x7F )
33183311 {
3319- maxMatchStrLen = matchStrLen ;
3320- result = i ;
3312+ // Combine all the characters into a single uint, lowercased.
3313+ maxMatchStrLen = 3 ; // assume we'll successfully match
3314+ switch ( ( m0 << 16 ) | ( m1 << 8 ) | m2 | 0x202020 )
3315+ {
3316+ case 0x6a616e : /* 'jan' */ result = 1 ; break ;
3317+ case 0x666562 : /* 'feb' */ result = 2 ; break ;
3318+ case 0x6d6172 : /* 'mar' */ result = 3 ; break ;
3319+ case 0x617072 : /* 'apr' */ result = 4 ; break ;
3320+ case 0x6d6179 : /* 'may' */ result = 5 ; break ;
3321+ case 0x6a756e : /* 'jun' */ result = 6 ; break ;
3322+ case 0x6a756c : /* 'jul' */ result = 7 ; break ;
3323+ case 0x617567 : /* 'aug' */ result = 8 ; break ;
3324+ case 0x736570 : /* 'sep' */ result = 9 ; break ;
3325+ case 0x6f6374 : /* 'oct' */ result = 10 ; break ;
3326+ case 0x6e6f76 : /* 'nov' */ result = 11 ; break ;
3327+ case 0x646563 : /* 'dec' */ result = 12 ; break ;
3328+ default : maxMatchStrLen = 0 ; break ; // undo match assumption
3329+ }
3330+ }
3331+ }
3332+ }
3333+ else
3334+ {
3335+ // Scan the month names (note that some calendars has 13 months) and find
3336+ // the matching month name which has the max string length.
3337+ // We need to do this because some cultures (e.g. "cs-CZ") which have
3338+ // abbreviated month names with the same prefix.
3339+ int monthsInYear = ( dtfi . GetMonthName ( 13 ) . Length == 0 ? 12 : 13 ) ;
3340+ for ( int i = 1 ; i <= monthsInYear ; i ++ )
3341+ {
3342+ string searchStr = dtfi . GetAbbreviatedMonthName ( i ) ;
3343+ int matchStrLen = searchStr . Length ;
3344+ if ( dtfi . HasSpacesInMonthNames
3345+ ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3346+ : str . MatchSpecifiedWord ( searchStr ) )
3347+ {
3348+ if ( matchStrLen > maxMatchStrLen )
3349+ {
3350+ maxMatchStrLen = matchStrLen ;
3351+ result = i ;
3352+ }
33213353 }
33223354 }
33233355 }
@@ -3370,25 +3402,54 @@ private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi,
33703402 result = - 1 ;
33713403 if ( str . GetNext ( ) )
33723404 {
3373- //
3374- // Scan the month names (note that some calendars has 13 months) and find
3375- // the matching month name which has the max string length.
3376- // We need to do this because some cultures (e.g. "vi-VN") which have
3377- // month names with the same prefix.
3378- //
3379- int monthsInYear = ( dtfi . GetMonthName ( 13 ) . Length == 0 ? 12 : 13 ) ;
3380- for ( int i = 1 ; i <= monthsInYear ; i ++ )
3405+ if ( ReferenceEquals ( dtfi , DateTimeFormat . InvariantFormatInfo ) )
33813406 {
3382- string searchStr = dtfi . GetMonthName ( i ) ;
3383- int matchStrLen = searchStr . Length ;
3384- if ( dtfi . HasSpacesInMonthNames
3385- ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3386- : str . MatchSpecifiedWord ( searchStr ) )
3407+ // Invariant data. Do a fast lookup on the known month names.
3408+ ReadOnlySpan < char > span = str . Value . Slice ( str . Index ) ;
3409+ if ( span . Length >= 3 )
33873410 {
3388- if ( matchStrLen > maxMatchStrLen )
3411+ uint m0 = span [ 0 ] , m1 = span [ 1 ] , m2 = span [ 2 ] ;
3412+ if ( ( m0 | m1 | m2 ) <= 0x7F )
33893413 {
3390- maxMatchStrLen = matchStrLen ;
3391- result = i ;
3414+ // Combine all the characters into a single uint, lowercased.
3415+ switch ( ( m0 << 16 ) | ( m1 << 8 ) | m2 | 0x202020 )
3416+ {
3417+ case 0x6a616e : /* 'jan' */ SetIfStartsWith ( span , "January" , 1 , ref result , ref maxMatchStrLen ) ; break ;
3418+ case 0x666562 : /* 'feb' */ SetIfStartsWith ( span , "February" , 2 , ref result , ref maxMatchStrLen ) ; break ;
3419+ case 0x6d6172 : /* 'mar' */ SetIfStartsWith ( span , "March" , 3 , ref result , ref maxMatchStrLen ) ; break ;
3420+ case 0x617072 : /* 'apr' */ SetIfStartsWith ( span , "April" , 4 , ref result , ref maxMatchStrLen ) ; break ;
3421+ case 0x6d6179 : /* 'may' */ SetIfStartsWith ( span , "May" , 5 , ref result , ref maxMatchStrLen ) ; break ;
3422+ case 0x6a756e : /* 'jun' */ SetIfStartsWith ( span , "June" , 6 , ref result , ref maxMatchStrLen ) ; break ;
3423+ case 0x6a756c : /* 'jul' */ SetIfStartsWith ( span , "July" , 7 , ref result , ref maxMatchStrLen ) ; break ;
3424+ case 0x617567 : /* 'aug' */ SetIfStartsWith ( span , "August" , 8 , ref result , ref maxMatchStrLen ) ; break ;
3425+ case 0x736570 : /* 'sep' */ SetIfStartsWith ( span , "September" , 9 , ref result , ref maxMatchStrLen ) ; break ;
3426+ case 0x6f6374 : /* 'oct' */ SetIfStartsWith ( span , "October" , 10 , ref result , ref maxMatchStrLen ) ; break ;
3427+ case 0x6e6f76 : /* 'nov' */ SetIfStartsWith ( span , "November" , 11 , ref result , ref maxMatchStrLen ) ; break ;
3428+ case 0x646563 : /* 'dec' */ SetIfStartsWith ( span , "December" , 12 , ref result , ref maxMatchStrLen ) ; break ;
3429+ }
3430+ }
3431+ }
3432+ }
3433+ else
3434+ {
3435+ // Scan the month names (note that some calendars has 13 months) and find
3436+ // the matching month name which has the max string length.
3437+ // We need to do this because some cultures (e.g. "vi-VN") which have
3438+ // month names with the same prefix.
3439+ int monthsInYear = ( dtfi . GetMonthName ( 13 ) . Length == 0 ? 12 : 13 ) ;
3440+ for ( int i = 1 ; i <= monthsInYear ; i ++ )
3441+ {
3442+ string searchStr = dtfi . GetMonthName ( i ) ;
3443+ int matchStrLen = searchStr . Length ;
3444+ if ( dtfi . HasSpacesInMonthNames
3445+ ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3446+ : str . MatchSpecifiedWord ( searchStr ) )
3447+ {
3448+ if ( matchStrLen > maxMatchStrLen )
3449+ {
3450+ maxMatchStrLen = matchStrLen ;
3451+ result = i ;
3452+ }
33923453 }
33933454 }
33943455 }
@@ -3442,18 +3503,46 @@ private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatIn
34423503 result = - 1 ;
34433504 if ( str . GetNext ( ) )
34443505 {
3445- for ( DayOfWeek i = DayOfWeek . Sunday ; i <= DayOfWeek . Saturday ; i ++ )
3506+ if ( ReferenceEquals ( dtfi , DateTimeFormat . InvariantFormatInfo ) )
34463507 {
3447- string searchStr = dtfi . GetAbbreviatedDayName ( i ) ;
3448- int matchStrLen = searchStr . Length ;
3449- if ( dtfi . HasSpacesInDayNames
3450- ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3451- : str . MatchSpecifiedWord ( searchStr ) )
3508+ // Invariant data. Do a fast lookup on the known abbreviated day names.
3509+ ReadOnlySpan < char > span = str . Value . Slice ( str . Index ) ;
3510+ if ( span . Length >= 3 )
34523511 {
3453- if ( matchStrLen > maxMatchStrLen )
3512+ uint d0 = span [ 0 ] , d1 = span [ 1 ] , d2 = span [ 2 ] ;
3513+ if ( ( d0 | d1 | d2 ) <= 0x7F )
34543514 {
3455- maxMatchStrLen = matchStrLen ;
3456- result = ( int ) i ;
3515+ // Combine all the characters into a single uint, lowercased.
3516+ maxMatchStrLen = 3 ; // assume we'll successfully match
3517+ switch ( ( d0 << 16 ) | ( d1 << 8 ) | d2 | 0x202020 )
3518+ {
3519+ case 0x73756E /* 'sun' */ : result = 0 ; break ;
3520+ case 0x6d6f6e /* 'mon' */ : result = 1 ; break ;
3521+ case 0x747565 /* 'tue' */ : result = 2 ; break ;
3522+ case 0x776564 /* 'wed' */ : result = 3 ; break ;
3523+ case 0x746875 /* 'thu' */ : result = 4 ; break ;
3524+ case 0x667269 /* 'fri' */ : result = 5 ; break ;
3525+ case 0x736174 /* 'sat' */ : result = 6 ; break ;
3526+ default : maxMatchStrLen = 0 ; break ; // undo match assumption
3527+ }
3528+ }
3529+ }
3530+ }
3531+ else
3532+ {
3533+ for ( DayOfWeek i = DayOfWeek . Sunday ; i <= DayOfWeek . Saturday ; i ++ )
3534+ {
3535+ string searchStr = dtfi . GetAbbreviatedDayName ( i ) ;
3536+ int matchStrLen = searchStr . Length ;
3537+ if ( dtfi . HasSpacesInDayNames
3538+ ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3539+ : str . MatchSpecifiedWord ( searchStr ) )
3540+ {
3541+ if ( matchStrLen > maxMatchStrLen )
3542+ {
3543+ maxMatchStrLen = matchStrLen ;
3544+ result = ( int ) i ;
3545+ }
34573546 }
34583547 }
34593548 }
@@ -3481,18 +3570,44 @@ private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, sc
34813570 result = - 1 ;
34823571 if ( str . GetNext ( ) )
34833572 {
3484- for ( DayOfWeek i = DayOfWeek . Sunday ; i <= DayOfWeek . Saturday ; i ++ )
3573+ if ( ReferenceEquals ( dtfi , DateTimeFormat . InvariantFormatInfo ) )
34853574 {
3486- string searchStr = dtfi . GetDayName ( i ) ;
3487- int matchStrLen = searchStr . Length ;
3488- if ( dtfi . HasSpacesInDayNames
3489- ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3490- : str . MatchSpecifiedWord ( searchStr ) )
3575+ // Invariant data. Do a fast lookup on the known day names.
3576+ ReadOnlySpan < char > span = str . Value . Slice ( str . Index ) ;
3577+ if ( span . Length >= 3 )
34913578 {
3492- if ( matchStrLen > maxMatchStrLen )
3579+ uint d0 = span [ 0 ] , d1 = span [ 1 ] , d2 = span [ 2 ] ;
3580+ if ( ( d0 | d1 | d2 ) <= 0x7F )
34933581 {
3494- maxMatchStrLen = matchStrLen ;
3495- result = ( int ) i ;
3582+ // Combine all the characters into a single uint, lowercased.
3583+ switch ( ( d0 << 16 ) | ( d1 << 8 ) | d2 | 0x202020 )
3584+ {
3585+ case 0x73756E /* 'sun' */ : SetIfStartsWith ( span , "Sunday" , 0 , ref result , ref maxMatchStrLen ) ; break ;
3586+ case 0x6d6f6e /* 'mon' */ : SetIfStartsWith ( span , "Monday" , 1 , ref result , ref maxMatchStrLen ) ; break ;
3587+ case 0x747565 /* 'tue' */ : SetIfStartsWith ( span , "Tuesday" , 2 , ref result , ref maxMatchStrLen ) ; break ;
3588+ case 0x776564 /* 'wed' */ : SetIfStartsWith ( span , "Wednesday" , 3 , ref result , ref maxMatchStrLen ) ; break ;
3589+ case 0x746875 /* 'thu' */ : SetIfStartsWith ( span , "Thursday" , 4 , ref result , ref maxMatchStrLen ) ; break ;
3590+ case 0x667269 /* 'fri' */ : SetIfStartsWith ( span , "Friday" , 5 , ref result , ref maxMatchStrLen ) ; break ;
3591+ case 0x736174 /* 'sat' */ : SetIfStartsWith ( span , "Saturday" , 6 , ref result , ref maxMatchStrLen ) ; break ;
3592+ }
3593+ }
3594+ }
3595+ }
3596+ else
3597+ {
3598+ for ( DayOfWeek i = DayOfWeek . Sunday ; i <= DayOfWeek . Saturday ; i ++ )
3599+ {
3600+ string searchStr = dtfi . GetDayName ( i ) ;
3601+ int matchStrLen = searchStr . Length ;
3602+ if ( dtfi . HasSpacesInDayNames
3603+ ? str . MatchSpecifiedWords ( searchStr , false , ref matchStrLen )
3604+ : str . MatchSpecifiedWord ( searchStr ) )
3605+ {
3606+ if ( matchStrLen > maxMatchStrLen )
3607+ {
3608+ maxMatchStrLen = matchStrLen ;
3609+ result = ( int ) i ;
3610+ }
34963611 }
34973612 }
34983613 }
@@ -3505,6 +3620,20 @@ private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, sc
35053620 return false ;
35063621 }
35073622
3623+ /// <summary>
3624+ /// Sets <paramref name="result"/> to <paramref name="matchResult"/> and <paramref name="maxMatchStrLen"/> to <paramref name="match"/>'s Length
3625+ /// if <paramref name="span"/> starts with <paramref name="match"/> with an ordinal ignore-case comparison.
3626+ /// </summary>
3627+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] // exposes StartsWith to constant `match`
3628+ private static void SetIfStartsWith ( ReadOnlySpan < char > span , [ ConstantExpected ] string match , int matchResult , scoped ref int result , ref int maxMatchStrLen )
3629+ {
3630+ if ( span . StartsWith ( match , StringComparison . OrdinalIgnoreCase ) )
3631+ {
3632+ result = matchResult ;
3633+ maxMatchStrLen = match . Length ;
3634+ }
3635+ }
3636+
35083637 /*=================================MatchEraName==================================
35093638 **Action: Parse era name from string starting at str.Index.
35103639 **Returns: An era value.
0 commit comments