RexxDateTime.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 #include "RexxCore.h"
39 #include "RexxDateTime.hpp"
40 #include "Numerics.hpp"
41 
42 // the base time used for Time('T');
44 // the largest possible date we can handle
45 RexxDateTime RexxDateTime::maxBaseTime(9999, 12, 31, 23, 59, 59, 999999);
46 
47 // formatting versions of the days
48 const char *RexxDateTime::dayNames[] =
49 {
50  "Monday",
51  "Tuesday",
52  "Wednesday",
53  "Thursday",
54  "Friday",
55  "Saturday",
56  "Sunday",
57 };
58 
59 // text names of the months
60 const char *RexxDateTime::monthNames[] =
61 {
62  "January",
63  "February",
64  "March",
65  "April",
66  "May",
67  "June",
68  "July",
69  "August",
70  "September",
71  "October",
72  "November",
73  "December"
74 };
75 
76 
77 // the day in year starting point for each of the months (non-leap year)
79 {
80  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
81 };
82 
83 // the day in year starting point for each of the months in a leap year.
85 {
86  0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
87 };
88 
89 // the number of days in each of the months
91 {
92  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
93 };
94 
95 
96 /**
97 
98  * Default constructor for a RexxDateTime instance. This
99  * initializes the time to all zeros.
100  */
102 {
103  clear();
104 }
105 
106 
107 /**
108  * Create a RexxDateTime instance from a basetime value.
109  *
110  * @param basetime The basetime for this instance.
111  */
113 {
114  clear();
115  setBaseTime(basetime);
116 }
117 
118 
119 
120 
121 /**
122  * Create a RexxDateTime instance from a baseDate value.
123  *
124  * @param basedate
125  * @param dummy Dummy argument to allow constructor overload to work.
126  */
128 {
129  clear();
130  setBaseDate(basedate);
131 }
132 
133 
134 /**
135  * Create a RexxDateTime instance from a year/month/day value.
136  *
137  * @param y The current year.
138  * @param m The month.
139  * @param d The day.
140  */
142 {
143  clear();
144  year = (int)y;
145  month = (int)m;
146  day = (int)d;
147 }
148 
149 
150 /**
151  * Create a RexxDateTime instance from a fully resolved date
152  * time value.
153  *
154  * @param y The date year.
155  * @param m The date month.
156  * @param d The date day.
157  * @param h The time hour.
158  * @param i The time minues
159  * @param s The time secons.
160  * @param u The time microseconds.
161  */
163 {
164  year = (int)y;
165  month = (int)m;
166  day = (int)d;
167  hours = (int)h;
168  minutes = (int)i;
169  seconds = (int)s;
170  microseconds = (int)u;
171  valid = false;
172 }
173 
174 
175 /**
176  * Retrieve the basedate from the timestamp. The basedate
177  * is the number of days since 01 Jan 0001, calculated using
178  * a Gregorian calendar system for the entire date range.
179  *
180  * @return The integer value for the basedate, in days.
181  */
183 {
184  wholenumber_t tempYear = year - 1;
185  // calculate base date up to beginning of current year
186  wholenumber_t basedate = (tempYear * 365) + (tempYear / 4) - (tempYear / 100) + (tempYear / 400);
187  // then add in days in this year
188  basedate += getYearDay() - 1;
189  return basedate;
190 }
191 
192 
193 /**
194  * Get the time in seconds from midnight in the current
195  * timestamp day.
196  *
197  * @return The calculated integer time, in seconds.
198  */
200 {
202 }
203 
204 
205 /**
206  * Calculate the basetime, returned as the number of microseconds
207  * since 00:00:00.000000 on 01 Jan 0001. The basetime is
208  * calculated using the same Gregorian calendar system used
209  * to calculate the basedate.
210  *
211  * @return The basetime as a 64-bit integer value.
212  */
214 {
215  // get the basedate and convert to seconds (BIG number)
216  int64_t time = getBaseDate();
217  time *= (int64_t)SECONDS_IN_DAY;
218  // now add in the seconds in the day and convert to micro seconds
219  time += getTimeSeconds();
220  time *= (int64_t)MICROSECONDS;
221  // and finally add in the microseconds bit
222  time += microseconds;
223  return time;
224 }
225 
226 
227 /**
228  * Return this time as a UTC timestamp (adjusted for
229  * the timezone offset);
230  *
231  * @return
232  */
234 {
235  return getBaseTime() + timeZoneOffset;
236 }
237 
238 
239 /**
240  * Calculate the basetime, returned as the number of seconds
241  * since 00:00:00.000000 on 01 Jan 1970. The basetime is
242  * calculated using the same Gregorian calendar system used to
243  * calculate the basedate. Times prior to 01 Jan 1970 are
244  * returned as a negative number.
245  *
246  * @return The unix time as a 64-bit integer value.
247  */
249 {
250  // subtract the baseline time and convert to seconds.
252 }
253 
254 
255 /**
256  * Set the date from a basedate value. The basedate is the
257  * number of days since 01 Jan 0001.
258  *
259  * @param basedays The basedays value (must be a positive integer).
260  */
262 {
263  wholenumber_t basedays = base;
264 
265  // make sure this is in range.
266  if (basedays < 0 || basedays > maxBaseTime.getBaseDate())
267  {
268  return false;
269  }
270 
271  // reset all of the fields
272  clear();
273 
274  basedays++; //
275  // get to start of current 400 years
276  year = (int)((basedays / OLYMPIAD_DAYS) * OLYMPIAD);
277  // adjust the input date downward
278  basedays -= BASE_DAYS(year);
279  // if this ended on a boundary, then this was the last day of
280  // a leap year
281  if (basedays == 0)
282  {
283  basedays = YEAR_DAYS + 1;
284  }
285  else
286  {
287  // now adjust to the start of the current century
288  year += (int)((basedays / CENTURY_DAYS) * CENTURY);
289  basedays = basedays % CENTURY_DAYS;
290  // another boundary condition for the century.
291  // Since this is a non-olympiad, it's the last day of
292  // a non-leap year.
293  if (basedays == 0)
294  {
295  basedays = YEAR_DAYS;
296  }
297  else
298  {
299  // now get to the start of next 4-year leap cycle
300  year += (int)((basedays / LEAP_DAYS) * LEAP_CYCLE);
301  basedays = basedays % LEAP_DAYS;
302  // yet another boundary condition. if 0, the
303  // current day is the last day of the leap year
304  if (basedays == 0)
305  {
306  basedays = YEAR_DAYS + 1;
307  }
308  else
309  {
310  // still may have a few more years to process
311  year += (int)(basedays / YEAR_DAYS);
312  // this will be the actual year day
313  basedays = basedays % YEAR_DAYS;
314  // if basedays is now 0, we're on the last day of the
315  // year boundary, so we need to set it accordingly.
316  // if not at the boundary, we need to adjust the year
317  // because we're currently zero-based,
318  if (basedays == 0)
319  {
320  basedays = YEAR_DAYS;
321  }
322  else
323  {
324  year++;
325  }
326  }
327  }
328  }
329 
330  // ok, the year day will differ depending on whether this is a leap year, or not.
331  int *monthTable = LeapYear(year) ? leapMonthStarts : monthStarts;
332 
333  // now find the relevant month and calculate the month/day fields
334  for (int i = 0; ; i++)
335  {
336  // have we reached the month yet?
337  if (monthTable[i] >= basedays)
338  {
339  month = i; // the index is the month number
340  // and adjust for the days
341  day = (int)(basedays - monthTable[i - 1]);
342  break; /* finished */
343  }
344  }
345  return true;
346 }
347 
348 
349 /**
350  * Set the date and time from a basetime value. The basetime
351  * is the number of microseconds from 00:00:00 on 01 Jan 0001.
352  *
353  * @param basetime The input timestamp, in microseconds.
354  */
356 {
357  // make sure this is in range
358  if (basetime < 0 || basetime > maxBaseTime.getBaseTime())
359  {
360  return false;
361  }
362 
363  // first subtract out the date portion and process it
364  int64_t basedays = basetime / MICROSECONDS_IN_DAY;
365  basetime -= basedays * MICROSECONDS_IN_DAY;
366 
367  // NOTE: setting the basedate clears all of the time fields
368  setBaseDate((wholenumber_t)basedays);
369 
370  // extract out the microseconds bit
371  microseconds = (int)(basetime % MICROSECONDS);
372  // and then down to a seconds field
373  basetime = basetime / MICROSECONDS;
374  // now pull out the other time fields
375  hours = (int)(basetime / SECONDS_IN_HOUR);
376  basetime = basetime % SECONDS_IN_HOUR;
377  minutes = (int)(basetime / SECONDS_IN_MINUTE);
378  seconds = (int)(basetime % SECONDS_IN_MINUTE);
379 
380  return true;
381 }
382 
383 
384 /**
385  * Set the date and time from a unix time value. The unix time
386  * is the number of seconds from 00:00:00 on 01 Jan 1970. The
387  * value may be either positive or negative.
388  *
389  * @param basetime The input timestamp, in seconds.
390  */
392 {
393  // calculate this as a base time value.
394  int64_t adjustedTime = (basetime * (int64_t)MICROSECONDS) + unixBaseTime.getBaseTime();
395  // set the value based on the adjustment.
396  return setBaseTime(adjustedTime);
397 }
398 
399 
400 /**
401  * Set the time stamp using the number of seconds since midnight.
402  *
403  * @param basetime The basetime, in seconds.
404  */
406 {
407  // clear everything
408  clear();
409  // extract the field information
410  hours = (int)(basetime / SECONDS_IN_HOUR);
411  basetime = (int)(basetime % SECONDS_IN_HOUR);
412  minutes = (int)(basetime / SECONDS_IN_MINUTE);
413  seconds = (int)(basetime % SECONDS_IN_MINUTE);
414 }
415 
416 /**
417  * Clear all of the values within a time object. This will
418  * reset the time to an invalid time value.
419  */
421 {
422  year = 0;
423  month = 0;
424  day = 0;
425  hours = 0;
426  minutes = 0;
427  seconds = 0;
428  microseconds = 0;
429  valid = false;
430 }
431 
432 /**
433  * Set the date value using a year/day in year pair.
434  *
435  * @param newYear The year to set.
436  * @param newDay The day within the year to set.
437  */
439 {
440  // set the year, then use that to calculate the month and day information
441  year = (int)newYear;
442  setDay(newDay);
443 }
444 
445 
446 /**
447  * Set the day in the year from a day offset. The year must
448  * be valid in the timestamp for this to work properly, as
449  * it is necessary to know if the current year is a leap year
450  * to properly calculate the month and day.
451  *
452  * @param basedays The days from the start of the year.
453  */
455 {
456  // ok, the year day will differ depending on whether this is a leap year, or not.
457  int *monthTable = LeapYear(year) ? leapMonthStarts : monthStarts;
458 
459  // now find the relevant month and calculate the month/day fields
460  for (int i = 0; ; i++)
461  {
462  // have we reached the month yet?
463  if (monthTable[i] >= (int)basedays)
464  {
465  month = i; // the index is the month number
466  // and adjust for the days
467  day = (int)basedays - monthTable[i - 1];
468  break; /* finished */
469  }
470  }
471 
472 }
473 
474 
475 /**
476  * Return the number of days since the start of the year.
477  *
478  * @return The count of days since the beginning of the contained year.
479  */
481 {
482  /* now calculate the yearday */
483  wholenumber_t yearday = (monthStarts[month - 1] + day);
484  // if after February in a leap year, add one more day
485  if (month > 2 && isLeapYear())
486  {
487  yearday++;
488  }
489  return yearday;
490 }
491 
492 
493 /**
494  * Return the day in the week as an integer value. Sundy is 0,
495  * Monday is 1, etc.
496  *
497  * @return The integer offset for the day of the week.
498  */
500 {
501  return getBaseDate() % 7;
502 }
503 
504 
505 /**
506  * Return the current day of the week, as a name.
507  *
508  * @return The string name of the timestamp day.
509  */
511 {
512  return dayNames[getWeekDay()];
513 }
514 
515 
516 /**
517  * Get the name of the timestamp month, as a string.
518  *
519  * @return The string name of the timestamp month.
520  */
522 {
523  return monthNames[month - 1];
524 }
525 
526 
527 /**
528  * Parse a date in 'N'ormal format into the timestamp.
529  *
530  * @param date The string version of the date.
531  * @param sep The field separator character used in the date. This argument
532  * can be NULL, which means use the default separator.
533  *
534  * @return true if the date parses correctly, false for any parsing errors.
535  */
536 bool RexxDateTime::parseNormalDate(const char *date, const char *sep)
537 {
538  return parseDateTimeFormat(date, "DD/MMM/YYYY", sep == NULL ? " " : sep, 0);
539 
540 }
541 
542 
543 /**
544  * Parse a date in 'S'tandard format into the timestamp.
545  *
546  * @param date The string version of the date.
547  * @param sep The field separator character used in the date. This argument
548  * can be NULL, which means use the default separator.
549  *
550  * @return true if the date parses correctly, false for any parsing errors.
551  */
552 bool RexxDateTime::parseStandardDate(const char *date, const char *sep)
553 {
554  return parseDateTimeFormat(date, "YYYY/mm/dd", sep == NULL ? "" : sep, 0);
555 }
556 
557 
558 /**
559  * Parse a date in 'E'uropean format into the timestamp.
560  *
561  * @param date The string version of the date.
562  * @param sep The field separator character used in the date. This argument
563  * can be NULL, which means use the default separator.
564  * @param currentYear
565  * The current year used to fill in the centuries portion of the
566  * date.
567  *
568  * @return true if the date parses correctly, false for any parsing errors.
569  */
570 bool RexxDateTime::parseEuropeanDate(const char *date, const char *sep, wholenumber_t currentYear)
571 {
572  return parseDateTimeFormat(date, "dd/mm/yy", sep == NULL ? "/" : sep, currentYear);
573 }
574 
575 
576 /**
577  * Parse a date in 'U'sa format into the timestamp.
578  *
579  * @param date The string version of the date.
580  * @param sep The field separator character used in the date. This argument
581  * can be NULL, which means use the default separator.
582  * @param currentYear
583  * The current year used to fill in the centuries portion of the
584  * date.
585  *
586  * @return true if the date parses correctly, false for any parsing errors.
587  */
588 bool RexxDateTime::parseUsaDate(const char *date, const char *sep, wholenumber_t currentYear)
589 {
590  return parseDateTimeFormat(date, "mm/dd/yy", sep == NULL ? "/" : sep, currentYear);
591 }
592 
593 
594 /**
595  * Parse a date in 'O'rderd format into the timestamp.
596  *
597  * @param date The string version of the date.
598  * @param sep The field separator character used in the date. This argument
599  * can be NULL, which means use the default separator.
600  * @param currentYear
601  * The current year used to fill in the centuries portion of the
602  * date.
603  *
604  * @return true if the date parses correctly, false for any parsing errors.
605  */
606 bool RexxDateTime::parseOrderedDate(const char *date, const char *sep, wholenumber_t currentYear)
607 {
608  return parseDateTimeFormat(date, "yy/mm/dd", sep == NULL ? "/" : sep, currentYear);
609 }
610 
611 
612 /**
613  * Parse a time in 'N'ormal format into the timestamp.
614  *
615  * @param date The string version of the date.
616  *
617  * @return true if the date parses correctly, false for any parsing errors.
618  */
619 bool RexxDateTime::parseNormalTime(const char *time)
620 {
621  return parseDateTimeFormat(time, "HH:ii:ss", "", 0);
622 }
623 
624 
625 /**
626  * Parse a time in 'C'ivil format into the timestamp.
627  *
628  * @param date The string version of the date.
629  *
630  * @return true if the date parses correctly, false for any parsing errors.
631  */
632 bool RexxDateTime::parseCivilTime(const char *time)
633 {
634  return parseDateTimeFormat(time, "cc:iiCC", "", 0);
635 }
636 
637 
638 /**
639  * Parse a time in 'L'ong format into the timestamp.
640  *
641  * @param date The string version of the date.
642  *
643  * @return true if the date parses correctly, false for any parsing errors.
644  */
645 bool RexxDateTime::parseLongTime(const char *time)
646 {
647  return parseDateTimeFormat(time, "HH:ii:ss.uuuuuu", "", 0);
648 }
649 
650 
651 /**
652  * Set the time from an hours value. This sets all other
653  * time elements to zero.
654  *
655  * @param h The hours value.
656  *
657  * @return true if the time was set properly. false if the hours
658  * value was invalid.
659  */
661 {
662  if (h < 0 || h >= HOURS_IN_DAY)
663  {
664  return false;
665  }
666  hours = (int)h;
667  minutes = 0;
668  seconds = 0;
669  microseconds = 0;
670  return true;
671 }
672 
673 
674 /**
675  * Set the time from a seconds value. This sets the hour and
676  * minute values. The microseconds field is set to zero.
677  *
678  * @param s The seconds value.
679  *
680  * @return true if the time was set properly. false if the
681  * value was invalid.
682  */
684 {
685  if (s < 0 || s >= SECONDS_IN_DAY)
686  {
687  return false;
688  }
689 
690  // now break down into component parts
691  hours = (int)s / SECONDS_IN_HOUR;
692  s = (int)s % SECONDS_IN_HOUR;
693  minutes = (int)s / SECONDS_IN_MINUTE;
694  seconds = (int)s % SECONDS_IN_MINUTE;
695  microseconds = 0;
696  return true;
697 }
698 
699 
700 /**
701  * Set the time from a minutes value. This sets the hour and
702  * minute values. The seconds and microseconds field are set to
703  * zero.
704  *
705  * @param m The minutes value.
706  *
707  * @return true if the time was set properly. false if the
708  * value was invalid.
709  */
711 {
712  if (m < 0 || m >= MINUTES_IN_DAY)
713  {
714  return false;
715  }
716 
717  // now break down into component parts
718  hours = (int)m / MINUTES_IN_HOUR;
719  minutes = (int)m % MINUTES_IN_HOUR;
720  seconds = 0;
721  microseconds = 0;
722  return true;
723 }
724 
725 
726 /**
727  * Adjust the timestamp to a new timezone offset.
728  *
729  * @param o The offset value (in microseconds)
730  *
731  * @return true if the time was set properly. false if the
732  * value was invalid.
733  */
735 {
736  // we set the time using a UTC time adjusted by the offset,
737  int64_t base = getUTCBaseTime();
738  setBaseTime(base - o);
739  // then set the offset afterward
740  timeZoneOffset = o;
741  return true;
742 }
743 
744 
745 /**
746  * Parse an input date or time vs. a format template that
747  * describes the various fields.
748  *
749  * Format specifiers are:
750  *
751  * '/' A sepaarator is expected (passed in)
752  * 'm' Start of a month specification
753  * 'd' Start of a day specification
754  * 'y' Start of a 2-digit year spec
755  * 'Y' Start of a 4-digit year spec
756  * 'M' Start of a "named" 3 character month
757  * 'h' Start of a 12-hour hour field
758  * 'H' Start of a 24-hour hour field
759  * 'i' Start of a mInutes field
760  * 's' Start of a seconds field
761  * 'u' Start of a microseconds field
762  * 'C' Start of a Civil time meridian designation
763  * 'c' Start of a Civil time hour (no leading blanks)
764  * ':' ':' expected at this position
765  * '.' '.' expected at this position
766  *
767  * @param date The input date (or time).
768  * @param format The format the date/time is expected to be in.
769  * @param sep A variable separator character expected to be found in the
770  * input. Separators are marked with "/" in the format template.
771  * The separator can be a null string "".
772  * @param currentYear
773  * The current year value (used for formats that don't include
774  * century information).
775  *
776  * @return true if the date parses correctly, false otherwise.
777  */
778 bool RexxDateTime::parseDateTimeFormat(const char *date, const char *format, const char *sep, wholenumber_t currentYear)
779 {
780  day = 1; // set some defaults for the date portion
781  month = 1;
782  year = 1;
783  const char *inputscan = date; // and get some scanning pointers
784  const char *formatscan = format;
785 
786  if (strlen(date) > strlen(format)) // a mismatch on the lengths? this can't be correct
787  {
788  return false;
789  }
790  // scan through this character-by-character, parsing out the pieces
791  while (*formatscan != '\0')
792  {
793  switch (*formatscan)
794  {
795  // month spec, which requires two digits
796  case 'm':
797  // parse out the number version
798  if (!getNumber(inputscan, MONTH_SIZE, &month, MONTHS))
799  {
800  return false;
801  }
802  // must be in a valid range
803  if (month > MONTHS)
804  {
805  return false;
806  }
807  inputscan += MONTH_SIZE;
808  formatscan += MONTH_SIZE;
809  break;
810  // day specifier, which also requires a fixed size
811  case 'd':
812  // parse out the number version
813  if (!getNumber(inputscan, DAY_SIZE, &day))
814  {
815  return false;
816  }
817  // we can't validate the day range until we know the month and year
818  inputscan += DAY_SIZE; /* step both pointers */
819  formatscan += DAY_SIZE;
820  break;
821 
822  // variable length day specifier...this can be 1-2 digits
823  case 'D':
824  {
825  // We accept 1 or 2 digits here, so check the second to see if
826  // it's a digit, which will determine our length to scan.
827  int numberLength = 1;
828 
829  if (isdigit(*(inputscan + 1)))
830  {
831  numberLength = 2;
832  }
833  // parse out the number version
834  if (!getNumber(inputscan, numberLength, &day))
835  {
836  return false;
837  }
838  inputscan += numberLength; // this is stepped a variable amount
839  formatscan += DAY_SIZE; // and this one is fixed length
840  break;
841  }
842 
843  // 12 hour field format
844  case 'h':
845  // parse out the number version
846  if (!getNumber(inputscan, HOURS_SIZE, &hours, MAXCIVILHOURS))
847  {
848  return false;
849  }
850  inputscan += HOURS_SIZE;
851  formatscan += HOURS_SIZE;
852  break;
853 
854  // 24 hours format field
855  case 'H':
856  // parse out the number version
857  if (!getNumber(inputscan, HOURS_SIZE, &hours, MAXHOURS))
858  {
859  return false;
860  }
861  inputscan += HOURS_SIZE; /* step both pointers */
862  formatscan += HOURS_SIZE;
863  break;
864  // minutes field
865  case 'i':
866  // parse out the number version
867  if (!getNumber(inputscan, MINUTES_SIZE, &minutes, MAXMINUTES))
868  {
869  return false;
870  }
871  inputscan += MINUTES_SIZE; /* step both pointers */
872  formatscan += MINUTES_SIZE;
873  break;
874 
875  // seconds field
876  case 's':
877  // parse out the number version
878  if (!getNumber(inputscan, SECONDS_SIZE, &seconds, MAXSECONDS))
879  {
880  return false;
881  }
882  inputscan += SECONDS_SIZE; /* step both pointers */
883  formatscan += SECONDS_SIZE;
884  break;
885 
886  // microseconds in a long time
887  case 'u':
888  // parse out the number version
889  if (!getNumber(inputscan, MICRO_SIZE, &microseconds))
890  {
891  return false;
892  }
893  inputscan += MICRO_SIZE; /* step both pointers */
894  formatscan += MICRO_SIZE;
895  break;
896  // two digit year value
897  case 'y':
898  // parse out the number version
899  if (!getNumber(inputscan, SHORT_YEAR, &year))
900  {
901  return false;
902  }
903  // add in the current centry
904  year += (int)((currentYear / 100) * 100);
905  // did we go back in time by doing that?
906  // if by more than 50 years, we need to use the sliding window
907  if (year < currentYear)
908  {
909  if ((currentYear - year) > PAST_THRESHOLD)
910  {
911  year += CENTURY;
912  }
913  }
914  else
915  {
916  // if we ended up too far in the future, step back a centry
917  if ((year - currentYear ) > FUTURE_THRESHOLD)
918  {
919  year -= CENTURY; /* move it back a century */
920  }
921  }
922  inputscan += SHORT_YEAR; /* step both pointers */
923  formatscan += SHORT_YEAR;
924  break;
925 
926  // 4-digit year
927  case 'Y':
928  // parse out the number version
929  if (!getNumber(inputscan, LONG_YEAR, &year))
930  {
931  return false;
932  }
933  inputscan += LONG_YEAR; /* step both pointers */
934  formatscan += LONG_YEAR;
935  break;
936 
937  // 3 character language form
938  case 'M':
939  {
940  month = 0;
941  // can months table for a descriptive match
942  for (int i = 0; i < MONTHS; i++)
943  {
944  /* have a match? */
945  if (!memcmp(monthNames[i], inputscan, CHAR_MONTH))
946  {
947  month = i + 1;
948  break;
949  }
950  }
951  // no match found in the table
952  if (month == 0)
953  {
954  return false;
955  }
956  inputscan += CHAR_MONTH; /* step over the date */
957  formatscan += CHAR_MONTH; /* step over the date */
958  break;
959  }
960 
961  // am/pm civil time modifier
962  case 'C':
963  // "am" time */
964  if (!memcmp(inputscan, ANTEMERIDIAN, strlen(ANTEMERIDIAN)))
965  {
966  // for parsing purposes, 12:nn is really 00:nn
967  if (hours == 12)
968  {
969  hours = 0;
970  }
971  }
972  // "pm"time
973  else if (!memcmp(inputscan, POSTMERIDIAN, strlen(POSTMERIDIAN)))
974  {
975  // if 12 something, that's at the beginning of the period.
976  // otherwise, add 12 to convert to 24 hours time internally,
977  if (hours != 12)
978  {
979  hours += 12;
980  }
981  }
982  // invalid marker
983  else
984  {
985  return false;
986  }
987  inputscan += strlen(ANTEMERIDIAN);
988  formatscan += strlen(ANTEMERIDIAN);
989  break;
990 
991  // civil time hours spec, which does not have leading zeros
992  case 'c': /* civil time hours spec */
993  {
994  // We accept 1 or 2 digits here, so check the second to see if
995  // it's a digit, which will determine our length to scan.
996  int numberLength = 1;
997 
998  if (isdigit(*(inputscan +1)))
999  {
1000  numberLength = 2;
1001  }
1002  // parse out the number version
1003  if (!getNumber(inputscan, numberLength, &hours, MAXHOURS))
1004  {
1005  return false;
1006  }
1007  inputscan += numberLength; // this is stepped a variable amount
1008  formatscan += HOURS_SIZE; /* just step the format pointer */
1009  break;
1010  }
1011  // a separater
1012  case '/':
1013  if (*sep == '\0')
1014  {
1015  // only increment the format...we're not expecting a character in the input
1016  formatscan++;
1017  }
1018  else
1019  {
1020  // the input must match the provided separator
1021  if (*inputscan != *sep)
1022  {
1023  return false;
1024  }
1025  formatscan++;
1026  inputscan++;
1027  }
1028  break;
1029 
1030  // time format separator characters...these are hard coded.
1031  case ':':
1032  case '.':
1033  if (*inputscan != *formatscan)
1034  {
1035  return false;
1036  }
1037  formatscan++;
1038  inputscan++;
1039  break;
1040  // bad format?
1041  default:
1042  return false;
1043  }
1044  }
1045 
1046  // now we need to validity check the date fields. Zero's not valid for any of them
1047  if (day == 0 || month == 0 || year == 0)
1048  {
1049  return false;
1050  }
1051 
1052  // now validity check the day of the month is not greater than the max for that month. This
1053  // will require a special leapyear check for February
1054  if (month == FEBRUARY && isLeapYear())
1055  {
1056  if (day > LEAPMONTH) // too many days?
1057  {
1058  return false;
1059  }
1060  }
1061  // just check against the table
1062  else if (day > monthdays[month - 1])
1063  {
1064  return false;
1065  }
1066  // everything validated
1067  return true;
1068 }
1069 
1070 
1071 
1072 /**
1073  * Internal routine used to extract short number fields from
1074  * a date/time format.
1075  *
1076  * @param input The current input position.
1077  * @param length The length of the field.
1078  * @param target The returned integer value.
1079  *
1080  * @return true if the field is valid, false for any parsing error.
1081  */
1082 bool RexxDateTime::getNumber(const char *input, wholenumber_t length, int *target)
1083 {
1084  wholenumber_t value = 0; // the default
1085  // process the specified number of digits
1086  while (length-- > 0)
1087  {
1088  char digit = *input;
1089  // add to the accumulator
1090  if (isdigit(digit))
1091  {
1092  value = (value * 10) + (digit - '0');
1093  }
1094  else
1095  {
1096  // not valid
1097  return false;
1098  }
1099  // step to the next position
1100  input++;
1101  }
1102  *target = (int)value;
1103  return true; // good number
1104 }
1105 
1106 
1107 /**
1108  * Internal method for parsing short numeric fields from a
1109  * date/time value.
1110  *
1111  * @param input The current input position.
1112  * @param length The length of the field.
1113  * @param target The returned value.
1114  * @param max The max value range for the parsed number.
1115  *
1116  * @return true if the number is valid, false for any parsing/validation
1117  * errors.
1118  */
1119 bool RexxDateTime::getNumber(const char *input, wholenumber_t length, int *target, int max)
1120 {
1121  // if this scans correctly, validate the range
1122  if (getNumber(input, length, target))
1123  {
1124  if (*target <= max)
1125  {
1126  return true;
1127  }
1128  }
1129  // either invalid characters or out of range
1130  return false;
1131 }
1132 
1133 
1134 /**
1135  * Format a base date into human readable form.
1136  *
1137  * @param buffer The target buffer for the output.
1138  */
1139 void RexxDateTime::formatBaseDate(char *buffer, size_t size)
1140 {
1141  // format this into the buffer as a number
1142  snprintf(buffer, size, "%zd", getBaseDate());
1143 }
1144 
1145 
1146 /**
1147  * Format a base time into human readable form.
1148  *
1149  * @param buffer The target buffer for the output.
1150  */
1152 {
1153  Numerics::formatInt64(getBaseTime(), (char *)buffer);
1154 }
1155 
1156 
1157 /**
1158  * Format a unix time into human readable form.
1159  *
1160  * @param buffer The target buffer for the output.
1161  */
1163 {
1164  Numerics::formatInt64(getUnixTime(), (char *)buffer);
1165 }
1166 
1167 
1168 /**
1169  * Format a date as the number of days in the current year.
1170  *
1171  * @param buffer The target buffer for the output.
1172  */
1173 void RexxDateTime::formatDays(char *buffer, size_t size)
1174 {
1175  // format this into the buffer as a number
1176  snprintf(buffer, size, "%d", (int)getYearDay());
1177 }
1178 
1179 
1180 /**
1181  * Format a date in 'E'uropean format.
1182  *
1183  * @param buffer The target buffer for the output.
1184  * @param sep The separator character used for the fields. This value can
1185  * be NULL, in which case the default is used. The string value
1186  * can also be a null string ("").
1187  */
1188 void RexxDateTime::formatEuropeanDate(char *buffer, size_t size, const char *sep)
1189 {
1190  // make sure we have a valid delimiter
1191  sep = sep == NULL ? "/" : sep;
1192  snprintf(buffer, size, "%02d%s%02d%s%02d", day, sep, month, sep, year % 100);
1193 }
1194 
1195 
1196 /**
1197  * Format a date as the name of the current month.
1198  *
1199  * @param buffer The target buffer for the output.
1200  */
1202 {
1203  strcpy(buffer, getMonthName());
1204 }
1205 
1206 
1207 /**
1208  * Format a date in 'N'ormal format.
1209  *
1210  * @param buffer The target buffer for the output.
1211  * @param sep The separator character used for the fields. This value can
1212  * be NULL, in which case the default is used. The string value
1213  * can also be a null string ("").
1214  */
1215 void RexxDateTime::formatNormalDate(char *buffer, size_t size, const char *sep)
1216 {
1217  // make sure we have a valid delimiter
1218  sep = sep == NULL ? " " : sep;
1219  snprintf(buffer, size, "%d%s%3.3s%s%4.4d", day, sep, monthNames[month-1], sep, year);
1220 }
1221 
1222 
1223 /**
1224  * Format a date in 'O'rdered format.
1225  *
1226  * @param buffer The target buffer for the output.
1227  * @param sep The separator character used for the fields. This value can
1228  * be NULL, in which case the default is used. The string value
1229  * can also be a null string ("").
1230  */
1231 void RexxDateTime::formatOrderedDate(char *buffer, size_t size, const char *sep)
1232 {
1233  // make sure we have a valid delimiter
1234  sep = sep == NULL ? "/" : sep;
1235  snprintf(buffer, size, "%02d%s%02d%s%02d", year % 100, sep, month, sep, day);
1236 }
1237 
1238 
1239 /**
1240  * Format a date in 'S'tandard format.
1241  *
1242  * @param buffer The target buffer for the output.
1243  * @param sep The separator character used for the fields. This value can
1244  * be NULL, in which case the default is used. The string value
1245  * can also be a null string ("").
1246  */
1247 void RexxDateTime::formatStandardDate(char *buffer, size_t size, const char *sep)
1248 {
1249  // make sure we have a valid delimiter
1250  sep = sep == NULL ? "" : sep;
1251  snprintf(buffer, size, "%04d%s%02d%s%02d", year, sep, month, sep, day);
1252 }
1253 
1254 
1255 /**
1256  * Format a date in 'U'sa format.
1257  *
1258  * @param buffer The target buffer for the output.
1259  * @param sep The separator character used for the fields. This value can
1260  * be NULL, in which case the default is used. The string value
1261  * can also be a null string ("").
1262  */
1263 void RexxDateTime::formatUsaDate(char *buffer, size_t size, const char *sep)
1264 {
1265  // make sure we have a valid delimiter
1266  sep = sep == NULL ? "/" : sep;
1267  snprintf(buffer, size, "%02d%s%02d%s%02d", month, sep, day, sep, year % 100);
1268 }
1269 
1270 
1271 
1272 
1273 /**
1274  * Format a date as the name of the current day.
1275  *
1276  * @param buffer The target buffer for the output.
1277  */
1279 {
1280 
1281  strcpy(buffer, getDayName()); // copy over the text name
1282 }
1283 
1284 
1285 /**
1286  * Format a time in 'C'ivil format.
1287  *
1288  * @param buffer The target buffer for the output.
1289  */
1290 void RexxDateTime::formatCivilTime(char *buffer, size_t size)
1291 {
1292  int adjustedHours = hours;
1293  if (adjustedHours == 0)
1294  {
1295  adjustedHours = 12;
1296  }
1297  else if (adjustedHours > 12)
1298  {
1299  adjustedHours -= 12;
1300  }
1301  snprintf(buffer, size, "%d:%2.2d%s", adjustedHours, minutes, hours >= 12 ? POSTMERIDIAN : ANTEMERIDIAN);
1302 }
1303 
1304 
1305 /**
1306  * Format a time in 'H'ours format.
1307  *
1308  * @param buffer The target buffer for the output.
1309  */
1310 void RexxDateTime::formatHours(char *buffer, size_t size)
1311 {
1312  snprintf(buffer, size, "%d", hours); // just format the hours
1313 }
1314 
1315 
1316 /**
1317  * Format a time in 'L'ong format.
1318  *
1319  * @param buffer The target buffer for the output.
1320  */
1321 void RexxDateTime::formatLongTime(char *buffer, size_t size)
1322 {
1323  snprintf(buffer, size, "%2.2d:%2.2d:%2.2d.%6.6d", hours, minutes, seconds, microseconds);
1324 }
1325 
1326 
1327 /**
1328  * Format a time in 'M'inutes format.
1329  *
1330  * @param buffer The target buffer for the output.
1331  */
1332 void RexxDateTime::formatMinutes(char *buffer, size_t size)
1333 {
1334  snprintf(buffer, size, "%d", hours * MINUTES_IN_HOUR + minutes);
1335 }
1336 
1337 
1338 /**
1339  * Format a time in 'N'ormal format.
1340  *
1341  * @param buffer The target buffer for the output.
1342  */
1343 void RexxDateTime::formatNormalTime(char *buffer, size_t size)
1344 {
1345  snprintf(buffer, size, "%2.2d:%2.2d:%2.2d", hours, minutes, seconds);
1346 }
1347 
1348 
1349 /**
1350  * Format a time in 'S'econds format.
1351  *
1352  * @param buffer The target buffer for the output.
1353  */
1354 void RexxDateTime::formatSeconds(char *buffer, size_t size)
1355 {
1356  snprintf(buffer, size, "%d", (hours * MINUTES_IN_HOUR + minutes) * SECONDS_IN_MINUTE + seconds);
1357 }
1358 
1359 
1360 /**
1361  * Format a the time zone offset value
1362  *
1363  * @param buffer The target buffer for the output.
1364  */
1366 {
1367  // the time zone is a sized value
1368  Numerics::formatInt64(timeZoneOffset, (char *)buffer);
1369 }
#define LEAP_DAYS
#define LEAPMONTH
#define CENTURY_DAYS
#define MAXCIVILHOURS
#define CHAR_MONTH
#define MINUTES_IN_HOUR
#define MICROSECONDS_IN_DAY
#define SECONDS_IN_MINUTE
#define MICRO_SIZE
#define HOURS_SIZE
#define MINUTES_SIZE
#define HOURS_IN_DAY
#define FUTURE_THRESHOLD
#define SHORT_YEAR
#define MAXMINUTES
#define SECONDS_SIZE
#define DAY_SIZE
#define SECONDS_IN_HOUR
#define LONG_YEAR
#define LeapYear(year)
#define CENTURY
#define PAST_THRESHOLD
#define MINUTES_IN_DAY
#define YEAR_DAYS
#define MICROSECONDS
#define FEBRUARY
#define POSTMERIDIAN
#define LEAP_CYCLE
#define SECONDS_IN_DAY
#define OLYMPIAD_DAYS
#define ANTEMERIDIAN
#define OLYMPIAD
#define MONTHS
#define BASE_DAYS(year)
#define MONTH_SIZE
#define MAXHOURS
#define MAXSECONDS
static size_t formatInt64(int64_t integer, char *dest)
Definition: Numerics.cpp:739
void formatNormalDate(char *buffer, size_t size, const char *sep)
static const char * monthNames[]
void formatMinutes(char *buffer, size_t size)
wholenumber_t getTimeSeconds()
void formatHours(char *buffer, size_t size)
wholenumber_t getWeekDay()
static const char * dayNames[]
void formatNormalTime(char *buffer, size_t size)
static int monthStarts[]
void formatBaseTime(char *buffer)
int64_t getUnixTime()
void formatBaseDate(char *buffer, size_t size)
void formatWeekDay(char *buffer)
void setDate(wholenumber_t newYear, wholenumber_t newDay)
void formatMonthName(char *buffer)
wholenumber_t getYearDay()
bool setMinutes(wholenumber_t m)
void formatCivilTime(char *buffer, size_t size)
bool setBaseDate(wholenumber_t basedays)
void setTimeInSeconds(wholenumber_t basetime)
int64_t timeZoneOffset
bool parseUsaDate(const char *date, const char *sep, wholenumber_t currentYear)
int64_t getBaseTime()
bool parseOrderedDate(const char *date, const char *sep, wholenumber_t currentYear)
bool getNumber(const char *input, wholenumber_t length, int *target)
void formatLongTime(char *buffer, size_t size)
bool parseNormalTime(const char *date)
void formatTimeZone(char *buffer)
void formatUsaDate(char *buffer, size_t size, const char *sep)
void formatSeconds(char *buffer, size_t size)
bool parseLongTime(const char *date)
static RexxDateTime maxBaseTime
bool parseEuropeanDate(const char *date, const char *sep, wholenumber_t currentYear)
bool parseCivilTime(const char *date)
const char * getDayName()
int64_t getUTCBaseTime()
void formatDays(char *buffer, size_t size)
const char * getMonthName()
void formatStandardDate(char *buffer, size_t size, const char *sep)
static int leapMonthStarts[]
bool parseDateTimeFormat(const char *date, const char *format, const char *sep, wholenumber_t currentYear)
bool setBaseTime(int64_t basetime)
bool setHours(wholenumber_t h)
bool parseStandardDate(const char *date, const char *sep)
bool parseNormalDate(const char *date, const char *sep)
bool adjustTimeZone(int64_t o)
bool setUnixTime(int64_t basetime)
void formatEuropeanDate(char *buffer, size_t size, const char *sep)
void setDay(wholenumber_t basedays)
static RexxDateTime unixBaseTime
wholenumber_t getBaseDate()
void formatUnixTime(char *buffer)
void formatOrderedDate(char *buffer, size_t size, const char *sep)
static int monthdays[]
bool setSeconds(wholenumber_t s)
ssize_t wholenumber_t
Definition: rexx.h:230
signed __int64 int64_t