11using System ;
22using System . Globalization ;
3+ using System . IO ;
34using System . Linq ;
45using System . Text ;
56
@@ -46,43 +47,56 @@ public static DateTime Decode(Asn1Reader asn, out TimeZoneInfo? zone) {
4647 return extractDateTime ( SB . ToString ( ) , out zone ) ;
4748 }
4849
49- static DateTime extractDateTime ( String strValue , out TimeZoneInfo zone ) {
50- Int32 delimiterIndex ;
51- zone = TimeZoneInfo . FindSystemTimeZoneById ( "Greenwich Standard Time" ) ;
52- if ( strValue . ToUpper ( ) . Contains ( "Z" ) ) {
53- delimiterIndex = strValue . ToUpper ( ) . IndexOf ( 'Z' ) ;
54- return extractZulu ( strValue , delimiterIndex ) ;
55- }
56- Boolean hasZone = extractZoneShift ( strValue , out Int32 hours , out Int32 minutes , out delimiterIndex ) ;
57- Int32 milliseconds = extractMilliseconds ( strValue , delimiterIndex , out Int32 msDelimiter ) ;
58- DateTime retValue = extractDateTime ( strValue , msDelimiter , delimiterIndex ) ;
50+ static DateTime extractDateTime ( String strValue , out TimeZoneInfo ? zone ) {
51+ zone = null ;
52+ Boolean hasZone = extractZoneShift ( strValue , out Int32 hours , out Int32 minutes , out Int32 zoneDelimiter ) ;
53+ Int32 milliseconds = extractMilliseconds ( strValue , zoneDelimiter , out Int32 msDelimiter ) ;
54+ DateTime retValue = extractDateTime ( strValue , msDelimiter , zoneDelimiter ) ;
5955 if ( hasZone ) {
6056 zone = bindZone ( hours , minutes ) ;
61- retValue = retValue . AddHours ( hours ) ;
62- retValue = retValue . AddMinutes ( minutes ) ;
57+ //retValue = retValue.AddHours(hours);
58+ //retValue = retValue.AddMinutes(minutes);
59+ } else {
60+ retValue = DateTime . SpecifyKind ( retValue , DateTimeKind . Utc ) . ToLocalTime ( ) ;
6361 }
6462 retValue = retValue . AddMilliseconds ( milliseconds ) ;
63+
6564 return retValue ;
6665 }
67- static DateTime extractZulu ( String strValue , Int32 zoneDelimiter ) {
68- return zoneDelimiter switch {
69- 12 => parseExactUtc ( strValue . Replace ( "Z" , null ) , UTCFormat ) . ToLocalTime ( ) ,
70- 16 => parseExactUtc ( strValue . Replace ( "Z" , null ) , UTCPreciseFormat ) . ToLocalTime ( ) ,
71- 14 => DateTime . ParseExact ( strValue . Replace ( "Z" , null ) , GtFormat , null ) . ToLocalTime ( ) ,
72- 18 => DateTime . ParseExact ( strValue . Replace ( "Z" , null ) , GtPreciseFormat , null ) . ToLocalTime ( ) ,
73- _ => throw new ArgumentException ( "Time zone suffix is not valid." )
66+ static DateTime extractDateTime ( String strValue , Int32 msDelimiter , Int32 zoneDelimiter ) {
67+ String rawString ;
68+ if ( msDelimiter < 0 && zoneDelimiter < 0 ) {
69+ // Zulu time zone, no milliseconds
70+ rawString = strValue ;
71+ } else if ( msDelimiter < 0 ) {
72+ // Custom time zone, no milliseconds
73+ rawString = strValue . Substring ( 0 , zoneDelimiter ) ;
74+ } else {
75+ // Milliseconds
76+ rawString = strValue . Substring ( 0 , msDelimiter ) ;
77+ }
78+
79+ return rawString . Length switch {
80+ 12 => parseExactUtc ( rawString , UTCFormat ) ,
81+ 14 => DateTime . ParseExact ( rawString , GtFormat , null ) ,
82+ _ => throw new ArgumentException ( "Time zone suffix is not valid." )
7483 } ;
7584 }
7685 static Boolean extractZoneShift ( String strValue , out Int32 hours , out Int32 minutes , out Int32 delimiterIndex ) {
86+ if ( strValue . EndsWith ( "Z" ) ) {
87+ delimiterIndex = strValue . IndexOf ( 'Z' ) ;
88+ hours = minutes = 0 ;
89+ return false ;
90+ }
91+
7792 if ( strValue . Contains ( '+' ) ) {
7893 delimiterIndex = strValue . IndexOf ( '+' ) ;
7994 hours = Int32 . Parse ( strValue . Substring ( delimiterIndex , 3 ) ) ;
8095 } else if ( strValue . Contains ( '-' ) ) {
8196 delimiterIndex = strValue . IndexOf ( '-' ) ;
8297 hours = - Int32 . Parse ( strValue . Substring ( delimiterIndex , 3 ) ) ;
8398 } else {
84- hours = minutes = delimiterIndex = 0 ;
85- return false ;
99+ throw new InvalidDataException ( "ASN.1 DateTime has missing time zone identifier." ) ;
86100 }
87101 minutes = strValue . Length > delimiterIndex + 3
88102 ? - Int32 . Parse ( strValue . Substring ( delimiterIndex + 3 , 2 ) )
@@ -97,40 +111,27 @@ static Int32 extractMilliseconds(String strValue, Int32 zoneDelimiter, out Int32
97111 Int32 precisionLength = zoneDelimiter > 0
98112 ? zoneDelimiter - msDelimiter - 1
99113 : strValue . Length - msDelimiter - 1 ;
100- return Int32 . Parse ( strValue . Substring ( msDelimiter + 1 , precisionLength ) ) ;
114+ // milliseconds decimal part
115+ Int32 msNumber = Int32 . Parse ( strValue . Substring ( msDelimiter + 1 , precisionLength ) ) ;
116+ // if precision length is 1, then msNumber represents milliseconds * 100
117+ // if precision length is 2, then msNumber represents milliseconds * 10
118+ // if precision length is 3, then msNumber represents milliseconds * 1
119+ // we can get this by: 100 * msNumber / 10 ^ precisionLength
120+ return ( Int32 ) ( msNumber / Math . Pow ( 10 , precisionLength ) * 1000 ) ;
101121 }
102- static DateTime parseExactUtc ( String strValue , String format ) {
122+ static DateTime parseExactUtc ( String strValue , params String [ ] format ) {
103123 // fix: .NET 'yy' format works in range between 1930-2030. As per RFC5280,
104124 // dates must be between 1950-2049. In .NET, years between 30 and 50 are treated
105125 // as 1930-1950, while it should be 2030-2050. So, fix the range between 30 and 50
106126 // by adding a century.
107- var dateTime = DateTime . ParseExact ( strValue , format , null ) ;
127+ var dateTime = DateTime . ParseExact ( strValue , format , null , DateTimeStyles . None ) ;
108128 // not inclusive. Starting with 2050, GeneralizedTime is used, so 50+ values will go
109- // to 20th century as in .NET
129+ // to 21st century as in .NET
110130 if ( dateTime . Year < 1950 ) {
111131 dateTime = dateTime . AddYears ( 100 ) ;
112132 }
113133 return dateTime ;
114134 }
115- static DateTime extractDateTime ( String strValue , Int32 msDelimiter , Int32 zoneDelimiter ) {
116- String rawString ;
117- if ( msDelimiter < 0 && zoneDelimiter < 0 ) {
118- // Zulu time zone, no milliseconds
119- rawString = strValue ;
120- } else if ( msDelimiter < 0 ) {
121- // Custom time zone, no milliseconds
122- rawString = strValue . Substring ( 0 , zoneDelimiter ) ;
123- } else {
124- // Milliseconds
125- rawString = strValue . Substring ( 0 , msDelimiter ) ;
126- }
127-
128- return rawString . Length switch {
129- 12 => parseExactUtc ( rawString , UTCFormat ) ,
130- 14 => DateTime . ParseExact ( rawString , GtFormat , null ) ,
131- _ => throw new ArgumentException ( "Time zone suffix is not valid." )
132- } ;
133- }
134135 static TimeZoneInfo bindZone ( Int32 hours , Int32 minutes ) {
135136 foreach ( TimeZoneInfo zone in TimeZoneInfo . GetSystemTimeZones ( ) . Where ( zone => zone . BaseUtcOffset . Hours == hours && zone . BaseUtcOffset . Minutes == minutes ) ) {
136137 return zone ;
@@ -140,8 +141,6 @@ static TimeZoneInfo bindZone(Int32 hours, Int32 minutes) {
140141
141142 #region Constants
142143 const String UTCFormat = "yyMMddHHmmss" ;
143- const String UTCPreciseFormat = "yyMMddHHmmss.FFF" ;
144144 const String GtFormat = "yyyyMMddHHmmss" ;
145- const String GtPreciseFormat = "yyyyMMddHHmmss.FFF" ;
146145 #endregion
147146}
0 commit comments