Move various development tools that are seldomly used to the
[mono.git] / mcs / class / Microsoft.JScript / Microsoft.JScript / DateConstructor.cs
1 //
2 // DateConstructor.cs:
3 //
4 // Author: Cesar Octavio Lopez Nataren
5 //
6 // (C) 2003, Cesar Octavio Lopez Nataren, <cesar@ciencias.unam.mx>
7 //
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31
32 namespace Microsoft.JScript {
33
34         public class DateConstructor : ScriptFunction {
35
36                 const double SECONDS_PER_MINUTE = 60.0;
37                 const double HOURS_PER_DAY = 24.0;
38                 const double MINUTES_PER_HOUR = 60.0;
39                 const double MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
40                 const double SECONDS_PER_DAY = MINUTES_PER_DAY * SECONDS_PER_MINUTE;
41                 const double MS_PER_SECOND = 1000.0;
42                 const double MS_PER_MINUTE = SECONDS_PER_MINUTE * MS_PER_SECOND;
43                 const double MS_PER_DAY = SECONDS_PER_DAY * MS_PER_SECOND;
44
45                 internal static DateConstructor Ctr = new DateConstructor ();
46                 
47                 internal DateConstructor ()
48                 {
49                 }
50                 
51                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasVarArgs)]
52                 public new DateObject CreateInstance (params Object[] args)
53                 {
54                         throw new NotImplementedException ();
55                 }
56
57                 public String Invoke ()
58                 {
59                         throw new NotImplementedException ();
60                 }
61
62                 [JSFunctionAttribute(0, JSBuiltin.Date_parse)]
63                 public static double parse (String str)
64                 {
65                         int year = -1;
66                         int mon = -1;
67                         int mday = -1;
68                         int hour = -1;
69                         int min = -1;
70                         int sec = -1;
71                         
72                         char c = '0';
73                         char si = '0';
74                         
75                         int i = 0;
76                         int n = -1;
77                         
78                         double tzoffset = -1;
79                         
80                         char prevc = '0';
81                         
82                         int limit = 0;
83                         
84                         bool seenplusminus = false;
85                         
86                         limit = str.Length;
87
88                         while (i < limit) {
89                                 c = str [i];
90                                 i++;
91                                 if (c <= ' ' || c == ',' || c == '-') {
92                                         if (i < limit) {
93                                                 si = str [i];
94                                                 if (c == '-' && '0' <= si && si <= '9')
95                                                         prevc = c;
96                                         }
97                                         continue;
98                                 }
99                                 
100                                 if (c == '(') { /* comments) */
101                                         int depth = 1;
102                                         while (i < limit) {
103                                                 c = str [i];
104                                                 i++;
105                                                 if (c == '(')
106                                                         depth++;
107                                                 else if (c == ')')
108                                                         if (--depth <= 0)
109                                                                 break;
110                                         }
111                                         continue;
112                                 }
113
114                                 if ('0' <= c && c <= '9') {
115                                         n = c - '0';
116                                         while (i < limit && '0' <= (c = str [i]) && c <= '9') {
117                                                 n = n * 10 + c - '0';
118                                                 i++;
119                                         }
120                                         
121                                         /* allow TZA before the year, so
122                                          * 'Wed Nov 05 21:49:11 GMT-0800 1997'
123                                          * works */
124
125                                         /* uses of seenplusminus allow : in TZA, so Java
126                                          * no-timezone style of GMT+4:30 works
127                                          */
128                                         if ((prevc == '+' || prevc == '-') /* && year >= 0 */) {
129                                                 /* make ':' case below change tzoffset */
130                                                 seenplusminus = true;
131
132                                                 /* offset */
133                                                 if (n < 24)
134                                                         n = n * 60; /* EG. "GMT-3" */
135                                                 else
136                                                         n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
137                                                 if (prevc == '+') /* plus means east of GMT */
138                                                         n = -n;
139                                                 if (tzoffset != 0 && tzoffset != -1)
140                                                         return Double.NaN;
141                                                 tzoffset = n;
142                                         } else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
143                                                 if (year >= 0)
144                                                         return Double.NaN;
145                                                 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
146                                                         year = n < 100 ? n + 1900 : n;
147                                                 else
148                                                         return Double.NaN;
149                                         } else if (c == ':') {
150                                                 if (hour < 0)
151                                                         hour = /* byte */ n;
152                                                 else if (min < 0)
153                                                         min = /* byte */ n;
154                                                 else
155                                                         return Double.NaN;
156                                         } else if (c == '/') {
157                                                 if (mon < 0)
158                                                         mon = /* byte */ n - 1;
159                                                 else if (mday < 0)
160                                                         mday = /* byte */ n;
161                                                 else
162                                                         return Double.NaN;
163                                         } else if (i < limit &&  c != ',' && c > ' ' && c != '-')
164                                                 return Double.NaN;
165                                         else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
166                                                 if (tzoffset < 0)
167                                                         tzoffset -= n;
168                                                 else
169                                                         tzoffset += n;
170                                         } else if (hour >= 0 && min < 0)
171                                                 min = /* byte */ n;
172                                         else if (min >= 0 && sec < 0)
173                                                 sec = /* byte */ n;
174                                         else if (mday < 0)
175                                                 mday = /* byte */ n;
176                                         else 
177                                                 return Double.NaN;
178                                         prevc = '0';
179                                 } else if (c == '/' || c == ':' || c == '+' || c == '-')
180                                         prevc = c;
181                                 else {
182                                         int st = i - 1;
183                                         while (i < limit) {
184                                                 c = str [i];
185                                                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
186                                                         break;
187                                                 i++;
188                                         }
189
190                                         int letterCount = i - st;
191                                         if (letterCount < 2)
192                                                 return Double.NaN;
193
194                                         /*
195                                          * Use ported code from jsdate.c rather than the locale-specific
196                                          * date-parsing code from Java, to keep js and rhino consistent.
197                                          * Is this the right strategy?
198                                          */
199                                         string wtb = "am;pm;"
200                                                 + "monday;tuesday;wednesday;thursday;friday;"
201                                                 + "saturday;sunday;"
202                                                 + "january;february;march;april;may;june;"
203                                                 + "july;august;september;october;november;december;"
204                                                 + "gmt;ut;utc;est;edt;cst;cdt;mst;mdt;pst;pdt;";
205
206                                         int index = 0;
207
208                                         for (int wtbOffset = 0;;) {
209                                                 int wtbNext = wtb.IndexOf (';', wtbOffset);
210
211                                                 if (wtbNext < 0)
212                                                         return Double.NaN;
213                                                 
214                                                 if (String.Compare (wtb, wtbOffset, str, st, letterCount, true) == 0)
215                                                         break;
216                                                 
217                                                 wtbOffset = wtbNext + 1;
218                                                 ++index;
219                                         }
220
221                                         if (index < 2) {
222                                                 /*
223                                                  * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
224                                                  * 12:30, instead of blindly adding 12 if PM.
225                                                  */
226                                                 if  (hour > 12 || hour < 0)
227                                                         return Double.NaN;
228                                                 else if (index == 0) {
229                                                         // AM
230                                                         if (hour == 12)
231                                                                 hour = 0;
232                                                 } else {
233                                                         // PM
234                                                         if (hour != 12)
235                                                                 hour += 12;
236                                                 }
237                                         } else if ((index -= 2) < 7) {
238                                                 // ignore week days
239                                         } else if ((index -= 7) < 12) {
240                                                 // month
241                                                 if (mon < 0)
242                                                         mon = index;
243                                                 else 
244                                                         return Double.NaN;
245                                         } else {
246                                                 index -= 12;
247                                                 // timezones
248                                                 switch (index) {
249                                                 case 0 /* gmt */:
250                                                         tzoffset = 0; 
251                                                         break;
252
253                                                 case 1 /* ut */:
254                                                         tzoffset = 0;
255                                                         break;
256                                                         
257                                                 case 2 /* utc */:
258                                                         tzoffset = 0;
259                                                         break;
260
261                                                 case 3 /* est */:
262                                                         tzoffset = 5 * 60;
263                                                         break;
264
265                                                 case 4 /* edt */:
266                                                         tzoffset = 4 * 60;
267                                                         break;
268
269                                                 case 5 /* cst */:
270                                                         tzoffset = 6 * 60;
271                                                         break;
272
273                                                 case 6 /* cdt */:
274                                                         tzoffset = 5 * 60;
275                                                         break;
276
277                                                 case 7 /* mst */:
278                                                         tzoffset = 7 * 60;
279                                                         break;
280                                                         
281                                                 case 8 /* mdt */:
282                                                         tzoffset = 6 * 60;
283                                                         break;
284                                                         
285                                                 case 9 /* pst */:
286                                                         tzoffset = 8 * 60;
287                                                         break;
288                                                         
289                                                 case 10 /* pdt */:
290                                                         tzoffset = 7 * 60;
291                                                         break;
292                                                 }
293                                         }
294                                 }
295                         }
296
297                         if (year < 0 || mon < 0 || mday < 0)
298                                 return Double.NaN;
299                         if (sec < 0)
300                                 sec = 0;
301                         if (min < 0)
302                                 min = 0;
303                         if (hour < 0)
304                                 hour = 0;
305                         
306                         double msec = msec_from_date (year, mon, mday, hour, min, sec, 0);
307                         
308                         if (tzoffset == -1) { // no time zone specified, have to use local 
309                                 Console.WriteLine ("FIXME: no time zone specified.");
310                                 throw new NotImplementedException ();
311                         } else
312                                 return msec + tzoffset * MS_PER_MINUTE;
313                 }
314
315                 //
316                 // find UTC time from given date... no 1900 correction!
317                 //
318                 static double msec_from_date (double year, double mon, double mday, double hour, double min, double sec, double msec)
319                 {
320                         double day, time, result;
321                         day = MakeDay (year, mon, mday);
322                         time = MakeTime (hour, min, sec, msec);
323                         result = MakeDate (day, time);
324                         return result;
325                 }
326
327                 static double MakeDay (double year, double month, double date)
328                 {
329                         year += Math.Floor (month / 12);
330                         month = month % 12;
331
332                         if (month < 0)
333                                 month += 12;
334                         
335                         double year_day = Math.Floor (TimeFromYear (year) / MS_PER_DAY);
336                         double month_day = DayFromMonth ((int) month, (int) year);
337                         return year_day + month_day + date - 1;
338                 }
339
340                 static double MakeTime (double hour, double min, double sec, double ms)
341                 {
342                         return ((hour * MINUTES_PER_HOUR + min) * SECONDS_PER_MINUTE + sec) * MS_PER_SECOND + ms;
343                 }
344
345                 static double MakeDate (double day, double time)
346                 {
347                         return day * MS_PER_DAY + time;
348                 }
349
350                 static double TimeFromYear (double y)
351                 {
352                         return DayFromYear (y) * MS_PER_DAY;
353                 }
354
355                 static double DayFromYear (double y)
356                 {
357                         return ((365 * ((y) - 1970) + Math.Floor (((y) - 1969) / 4.0) - Math.Floor(((y) - 1901) / 100.0) + Math.Floor (((y) - 1601) / 400.0)));
358                 }
359
360                 static double DayFromMonth (int m , int year)
361                 {
362                         int day = m * 30;
363
364                         if (m >= 7)
365                                 day += m / 2 - 1;
366                         else if (m >= 2)
367                                 day += (m - 1) / 2 - 1;
368                         else 
369                                 day += m;
370
371                         if (m >= 2 && IsLeapYear (year))
372                                 ++day;
373
374                         return day;
375                 }
376
377                 static bool IsLeapYear (int year)
378                 {
379                         return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
380                 }
381                 
382                 [JSFunctionAttribute(0, JSBuiltin.Date_UTC)]
383                 public static double UTC (Object year, Object month, Object date, 
384                                           Object hours, Object minutes, Object seconds, Object ms)
385                 {
386                         throw new NotImplementedException ();
387                 }
388         }
389 }