source: Scheduling/trunk/cs/bsdx0200GUISourceCode/bin/Release/FMDateTime.cs@ 1728

Last change on this file since 1728 was 1728, checked in by Faisal Sami, 8 years ago

ClinicalScheduling version 2.0.

File size: 29.0 KB
Line 
1/*
2 * Copyright (C) 2004-2009 Medsphere Systems Corporation
3 * All rights reserved.
4 *
5 * This source code contains the intellectual property
6 * of its copyright holder(s), and is made available
7 * under a license. If you do not know the terms of
8 * the license, please stop and do not read further.
9 *
10 * Please read LICENSES for detailed information about
11 * the license this source code file is available under.
12 * Questions should be directed to legal@medsphere.com
13 *
14 *
15 * Licensed under AGPL v3.
16 *
17 *
18
19 Modified by Sam Habiel to use in Scheduling GUI. Modified class licensed under LGPL.
20 */
21
22
23using System;
24using System.Globalization;
25using System.Text.RegularExpressions;
26
27namespace IndianHealthService.ClinicalScheduling
28{
29 public delegate void FMDateTimeHandler (FMDateTime time);
30 public delegate string FMDateStringHandler (string s);
31
32 [Serializable]
33 [System.Xml.Serialization.SoapType (Namespace="http://ws.medsphere.com")]
34 public class FMDateTime : IComparable, ICloneable
35 {
36 /* public properties */
37 [System.Xml.Serialization.SoapAttribute (Namespace="http://ws.medsphere.com")]
38 public DateTime DateTime {
39 get {
40 switch (Precision) {
41 case FMDateTimePrecision.YearOnly:
42 return new DateTime (Year, 1, 1);
43 case FMDateTimePrecision.YearMonthOnly:
44 return new DateTime (Year, Month, 1);
45 case FMDateTimePrecision.DateOnly:
46 return new DateTime (Year, Month, Day);
47 default:
48 return new DateTime (Year, Month, Day, Hour, Minute, Second);
49 }
50 }
51 }
52
53 public int Year {
54 get { return year; }
55 set { year = value; }
56 }
57
58 public int Month {
59 get { return month; }
60 set { month = value; }
61 }
62
63 public int Day {
64 get { return day; }
65 set { day = value; }
66 }
67
68 public int Hour {
69 get { return hour; }
70 set {
71 if (value > 24) {
72 throw new ArgumentException ("Hour cannot be greater than 24");
73 }
74
75 hour = value % 24;
76 }
77 }
78
79 public int Minute {
80 get { return minute; }
81 set {
82 if (value > 59) {
83 throw new ArgumentException ("Minute cannot be greater than 59");
84 }
85
86 minute = value;
87 }
88 }
89
90 public int Second {
91 get { return second; }
92 set {
93 if (value > 59) {
94 throw new ArgumentException ("Second cannot be greater than 59");
95 }
96
97 second = value;
98 }
99 }
100
101 public static FMDateTime ServerNow {
102 get { return FMDateTime.Create (DateTime.Now - server_offset); }
103 }
104
105 public string RelativeFMDateString {
106 get { return Raw; }
107 }
108
109 public string ShortHandString {
110 get {
111 if (Raw != null && date_format.IsMatch (Raw.Replace (" ", ""))) {
112 return Raw;
113 }
114 return ToString ();
115 }
116 }
117
118 public FMDateTime AtZero {
119 get {
120 FMDateTime d = (FMDateTime)this.Clone ();
121 d.Precision = FMDateTimePrecision.DateAndTime;
122 d.Hour = 0;
123 d.Minute = 0;
124 d.Second = 0;
125 return d;
126 }
127 }
128
129 public FMDateTime AtMidnight {
130 get {
131 FMDateTime d = (FMDateTime)this.Clone ();
132 d.Precision = FMDateTimePrecision.DateAndTime;
133 d.Hour = 23;
134 d.Minute = 59;
135 d.Second = 59;
136 return d;
137 }
138 }
139
140 public FMDateTime DateOnly {
141 get {
142 FMDateTime d = (FMDateTime)this.Clone ();
143 if (Precision != FMDateTimePrecision.DateAndTime) {
144 return d;
145 }
146 d.Precision = FMDateTimePrecision.DateOnly;
147 d.Hour = d.Minute = d.Second = 0;
148 return d;
149 }
150 }
151
152 public string FMDateString {
153 get {
154 switch (Precision) {
155 case FMDateTimePrecision.YearOnly:
156 return String.Format ("{0:000}", year - 1700);
157 case FMDateTimePrecision.YearMonthOnly:
158 return String.Format ("{0:000}{1:00}", year - 1700, month);
159 case FMDateTimePrecision.DateOnly:
160 return String.Format ("{0:000}{1:00}{2:00}", year - 1700, month, day);
161 case FMDateTimePrecision.DateAndTime:
162 default:
163 if (second > 0) {
164 return String.Format ("{0:000}{1:00}{2:00}.{3:00}{4:00}{5:00}",
165 year - 1700, month, day, hour, minute, second);
166 } else {
167 return String.Format ("{0:000}{1:00}{2:00}.{3:00}{4:00}",
168 year - 1700, month, day, hour, minute);
169 }
170 }
171 }
172 }
173
174 /* public fields */
175 public FMDateTimePrecision Precision;
176
177 [NonSerialized]
178 public static FMDateStringHandler ValidationMethod;
179
180 public static FMDateTime MinValue; // 1/1/1700
181 public static FMDateTime MaxValue; // 12/31/2699 12:59 PM
182
183 /* public methods */
184 static FMDateTime ()
185 {
186 // This is the equivalent of the FMDateTime string '0000101'
187 // We do this manually to avoid parsing overhead here.
188 MinValue = new FMDateTime (); // 1/1/1700
189 MinValue.Hour = MinValue.Minute = MinValue.Second = 0;
190 MinValue.Day = MinValue.Month = 1;
191 MinValue.Year = 1700;
192 MinValue.Precision = FMDateTimePrecision.DateOnly;
193
194 // This is the equivalent of the FMDateTime string '9991231.235959'
195 // We do this manually to avoid parsing overhead here.
196 MaxValue = new FMDateTime (); // 12/31/2699 12:59 PM
197 MaxValue.Hour = 23;
198 MaxValue.Minute = 59;
199 MaxValue.Second = 59;
200 MaxValue.Day = 31;
201 MaxValue.Month = 12;
202 MaxValue.Year = 2699;
203 MaxValue.Precision = FMDateTimePrecision.DateAndTime;
204 }
205
206 public FMDateTime ()
207 {
208 }
209
210 public FMDateTime(DateTime d)
211 : this(d, FMDateTimePrecision.DateAndTime)
212 {
213 }
214
215 public FMDateTime(DateTime d, FMDateTimePrecision precision)
216 {
217 if (d > MaxValue.DateTime || d < MinValue.DateTime)
218 return;
219
220 this.Precision = precision;
221 this.Year = d.Year;
222 this.Month = d.Month;
223 this.Day = d.Day;
224 this.Hour = d.Hour;
225 this.Minute = d.Minute;
226 this.Second = d.Second;
227 }
228
229 public static FMDateTime Create (DateTime d, FMDateTimePrecision precision)
230 {
231 if (d > MaxValue.DateTime || d < MinValue.DateTime) {
232 return null;
233 }
234
235 FMDateTime f = new FMDateTime ();
236 f.Precision = precision;
237 f.Year = d.Year;
238 f.Month = d.Month;
239 f.Day = d.Day;
240 f.Hour = d.Hour;
241 f.Minute = d.Minute;
242 f.Second = d.Second;
243
244 return f;
245 }
246
247 public static FMDateTime Create (DateTime d)
248 {
249 return Create (d, FMDateTimePrecision.DateAndTime);
250 }
251
252 /*public static string FMDate (this DateTime d)
253 {
254 return Create(d, FMDateTimePrecision.DateAndTime).FMDateString;
255 }*/
256
257
258 public static FMDateTime Parse (string str)
259 {
260 return Parse (str, FMDateTime.ValidationMethod);
261 }
262
263 public static FMDateTime Parse (string str,
264 FMDateStringHandler validation_method)
265 {
266 if (validation_method == null) {
267 throw new ArgumentNullException ("You must pass in a valid validation_method");
268 }
269
270 if (str == null) {
271 return null;
272 }
273
274 FMDateTime date = null;
275
276 str = str.Trim ();
277 if (str == "0" || str == String.Empty) {
278 return null;
279 }
280
281 if (str.IndexOf ("@") != -1) {
282 date = new FMDateTime ();
283
284 // string has a date and time part
285 string[] tokens = str.Split (new char[] {'@'}, 2);
286 if (ParseDatePart (tokens[0], ref date)
287 || ParseUsingDateTime (tokens[0], ref date)
288 || (validation_method != null
289 && ParseInternalFormat (validation_method (tokens[0]), ref date))) {
290 // Its been decided that if you have an
291 // invalid time part, that the entire
292 // string is invalid
293 if (!ParseTimePart (tokens[1], true, ref date)) {
294 return null;
295 }
296
297 date.Raw = str;
298 return date;
299 } else {
300 // Account for @0600
301 date = FMDateTime.ServerNow;
302 if (!ParseTimePart (tokens[1], true, ref date)) {
303 return null;
304 }
305 return date;
306 }
307 }
308
309 if (ParseDatePart (str, ref date)) {
310 date.Raw = str;
311 return date;
312 }
313
314 if (ParseTimePart (str, false, ref date)) {
315 FMDateTime now = ServerNow;
316 date.Year = now.Year;
317 date.Month = now.Month;
318 date.Day = now.Day;
319 return date;
320 }
321
322 if (ParseUsingDateTime (str, ref date)) {
323 return date;
324 }
325
326 if (ParseInternalFormat (str, ref date)) {
327 return date;
328 }
329
330 if (validation_method != null) {
331 if (ParseInternalFormat (validation_method (str), ref date)) {
332 return date;
333 }
334 return null;
335 }
336
337 if (date == null) {
338 Console.WriteLine ("WARNING: FMDateTime failed parsing '{0}'", str);
339 }
340
341 return date;
342 }
343
344 public static FMDateTime Parse (string str, FMDateTimePrecision precision)
345 {
346 FMDateTime date = FMDateTime.Parse (str);
347 if (date != null) {
348 date.Precision = precision;
349 }
350
351 return date;
352 }
353
354 public void PopulateFrom12HrTime (int hour, int minute, int second, bool is_pm)
355 {
356 if (hour < 12 && is_pm) {
357 hour += 12;
358 } else if (hour == 12 && !is_pm) {
359 hour = 0;
360 }
361
362 Hour = hour;
363 Minute = minute;
364 Second = second;
365 }
366
367 public bool IsFutureDate
368 {
369 get {
370 return (CompareTo (Precision, FMDateTime.ServerNow, FMDateTime.ServerNow.Precision) > 0);
371 }
372 }
373
374 public bool IsPastDate
375 {
376 get {
377 return (CompareTo (Precision, FMDateTime.ServerNow, FMDateTime.ServerNow.Precision) < 0);
378 }
379 }
380
381 public static void UpdateServerNow (FMDateTime server_now)
382 {
383 if (server_now != null) {
384 server_offset = (DateTime.Now - server_now.DateTime);
385 }
386 }
387
388 public override string ToString ()
389 {
390 switch (Precision) {
391 case FMDateTimePrecision.YearOnly:
392 return DateTime.ToString ("yyyy");
393 case FMDateTimePrecision.YearMonthOnly:
394 return DateTime.ToString ("Y");
395 case FMDateTimePrecision.DateOnly:
396 return DateTime.ToString ("d");
397 default:
398 return DateTime.ToString ("G");
399 }
400 }
401
402 public static string ToString (FMDateTime date)
403 {
404 if (date != null) {
405 return date.ToString ();
406 }
407 return String.Empty;
408 }
409
410 public string ToString (string format)
411 {
412 return DateTime.ToString (format);
413 }
414
415 public static string ToString (FMDateTime date, string format)
416 {
417 if (date != null) {
418 return date.ToString (format);
419 }
420 return String.Empty;
421 }
422
423 public string ToDateString ()
424 {
425 return DateTime.ToString ("d");
426 }
427
428 public static string ToDateString (FMDateTime date)
429 {
430 if (date != null) {
431 return date.ToDateString ();
432 }
433 return String.Empty;
434 }
435
436 public string ToTimeString ()
437 {
438 return DateTime.ToString ("t");
439 }
440
441 public static string ToTimeString (FMDateTime date)
442 {
443 if (date != null) {
444 return date.ToTimeString ();
445 }
446 return String.Empty;
447 }
448
449 public static string ToFMDateString (FMDateTime date)
450 {
451 if (date != null) {
452 return date.FMDateString;
453 }
454 return String.Empty;
455 }
456
457 /**
458 * Compares this FMDateTime instance with given FMDateTimePrecision this_p to dt
459 * using the given FMDateTimePrecision p.
460 */
461 public int CompareTo (FMDateTimePrecision this_p, FMDateTime dt, FMDateTimePrecision dt_p)
462 {
463 int r;
464 FMDateTimePrecision save_this_p = Precision;
465 FMDateTimePrecision save_dt_p = dt.Precision;
466 Precision = this_p;
467 dt.Precision = dt_p;
468 r = DateTime.CompareTo (dt.DateTime);
469 Precision = save_this_p;
470 dt.Precision = save_dt_p;
471 return r;
472 }
473
474 /**
475 * Implementation of IComparable interface.
476 */
477 public int CompareTo (object o)
478 {
479 if (o == null) {
480 return 1;
481 } else if (o is FMDateTime) {
482 FMDateTime f = (FMDateTime)o;
483 int r = DateTime.CompareTo (f.DateTime);
484 if (r == 0) {
485 // special cases of DateTime comparison:
486 // 1900 and January,1900 and 01/01/1900 are all
487 // represented as 01/01/1900
488 // TODAY@0 and TODAY are both represented as TODAY@0
489 // these are the cases where precision has the last word
490 if (Precision < f.Precision) {
491 r = -1;
492 } else if (Precision > f.Precision) {
493 r = 1;
494 }
495 }
496 return r;
497 } else if (o is DateTime) {
498 DateTime d = (DateTime)o;
499 return DateTime.CompareTo (d);
500 }
501
502 throw new ArgumentException ("Value is not a DateTime or FMDateTime.");
503 }
504
505 public static int Compare (FMDateTime a, FMDateTime b)
506 {
507 if (a == null && b == null) {
508 return 0;
509 } else if (a != null && b != null) {
510 return a.CompareTo (b);
511 /* We sort the non-null item before the null one for the mixed case */
512 } else if (a == null) {
513 return -1;
514 } else if (b == null) {
515 return 1;
516 }
517
518 // This code path really has no way of being hit.
519 return 0;
520 }
521
522 public override bool Equals (object o)
523 {
524 if (o == null) {
525 return false;
526 } else if (o is FMDateTime) {
527 FMDateTime f = (FMDateTime)o;
528
529 if (f.Precision != Precision) {
530 return false;
531 }
532
533 switch (Precision) {
534 case FMDateTimePrecision.YearOnly:
535 return Year == f.Year;
536 case FMDateTimePrecision.YearMonthOnly:
537 return Year == f.Year && Month == f.Month;
538 case FMDateTimePrecision.DateOnly:
539 return Year == f.Year && Month == f.Month && Day == f.Day;
540 case FMDateTimePrecision.DateAndTime:
541 default:
542 return Year == f.Year && Month == f.Month && Day == f.Day
543 && Hour == f.Hour && Minute == f.Minute && Second == f.Second;
544 }
545 }
546
547 throw new ArgumentException ("Value is not a FMDateTime.");
548 }
549
550 public override int GetHashCode ()
551 {
552 return (int)Precision + year + month + day + hour + minute + second;
553 }
554
555 /**
556 * This gets a hash code based upon the FMDateTime precision, so that
557 * an object can be stored based on DateOnly, for example, and if you
558 * try to look it up later using a different FMDateTime object that
559 * has the same date, but may have different time. Seconds are
560 * intentionally never factored into this hash code, even for DateAndTime
561 * cases. If you want to factor in seconds as well, just use GetHashCode().
562 *
563 * @return An integer hash code.
564 */
565 public int GetPrecisionAwareHashCode ()
566 {
567 int hash_code = (int)Precision;
568
569 switch (Precision)
570 {
571 case FMDateTimePrecision.YearOnly:
572 hash_code += year;
573 break;
574 case FMDateTimePrecision.YearMonthOnly:
575 hash_code += year;
576 hash_code += month;
577 break;
578 case FMDateTimePrecision.DateOnly:
579 hash_code += year;
580 hash_code += month;
581 hash_code += day;
582 break;
583 case FMDateTimePrecision.DateAndTime:
584 default:
585 hash_code += year;
586 hash_code += month;
587 hash_code += day;
588 hash_code += hour;
589 hash_code += minute;
590 break;
591 }
592
593 return hash_code;
594 }
595
596 public object Clone ()
597 {
598 FMDateTime d = new FMDateTime ();
599 d.Precision = Precision;
600 d.Year = year;
601 d.Month = month;
602 d.Day = day;
603 d.Hour = hour;
604 d.Minute = minute;
605 d.Second = second;
606 return d;
607 }
608
609 public static bool operator == (FMDateTime a, FMDateTime b)
610 {
611 object obj_a = (object)a;
612 object obj_b = (object)b;
613
614 if (obj_a == null && obj_b == null) {
615 return true;
616 } else if (obj_a != null && obj_b != null) {
617 return a.Equals (b);
618 } else {
619 return false;
620 }
621 }
622
623 public static bool operator != (FMDateTime a, FMDateTime b)
624 {
625 return !(a == b);
626 }
627
628 public static bool operator > (FMDateTime a, FMDateTime b)
629 {
630 if (a == null) {
631 throw new ArgumentException ("Left hand argument to comparison cannot be null.");
632 }
633
634 return (a.CompareTo (b) > 0);
635 }
636
637 public static bool operator >= (FMDateTime a, FMDateTime b)
638 {
639 if (a == null) {
640 throw new ArgumentException ("Left hand argument to comparison cannot be null.");
641 }
642
643 return (a.CompareTo (b) >= 0);
644 }
645
646 public static bool operator < (FMDateTime a, FMDateTime b)
647 {
648 if (a == null) {
649 throw new ArgumentException ("Left hand argument to comparison cannot be null.");
650 }
651
652 return (a.CompareTo (b) < 0);
653 }
654
655 public static bool operator <= (FMDateTime a, FMDateTime b)
656 {
657 if (a == null) {
658 throw new ArgumentException ("Left hand argument to comparison cannot be null.");
659 }
660
661 return (a.CompareTo (b) <= 0);
662 }
663
664 public static implicit operator FMDateTime (DateTime d)
665 {
666 return FMDateTime.Create (d);
667 }
668
669 /* protected properties */
670 protected string Raw;
671
672 /* private properties */
673 private static Calendar CurrentCalendar {
674 get { return CultureInfo.CurrentCulture.Calendar; }
675 }
676
677 /* private fields */
678 private int year, month, day, hour, minute, second;
679
680 // We do this here so we can lazy load the compiled regexes.
681 private static Regex internal_format {
682 get {
683 if (priv_internal_format == null) {
684 priv_internal_format = new Regex (@"^(\d{3})(\d{2})?(\d{2})?(\.(\d{1,2})?(\d{1,2})?(\d{1,2})?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
685 }
686
687 return priv_internal_format;
688 }
689 }
690 private static Regex priv_internal_format;
691
692 private static Regex date_format {
693 get {
694 if (priv_date_format == null) {
695 priv_date_format = new Regex (@"^(t(oday)?|n(ow)?)(([+-])(\d+)(w)?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
696 }
697
698 return priv_date_format;
699 }
700 }
701 private static Regex priv_date_format;
702
703 private static Regex time_format {
704 get {
705 if (priv_time_format == null) {
706 priv_time_format = new Regex (@"^(\d{1,2})(:(\d{2}))?(:(\d{2}))?(\s*)(AM|PM)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
707 }
708
709 return priv_time_format;
710 }
711 }
712 private static Regex priv_time_format;
713
714 private static TimeSpan server_offset = new TimeSpan ();
715
716 /* private methods */
717 private static bool ParseDatePart (string str, ref FMDateTime date)
718 {
719 FMDateTime orig_date = date;
720
721 string clean = str.Replace (" ", "");
722
723 // see if it matches (t|today|now) +/- some number
724 if (!date_format.IsMatch (clean)) {
725 return false;
726 }
727
728 Match m = date_format.Match (clean);
729 if (m != null) {
730 if (date == null) {
731 date = new FMDateTime ();
732 }
733
734 // be safe about dates like T-99999999
735 try {
736 int modifier = 0;
737 if (m.Groups[5].Value != String.Empty) {
738 int sign_bit = 1;
739 if (m.Groups[5].Value == "-") {
740 sign_bit = -1;
741 }
742
743 // Convert will bomb if the modifier
744 // won't fit into an int (it's invalid
745 // anyway)
746 modifier = sign_bit * Convert.ToInt32 (m.Groups[6].Value);
747 }
748
749 DateTime dt = ServerNow.DateTime;
750 if (m.Groups[7].Value.ToLower () == "w") {
751 dt = CurrentCalendar.AddWeeks (dt, modifier);
752 } else {
753 dt = CurrentCalendar.AddDays (dt, modifier);
754 }
755
756 date.Day = dt.Day;
757 date.Month = dt.Month;
758 date.Year = dt.Year;
759
760 if (m.Groups[1].Value.ToLower ().StartsWith ("n")) {
761 date.Precision = FMDateTimePrecision.DateAndTime;
762 date.Hour = dt.Hour;
763 date.Minute = dt.Minute;
764 date.Second = dt.Second;
765 } else {
766 date.Precision = FMDateTimePrecision.DateOnly;
767 date.Hour = date.Minute = date.Second = 0;
768 }
769 } catch {
770 date = orig_date;
771 return false;
772 }
773
774 return true;
775 }
776
777 date = orig_date;
778 return false;
779 }
780
781 private static bool ParseTimePart (string str, bool try_int_parse, ref FMDateTime date)
782 {
783 int time;
784 str = str.ToUpper ();
785 if (str == "NOON") {
786 if (date == null) {
787 date = new FMDateTime ();
788 }
789
790 date.Hour = 12;
791 date.Minute = date.Second = 0;
792
793 date.Precision = FMDateTimePrecision.DateAndTime;
794
795 return true;
796 } else if (str == "MID" || str == "MIDNIGHT") {
797 if (date == null) {
798 date = new FMDateTime ();
799 }
800
801 date.Hour = 23;
802 date.Minute = 59;
803 date.Second = 59;
804
805 date.Precision = FMDateTimePrecision.DateAndTime;
806
807 return true;
808 } else if (time_format.IsMatch (str)) {
809 Match m = time_format.Match (str);
810 if (m == null) {
811 return false;
812 }
813
814 int hour, minute, second;
815 int.TryParse(m.Groups[1].Value, out hour);
816 int.TryParse(m.Groups[3].Value, out minute);
817 int.TryParse(m.Groups[5].Value, out second);
818
819 if (m.Groups[7].Value == "PM") {
820 hour += 12;
821 }
822
823 if (hour == 24 && minute == 0 && second == 0) {
824 hour = 23;
825 minute = second = 59;
826 }
827
828 if (!ValidateTime (hour, minute, second)) {
829 return false;
830 }
831
832 if (date == null) {
833 date = new FMDateTime ();
834 }
835
836 date.Hour = hour;
837 date.Minute = minute;
838 date.Second = second;
839 date.Precision = FMDateTimePrecision.DateAndTime;
840
841 return true;
842 } else if (try_int_parse && int.TryParse(str, out time)) {
843
844 int hour, minute, second;
845 if (time <= 2359) {
846 hour = time / 100;
847 minute = time - (hour * 100);
848 second = 0;
849 } else if (time <= 235959) {
850 hour = time / 10000;
851 minute = (time - (hour * 10000)) / 100;
852 second = time - ((time / 100) * 100);
853 } else {
854 return false;
855 }
856
857 if (hour == 24 && minute == 0 && second == 0) {
858 hour = 23;
859 minute = second = 59;
860 }
861
862 if (!ValidateTime (hour, minute, second)) {
863 return false;
864 }
865
866 if (date == null) {
867 date = new FMDateTime ();
868 }
869
870 date.Hour = hour;
871 date.Minute = minute;
872 date.Second = second;
873 date.Precision = FMDateTimePrecision.DateAndTime;
874
875 return true;
876 }
877
878 return false;
879 }
880
881 private static bool ParseUsingDateTime (string str, ref FMDateTime date)
882 {
883 // we need to use DateTime.Parse to allow
884 // roundtripping of our ToString () methods
885
886 // LAMESPEC: There isn't any way to find out whether a
887 // DateTime contains a time part, or just a date part
888 // after calling Parse, so we have to specifically call
889 // ParseExact on a few known formats
890 try {
891 string[] formats = new string[] {
892 "yyyy"
893 };
894
895 DateTime d = DateTime.ParseExact (str, formats, null,
896 DateTimeStyles.AllowWhiteSpaces);
897 if (date == null) {
898 date = new FMDateTime ();
899 }
900
901 date.Year = d.Year;
902 date.Precision = FMDateTimePrecision.YearOnly;
903 return true;
904 } catch (FormatException) {
905 }
906
907 try {
908 string[] formats = new string[] {
909 "Y"
910 };
911
912 DateTime d = DateTime.ParseExact (str, formats, null,
913 DateTimeStyles.AllowWhiteSpaces);
914 if (date == null) {
915 date = new FMDateTime ();
916 }
917
918 date.Year = d.Year;
919 date.Month = d.Month;
920 date.Precision = FMDateTimePrecision.YearMonthOnly;
921 return true;
922 } catch (FormatException) {
923 }
924
925 try {
926 string[] formats = new string[] {
927 "d", "MM/dd/yy"
928 };
929
930 DateTime d = DateTime.ParseExact (str, formats, null,
931 DateTimeStyles.AllowWhiteSpaces);
932 if (date == null) {
933 date = new FMDateTime ();
934 }
935
936 date.Year = d.Year;
937 date.Month = d.Month;
938 date.Day = d.Day;
939 date.Precision = FMDateTimePrecision.DateOnly;
940 return true;
941 } catch (FormatException) {
942 }
943
944 try {
945 string[] formats = new string[] {
946 "g", "G", "MM/dd/yy hh:mm tt",
947 "MM/dd/yy h:mm tt"
948 };
949
950 DateTime d = DateTime.ParseExact (str, formats, null,
951 DateTimeStyles.AllowWhiteSpaces);
952 if (date == null) {
953 date = new FMDateTime ();
954 }
955
956 date.Year = d.Year;
957 date.Month = d.Month;
958 date.Day = d.Day;
959
960 date.Hour = d.Hour;
961 date.Minute = d.Minute;
962 date.Second = d.Second;
963
964 date.Precision = FMDateTimePrecision.DateAndTime;
965 return true;
966 } catch (FormatException) {
967 }
968
969 /* XXX: Disabiling this for now, since it sucks incredibly
970 // first try parsing date & time
971 try {
972 string[] date_time_formats = new string[] {
973 "dddd*, MMMM* dd, yyyy HH*:mm* tt*", "f",
974 "dddd*, MMMM* dd, yyyy HH*:mm*:ss* tt*", "F",
975 "MM/dd/yyyy HH*:mm* tt*", "g",
976 "MM/dd/yyyy HH*:mm*:ss* tt*", "G",
977 "dddd*, MMMM* dd, yyyy HH*:mm*:ss* tt*", "U"
978 };
979
980 DateTime d = DateTime.ParseExact (str, date_time_formats, null,
981 DateTimeStyles.AllowWhiteSpaces);
982 date.Year = d.Year;
983 date.Month = d.Month;
984 date.Day = d.Day;
985 date.Hour = d.Hour;
986 date.Minute = d.Minute;
987 date.Second = d.Second;
988 date.Precision = FMDateTimePrecision.DateAndTime;
989 return true;
990 } catch { }
991
992 // fall back on just parsing a date
993 try {
994 string[] date_formats = new string[] {
995 "d", "D", "m", "M", "y", "Y"
996 };
997
998 DateTime d = DateTime.ParseExact (str, date_formats, null,
999 DateTimeStyles.AllowWhiteSpaces);
1000 date.Year = d.Year;
1001 date.Month = d.Month;
1002 date.Day = d.Day;
1003 date.Precision = FMDateTimePrecision.DateOnly;
1004 return true;
1005 } catch { }
1006
1007 // if nothing else, try a couple of time formats
1008 try {
1009 string[] time_formats = new string[] {
1010 "HH*:mm* tt*", "t",
1011 "HH*:mm*:ss* tt*", "T"
1012 };
1013
1014 DateTime d = DateTime.ParseExact (str, time_formats, null,
1015 DateTimeStyles.AllowWhiteSpaces);
1016 date = FMDateTime.ServerNow;
1017 date.Hour = d.Hour;
1018 date.Minute = d.Minute;
1019 date.Second = d.Second;
1020 date.Precision = FMDateTimePrecision.DateAndTime;
1021 return true;
1022 } catch { }
1023 */
1024
1025 return false;
1026 }
1027
1028 private static bool ParseInternalFormat (string str, ref FMDateTime date)
1029 {
1030 FMDateTime orig_date = date;
1031
1032 if (internal_format.IsMatch (str)) {
1033 Match m = internal_format.Match (str);
1034 if (m != null && m.Groups.Count == 8) {
1035 int year, month, day, hour, minute, second;
1036
1037 int.TryParse(m.Groups[1].Value, out year);
1038 year += 1700;
1039
1040 int.TryParse(m.Groups[2].Value, out month);
1041 int.TryParse(m.Groups[3].Value, out day);
1042 int.TryParse(m.Groups[5].Value, out hour);
1043 int.TryParse(m.Groups[6].Value, out minute);
1044 int.TryParse(m.Groups[7].Value, out second);
1045
1046 // 1 digit hours apparently have just
1047 // had the trailing 0 dropped. Go figure.
1048 if (m.Groups[5].Value.Length == 1) {
1049 hour *= 10;
1050 }
1051
1052 // 1 digit minutes do too
1053 if (m.Groups[6].Value.Length == 1) {
1054 minute *= 10;
1055 }
1056
1057 // 1 digit seconds aren't to be left out
1058 if (m.Groups[7].Value.Length == 1) {
1059 second *= 10;
1060 }
1061
1062 if (!ValidateYear (year)) {
1063 return false;
1064 }
1065
1066 if (date == null) {
1067 date = new FMDateTime ();
1068 }
1069
1070 date.Year = year;
1071
1072 date.Precision = FMDateTimePrecision.YearOnly;
1073 if (m.Groups[5].Value != String.Empty
1074 && month > 0 && day > 0 && hour > 0) {
1075 if (!ValidateDate (year, month, day)
1076 || !ValidateTime (hour, minute, second)) {
1077 date = orig_date;
1078 return false;
1079 }
1080
1081 date.Month = month;
1082 date.Day = day;
1083 date.Hour = hour;
1084 date.Minute = minute;
1085 date.Second = second;
1086
1087 date.Precision = FMDateTimePrecision.DateAndTime;
1088 } else if (m.Groups[3].Value != String.Empty
1089 && month > 0 && day > 0) {
1090 if (!ValidateDate (year, month, day)) {
1091 date = orig_date;
1092 return false;
1093 }
1094
1095 date.Month = month;
1096 date.Day = day;
1097
1098 date.Precision = FMDateTimePrecision.DateOnly;
1099 } else if (m.Groups[2].Value != String.Empty
1100 && month > 0) {
1101 if (!ValidateYearMonth (year, month)) {
1102 date = orig_date;
1103 return false;
1104 }
1105
1106 date.Month = month;
1107
1108 date.Precision = FMDateTimePrecision.YearMonthOnly;
1109 }
1110
1111 return true;
1112 }
1113 }
1114
1115 return false;
1116 }
1117
1118 private static bool ValidateYear (int year)
1119 {
1120 // Sadly, we can't use MaxValue and MinValue due to
1121 // this function being used in the
1122 // parsing and initialization of those values
1123 if (year < 1700 || year > 2699) {
1124 return false;
1125 }
1126
1127 return true;
1128 }
1129
1130 private static bool ValidateYearMonth (int year, int month)
1131 {
1132 if (!ValidateYear (year)) {
1133 return false;
1134 }
1135
1136 int num_months = CurrentCalendar.GetMonthsInYear (year);
1137 if (month < 1 || month > num_months) {
1138 return false;
1139 }
1140
1141 return true;
1142 }
1143
1144 private static bool ValidateDate (int year, int month, int day)
1145 {
1146 if (!ValidateYearMonth (year, month)) {
1147 return false;
1148 }
1149
1150 int num_days = CurrentCalendar.GetDaysInMonth (year, month);
1151 if (day < 1 || day > num_days) {
1152 return false;
1153 }
1154
1155 return true;
1156 }
1157
1158 private static bool ValidateTime (int hour, int minute, int second)
1159 {
1160 if (hour < 0 || hour > 24) {
1161 return false;
1162 }
1163
1164 if (minute < 0 || minute > 59) {
1165 return false;
1166 }
1167
1168 if (second < 0 || second > 59) {
1169 return false;
1170 }
1171
1172 if (hour == 24 && (minute > 0 || second > 0)) {
1173 return false;
1174 }
1175
1176 return true;
1177 }
1178 }
1179
1180 public enum FMDateTimePrecision {
1181 YearOnly,
1182 YearMonthOnly,
1183 DateOnly,
1184 DateAndTime
1185 }
1186}
Note: See TracBrowser for help on using the repository browser.