1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- G N A T . C A L E N D A R . T I M E _ I O --
9 -- Copyright (C) 1999-2007, AdaCore --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Ada
.Calendar
; use Ada
.Calendar
;
35 with Ada
.Characters
.Handling
;
36 with Ada
.Strings
.Unbounded
; use Ada
.Strings
.Unbounded
;
41 package body GNAT
.Calendar
.Time_IO
is
57 function Month_Name_To_Number
58 (Str
: String) return Ada
.Calendar
.Month_Number
;
59 -- Converts a string that contains an abbreviated month name to a month
60 -- number. Constraint_Error is raised if Str is not a valid month name.
61 -- Comparison is case insensitive
63 type Padding_Mode
is (None
, Zero
, Space
);
65 type Sec_Number
is mod 2 ** 64;
66 -- Type used to compute the number of seconds since 01/01/1970. A 32 bit
67 -- number will cover only a period of 136 years. This means that for date
68 -- past 2106 the computation is not possible. A 64 bits number should be
69 -- enough for a very large period of time.
71 -----------------------
72 -- Local Subprograms --
73 -----------------------
75 function Am_Pm
(H
: Natural) return String;
76 -- Return AM or PM depending on the hour H
78 function Hour_12
(H
: Natural) return Positive;
79 -- Convert a 1-24h format to a 0-12 hour format
81 function Image
(Str
: String; Length
: Natural := 0) return String;
82 -- Return Str capitalized and cut to length number of characters. If
83 -- length is 0, then no cut operation is performed.
87 Padding
: Padding_Mode
:= Zero
;
88 Length
: Natural := 0) return String;
89 -- Return image of N. This number is eventually padded with zeros or spaces
90 -- depending of the length required. If length is 0 then no padding occurs.
94 Padding
: Padding_Mode
:= Zero
;
95 Length
: Natural := 0) return String;
96 -- As above with N provided in Integer format
102 function Am_Pm
(H
: Natural) return String is
104 if H
= 0 or else H
> 12 then
115 function Hour_12
(H
: Natural) return Positive is
132 Length
: Natural := 0) return String
134 use Ada
.Characters
.Handling
;
135 Local
: constant String :=
136 To_Upper
(Str
(Str
'First)) &
137 To_Lower
(Str
(Str
'First + 1 .. Str
'Last));
142 return Local
(1 .. Length
);
152 Padding
: Padding_Mode
:= Zero
;
153 Length
: Natural := 0) return String
156 return Image
(Sec_Number
(N
), Padding
, Length
);
161 Padding
: Padding_Mode
:= Zero
;
162 Length
: Natural := 0) return String
164 function Pad_Char
return String;
170 function Pad_Char
return String is
173 when None
=> return "";
174 when Zero
=> return "00";
175 when Space
=> return " ";
179 -- Local Declarations
181 NI
: constant String := Sec_Number
'Image (N
);
182 NIP
: constant String := Pad_Char
& NI
(2 .. NI
'Last);
184 -- Start of processing for Image
187 if Length
= 0 or else Padding
= None
then
188 return NI
(2 .. NI
'Last);
190 return NIP
(NIP
'Last - Length
+ 1 .. NIP
'Last);
199 (Date
: Ada
.Calendar
.Time
;
200 Picture
: Picture_String
) return String
202 Padding
: Padding_Mode
:= Zero
;
203 -- Padding is set for one directive
205 Result
: Unbounded_String
;
208 Month
: Month_Number
;
211 Minute
: Minute_Number
;
212 Second
: Second_Number
;
213 Sub_Second
: Second_Duration
;
218 -- Get current time in split format
220 Split
(Date
, Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
222 -- Null picture string is error
225 raise Picture_Error
with "null picture string";
228 -- Loop through characters of picture string, building result
230 Result
:= Null_Unbounded_String
;
232 while P
<= Picture
'Last loop
234 -- A directive has the following format "%[-_]."
236 if Picture
(P
) = '%' then
239 if P
= Picture
'Last then
240 raise Picture_Error
with "picture string ends with '%";
243 -- Check for GNU extension to change the padding
245 if Picture
(P
+ 1) = '-' then
249 elsif Picture
(P
+ 1) = '_' then
254 if P
= Picture
'Last then
255 raise Picture_Error
with "picture string ends with '- or '_";
258 case Picture
(P
+ 1) is
263 Result
:= Result
& '%';
268 Result
:= Result
& ASCII
.LF
;
273 Result
:= Result
& ASCII
.HT
;
278 Result
:= Result
& Image
(Hour
, Padding
, 2);
283 Result
:= Result
& Image
(Hour_12
(Hour
), Padding
, 2);
288 Result
:= Result
& Image
(Hour
, Space
, 2);
293 Result
:= Result
& Image
(Hour_12
(Hour
), Space
, 2);
298 Result
:= Result
& Image
(Minute
, Padding
, 2);
303 Result
:= Result
& Am_Pm
(Hour
);
305 -- Time, 12-hour (hh:mm:ss [AP]M)
309 Image
(Hour_12
(Hour
), Padding
, Length
=> 2) & ':' &
310 Image
(Minute
, Padding
, Length
=> 2) & ':' &
311 Image
(Second
, Padding
, Length
=> 2) & ' ' &
314 -- Seconds since 1970-01-01 00:00:00 UTC
315 -- (a nonstandard extension)
319 -- Compute the number of seconds using Ada.Calendar.Time
320 -- values rather than Julian days to account for Daylight
323 Neg
: Boolean := False;
324 Sec
: Duration := Date
- Time_Of
(1970, 1, 1, 0.0);
327 -- Avoid rounding errors and perform special processing
328 -- for dates earlier than the Unix Epoc.
334 Sec
:= abs (Sec
+ 0.5);
337 -- Prepend a minus sign to the result since Sec_Number
338 -- cannot handle negative numbers.
342 Result
& "-" & Image
(Sec_Number
(Sec
), None
);
344 Result
:= Result
& Image
(Sec_Number
(Sec
), None
);
351 Result
:= Result
& Image
(Second
, Padding
, Length
=> 2);
353 -- Milliseconds (3 digits)
354 -- Microseconds (6 digits)
355 -- Nanoseconds (9 digits)
357 when 'i' |
'e' |
'o' =>
359 Sub_Sec
: constant Long_Integer :=
360 Long_Integer (Sub_Second
* 1_000_000_000
);
362 Img1
: constant String := Sub_Sec
'Img;
363 Img2
: constant String :=
364 "00000000" & Img1
(Img1
'First + 1 .. Img1
'Last);
365 Nanos
: constant String :=
366 Img2
(Img2
'Last - 8 .. Img2
'Last);
369 case Picture
(P
+ 1) is
372 Nanos
(Nanos
'First .. Nanos
'First + 2);
376 Nanos
(Nanos
'First .. Nanos
'First + 5);
379 Result
:= Result
& Nanos
;
386 -- Time, 24-hour (hh:mm:ss)
390 Image
(Hour
, Padding
, Length
=> 2) & ':' &
391 Image
(Minute
, Padding
, Length
=> 2) & ':' &
392 Image
(Second
, Padding
, Length
=> 2);
394 -- Locale's abbreviated weekday name (Sun..Sat)
398 Image
(Day_Name
'Image (Day_Of_Week
(Date
)), 3);
400 -- Locale's full weekday name, variable length
401 -- (Sunday..Saturday)
405 Image
(Day_Name
'Image (Day_Of_Week
(Date
)));
407 -- Locale's abbreviated month name (Jan..Dec)
411 Image
(Month_Name
'Image (Month_Name
'Val (Month
- 1)), 3);
413 -- Locale's full month name, variable length
414 -- (January..December).
418 Image
(Month_Name
'Image (Month_Name
'Val (Month
- 1)));
420 -- Locale's date and time (Sat Nov 04 12:02:33 EST 1989)
425 Result
:= Result
& Image
(Date
, "%a %b %d %T %Y");
427 Result
:= Result
& Image
(Date
, "%a %b %_d %_T %Y");
429 Result
:= Result
& Image
(Date
, "%a %b %-d %-T %Y");
432 -- Day of month (01..31)
435 Result
:= Result
& Image
(Day
, Padding
, 2);
441 Image
(Month
, Padding
, 2) & '/' &
442 Image
(Day
, Padding
, 2) & '/' &
443 Image
(Year
, Padding
, 2);
445 -- Day of year (001..366)
448 Result
:= Result
& Image
(Day_In_Year
(Date
), Padding
, 3);
453 Result
:= Result
& Image
(Month
, Padding
, 2);
455 -- Week number of year with Sunday as first day of week
460 Offset
: constant Natural :=
461 (Julian_Day
(Year
, 1, 1) + 1) mod 7;
463 Week
: constant Natural :=
464 1 + ((Day_In_Year
(Date
) - 1) + Offset
) / 7;
467 Result
:= Result
& Image
(Week
, Padding
, 2);
470 -- Day of week (0..6) with 0 corresponding to Sunday
474 DOW
: Natural range 0 .. 6;
477 if Day_Of_Week
(Date
) = Sunday
then
480 DOW
:= Day_Name
'Pos (Day_Of_Week
(Date
));
483 Result
:= Result
& Image
(DOW
, Length
=> 1);
486 -- Week number of year with Monday as first day of week
490 Result
:= Result
& Image
(Week_In_Year
(Date
), Padding
, 2);
492 -- Last two digits of year (00..99)
496 Y
: constant Natural := Year
- (Year
/ 100) * 100;
498 Result
:= Result
& Image
(Y
, Padding
, 2);
504 Result
:= Result
& Image
(Year
, None
, 4);
507 raise Picture_Error
with
508 "unknown format character in picture string";
512 -- Skip past % and format character
516 -- Character other than % is copied into the result
519 Result
:= Result
& Picture
(P
);
524 return To_String
(Result
);
527 --------------------------
528 -- Month_Name_To_Number --
529 --------------------------
531 function Month_Name_To_Number
532 (Str
: String) return Ada
.Calendar
.Month_Number
534 subtype String3
is String (1 .. 3);
535 Abbrev_Upper_Month_Names
:
536 constant array (Ada
.Calendar
.Month_Number
) of String3
:=
537 ("JAN", "FEB", "MAR", "APR", "MAY", "JUN",
538 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC");
539 -- Short version of the month names, used when parsing date strings
544 GNAT
.Case_Util
.To_Upper
(S
);
546 for J
in Abbrev_Upper_Month_Names
'Range loop
547 if Abbrev_Upper_Month_Names
(J
) = S
then
552 return Abbrev_Upper_Month_Names
'First;
553 end Month_Name_To_Number
;
559 function Value
(Date
: String) return Ada
.Calendar
.Time
is
560 D
: String (1 .. 21);
561 D_Length
: constant Natural := Date
'Length;
564 Month
: Month_Number
;
567 Minute
: Minute_Number
;
568 Second
: Second_Number
;
569 Sub_Second
: Second_Duration
;
571 procedure Extract_Date
572 (Year
: out Year_Number
;
573 Month
: out Month_Number
;
574 Day
: out Day_Number
;
575 Time_Start
: out Natural);
576 -- Try and extract a date value from string D. Time_Start is set to the
577 -- first character that could be the start of time data.
579 procedure Extract_Time
581 Hour
: out Hour_Number
;
582 Minute
: out Minute_Number
;
583 Second
: out Second_Number
;
584 Check_Space
: Boolean := False);
585 -- Try and extract a time value from string D starting from position
586 -- Index. Set Check_Space to True to check whether the character at
587 -- Index - 1 is a space. Raise Constraint_Error if the portion of D
588 -- corresponding to the date is not well formatted.
594 procedure Extract_Date
595 (Year
: out Year_Number
;
596 Month
: out Month_Number
;
597 Day
: out Day_Number
;
598 Time_Start
: out Natural)
601 if D
(3) = '-' or else D
(3) = '/' then
602 if D_Length
= 8 or else D_Length
= 17 then
604 -- Formats are "yy*mm*dd" or "yy*mm*dd hh:mm:ss"
606 if D
(6) /= D
(3) then
607 raise Constraint_Error
;
610 Year
:= Year_Number
'Value ("20" & D
(1 .. 2));
611 Month
:= Month_Number
'Value (D
(4 .. 5));
612 Day
:= Day_Number
'Value (D
(7 .. 8));
615 elsif D_Length
= 10 or else D_Length
= 19 then
617 -- Formats are "mm*dd*yyyy" or "mm*dd*yyyy hh:mm:ss"
619 if D
(6) /= D
(3) then
620 raise Constraint_Error
;
623 Year
:= Year_Number
'Value (D
(7 .. 10));
624 Month
:= Month_Number
'Value (D
(1 .. 2));
625 Day
:= Day_Number
'Value (D
(4 .. 5));
628 elsif D_Length
= 11 or else D_Length
= 20 then
630 -- Formats are "dd*mmm*yyyy" or "dd*mmm*yyyy hh:mm:ss"
632 if D
(7) /= D
(3) then
633 raise Constraint_Error
;
636 Year
:= Year_Number
'Value (D
(8 .. 11));
637 Month
:= Month_Name_To_Number
(D
(4 .. 6));
638 Day
:= Day_Number
'Value (D
(1 .. 2));
642 raise Constraint_Error
;
645 elsif D
(3) = ' ' then
646 if D_Length
= 11 or else D_Length
= 20 then
648 -- Possible formats are "dd mmm yyyy", "dd mmm yyyy hh:mm:ss"
651 raise Constraint_Error
;
654 Year
:= Year_Number
'Value (D
(8 .. 11));
655 Month
:= Month_Name_To_Number
(D
(4 .. 6));
656 Day
:= Day_Number
'Value (D
(1 .. 2));
660 raise Constraint_Error
;
664 if D_Length
= 8 or else D_Length
= 17 then
666 -- Possible formats are "yyyymmdd" or "yyyymmdd hh:mm:ss"
668 Year
:= Year_Number
'Value (D
(1 .. 4));
669 Month
:= Month_Number
'Value (D
(5 .. 6));
670 Day
:= Day_Number
'Value (D
(7 .. 8));
673 elsif D_Length
= 10 or else D_Length
= 19 then
675 -- Possible formats are "yyyy*mm*dd" or "yyyy*mm*dd hh:mm:ss"
677 if (D
(5) /= '-' and then D
(5) /= '/')
678 or else D
(8) /= D
(5)
680 raise Constraint_Error
;
683 Year
:= Year_Number
'Value (D
(1 .. 4));
684 Month
:= Month_Number
'Value (D
(6 .. 7));
685 Day
:= Day_Number
'Value (D
(9 .. 10));
688 elsif D_Length
= 11 or else D_Length
= 20 then
690 -- Possible formats are "yyyy*mmm*dd"
692 if (D
(5) /= '-' and then D
(5) /= '/')
693 or else D
(9) /= D
(5)
695 raise Constraint_Error
;
698 Year
:= Year_Number
'Value (D
(1 .. 4));
699 Month
:= Month_Name_To_Number
(D
(6 .. 8));
700 Day
:= Day_Number
'Value (D
(10 .. 11));
703 elsif D_Length
= 12 or else D_Length
= 21 then
705 -- Formats are "mmm dd, yyyy" or "mmm dd, yyyy hh:mm:ss"
711 raise Constraint_Error
;
714 Year
:= Year_Number
'Value (D
(9 .. 12));
715 Month
:= Month_Name_To_Number
(D
(1 .. 3));
716 Day
:= Day_Number
'Value (D
(5 .. 6));
720 raise Constraint_Error
;
729 procedure Extract_Time
731 Hour
: out Hour_Number
;
732 Minute
: out Minute_Number
;
733 Second
: out Second_Number
;
734 Check_Space
: Boolean := False)
737 -- If no time was specified in the string (do not allow trailing
740 if Index
= D_Length
+ 2 then
746 -- Not enough characters left ?
748 if Index
/= D_Length
- 7 then
749 raise Constraint_Error
;
752 if Check_Space
and then D
(Index
- 1) /= ' ' then
753 raise Constraint_Error
;
756 if D
(Index
+ 2) /= ':' or else D
(Index
+ 5) /= ':' then
757 raise Constraint_Error
;
760 Hour
:= Hour_Number
'Value (D
(Index
.. Index
+ 1));
761 Minute
:= Minute_Number
'Value (D
(Index
+ 3 .. Index
+ 4));
762 Second
:= Second_Number
'Value (D
(Index
+ 6 .. Index
+ 7));
766 -- Local Declarations
768 Time_Start
: Natural := 1;
770 -- Start of processing for Value
773 Split
(Clock
, Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
779 and then D_Length
/= 10
780 and then D_Length
/= 11
781 and then D_Length
/= 12
782 and then D_Length
/= 17
783 and then D_Length
/= 19
784 and then D_Length
/= 20
785 and then D_Length
/= 21
787 raise Constraint_Error
;
790 -- After the correct length has been determined, it is safe to create
791 -- a local string copy in order to avoid String'First N arithmetic.
793 D
(1 .. D_Length
) := Date
;
798 Extract_Date
(Year
, Month
, Day
, Time_Start
);
799 Extract_Time
(Time_Start
, Hour
, Minute
, Second
, Check_Space
=> True);
801 Extract_Time
(1, Hour
, Minute
, Second
, Check_Space
=> False);
807 or else not Month
'Valid
808 or else not Day
'Valid
809 or else not Hour
'Valid
810 or else not Minute
'Valid
811 or else not Second
'Valid
813 raise Constraint_Error
;
816 return Time_Of
(Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
824 (Date
: Ada
.Calendar
.Time
;
825 Picture
: Picture_String
)
828 Ada
.Text_IO
.Put
(Image
(Date
, Picture
));
831 end GNAT
.Calendar
.Time_IO
;