source: Scheduling/branches/BMX4Support/FMDateTime.cs@ 1418

Last change on this file since 1418 was 1174, checked in by Sam Habiel, 13 years ago

Absorbed all changed from Radiology Support branch.
Patient and Provider classes now serializable to address new bug: Availablity slots cannot be saved b/c these classes are not serializable.

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.