[corlib] Parse datetime string using culture calendar. Fixes #18052
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Metadata.W3cXsd2001 / SoapDuration.cs
1 //
2 // System.Runtime.Remoting.Metadata.W3cXsd2001.SoapDuration.cs
3 //
4 // Authors:
5 //      Martin Willemoes Hansen (mwh@sysrq.dk)
6 //      Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2003 Martin Willemoes Hansen
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Text;
36 using System.Globalization;
37
38 namespace System.Runtime.Remoting.Metadata.W3cXsd2001 
39 {
40         [System.Runtime.InteropServices.ComVisible (true)]
41         public sealed class SoapDuration
42         {
43                 public SoapDuration()
44                 {
45                 }
46                 
47                 public static string XsdType {
48                         get { return "duration"; }
49                 }
50
51                 public static TimeSpan Parse (string value)
52                 {
53                         if (value.Length == 0)
54                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
55
56                         int start = 0;
57                         if (value [0] == '-')
58                                 start = 1;
59                         bool minusValue = (start == 1);
60
61                         if (value [start] != 'P')
62                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
63                         start++;
64
65                         int parseStep = 0;
66                         int days = 0;
67                         bool isTime = false;
68                         int hours = 0;
69                         int minutes = 0;
70                         double seconds = 0;
71
72                         bool error = false;
73
74                         int i = start;
75                         while (i < value.Length) {
76                                 if (value [i] == 'T') {
77                                         isTime = true;
78                                         parseStep = 4;
79                                         i++;
80                                         start = i;
81                                         continue;
82                                 }
83                                 bool isIntegerValue = true;
84                                 int dotOccurence = 0;
85                                 for (; i < value.Length; i++) 
86                                 {
87                                         if (!Char.IsDigit (value [i]))
88                                         {
89                                                 //check if it is a non integer value.
90                                                 if (value[i] == '.') 
91                                                 {
92                                                         isIntegerValue = false;
93                                                         dotOccurence++;
94                                                         //if there is more than one dot in the number 
95                                                         //than its an error
96                                                         if (dotOccurence > 1 )
97                                                         {
98                                                                 error = true;
99                                                                 break;
100                                                         }
101                                                 }
102                                                 else
103                                                         break;
104                                         }
105                                 }
106
107                                 int intValue = -1;
108                                 double doubleValue = -1;
109                                 if (isIntegerValue)
110                                         intValue = int.Parse (value.Substring (start, i - start));
111                                 else
112                                         doubleValue = double.Parse (value.Substring (start, i - start), CultureInfo.InvariantCulture);
113                                 switch (value [i]) {
114                                 case 'Y':
115                                         days += intValue * 365;
116                                         if (parseStep > 0 || !isIntegerValue)
117                                                 error = true;
118                                         else
119                                                 parseStep = 1;
120                                         break;
121                                 case 'M':
122                                         if (parseStep < 2 && isIntegerValue) {
123                                                 days += 365 * (intValue / 12) + 30 * (intValue % 12);
124                                                 parseStep = 2;
125                                         } else if (isTime && parseStep < 6 && isIntegerValue) {
126                                                 minutes = intValue;
127                                                 parseStep = 6;
128                                         }
129                                         else
130                                                 error = true;
131                                         break;
132                                 case 'D':
133                                         days += intValue;
134                                         if (parseStep > 2 || !isIntegerValue)
135                                                 error = true;
136                                         else
137                                                 parseStep = 3;
138                                         break;
139                                 case 'H':
140                                         hours = intValue;
141                                         if (!isTime || parseStep > 4 || !isIntegerValue)
142                                                 error = true;
143                                         else
144                                                 parseStep = 5;
145                                         break;
146                                 case 'S':
147                                         if (isIntegerValue)
148                                                 seconds = intValue;
149                                         else
150                                                 seconds = doubleValue;
151                                         if (!isTime || parseStep > 6)
152                                                 error = true;
153                                         else
154                                                 parseStep = 7;
155                                         break;
156                                 default:
157                                         error = true;
158                                         break;
159                                 }
160                                 if (error)
161                                         break;
162                                 ++i;
163                                 start = i;
164                         }
165                         if (error)
166                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
167                         TimeSpan ts = new TimeSpan (days, hours, minutes, 0) + TimeSpan.FromSeconds (seconds);
168                         return minusValue ? -ts : ts;
169                 }
170
171                 public static string ToString (TimeSpan timeSpan)
172                 {
173                         StringBuilder builder = new StringBuilder();
174                         if (timeSpan.Ticks < 0) {
175                                 builder.Append('-');
176                                 timeSpan = timeSpan.Negate();
177                         }
178                         builder.Append('P');
179                         if (timeSpan.Days > 0) builder.Append(timeSpan.Days).Append('D');
180                         if (timeSpan.Days > 0 || timeSpan.Minutes > 0 || timeSpan.Seconds > 0 || timeSpan.Milliseconds > 0) {
181                                 builder.Append('T');
182                                 if (timeSpan.Hours > 0) builder.Append(timeSpan.Hours).Append('H');
183                                 if (timeSpan.Minutes > 0) builder.Append(timeSpan.Minutes).Append('M');
184                                 if (timeSpan.Seconds > 0 || timeSpan.Milliseconds > 0) {
185                                         double secs = (double) timeSpan.Seconds;
186                                         if (timeSpan.Milliseconds > 0)
187                                                 secs += ((double)timeSpan.Milliseconds) / 1000.0;
188                                         builder.Append(String.Format(CultureInfo.InvariantCulture, "{0:0.0000000}", secs));
189                                         builder.Append('S');
190                                 }
191                         }
192                         return builder.ToString();
193                 }
194         }
195 }