1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . C A L E N D A R . F O R M A T T I N G --
9 -- Copyright (C) 2006-2007, Free Software Foundation, Inc. --
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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, 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.Calendar.Time_Zones; use Ada.Calendar.Time_Zones;
37 package body Ada.Calendar.Formatting is
39 --------------------------
40 -- Implementation Notes --
41 --------------------------
43 -- All operations in this package are target and time representation
44 -- independent, thus only one source file is needed for multiple targets.
46 procedure Check_Char (S : String; C : Character; Index : Integer);
47 -- Subsidiary to the two versions of Value. Determine whether the
48 -- input strint S has character C at position Index. Raise
49 -- Constraint_Error if there is a mismatch.
51 procedure Check_Digit (S : String; Index : Integer);
52 -- Subsidiary to the two versions of Value. Determine whether the
53 -- character of string S at position Index is a digit. This catches
54 -- invalid input such as 1983-*1-j3 u5:n7:k9 which should be
55 -- 1983-01-03 05:07:09. Raise Constraint_Error if there is a mismatch.
61 procedure Check_Char (S : String; C : Character; Index : Integer) is
63 if S (Index) /= C then
64 raise Constraint_Error;
72 procedure Check_Digit (S : String; Index : Integer) is
74 if S (Index) not in '0' .. '9' then
75 raise Constraint_Error;
85 Time_Zone : Time_Zones.Time_Offset := 0) return Day_Number
97 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
105 function Day_Of_Week (Date : Time) return Day_Name is
107 return Day_Name'Val (Formatting_Operations.Day_Of_Week (Date));
116 Time_Zone : Time_Zones.Time_Offset := 0) return Hour_Number
124 Ss : Second_Duration;
128 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
137 (Elapsed_Time : Duration;
138 Include_Time_Fraction : Boolean := False) return String
141 Minute : Minute_Number;
142 Second : Second_Number;
143 Sub_Second : Duration;
149 Result : String := "-00:00:00.00";
152 Split (abs (Elapsed_Time), Hour, Minute, Second, Sub_Second);
154 -- Determine the two slice bounds for the result string depending on
155 -- whether the input is negative and whether fractions are requested.
157 if Elapsed_Time < 0.0 then
163 if Include_Time_Fraction then
169 -- Prevent rounding when converting to natural
171 Sub_Second := Sub_Second * 100.0 - 0.5;
172 SS_Nat := Natural (Sub_Second);
175 Hour_Str : constant String := Hour_Number'Image (Hour);
176 Minute_Str : constant String := Minute_Number'Image (Minute);
177 Second_Str : constant String := Second_Number'Image (Second);
178 SS_Str : constant String := Natural'Image (SS_Nat);
181 -- Hour processing, positions 2 and 3
184 Result (3) := Hour_Str (2);
186 Result (2) := Hour_Str (2);
187 Result (3) := Hour_Str (3);
190 -- Minute processing, positions 5 and 6
193 Result (6) := Minute_Str (2);
195 Result (5) := Minute_Str (2);
196 Result (6) := Minute_Str (3);
199 -- Second processing, positions 8 and 9
202 Result (9) := Second_Str (2);
204 Result (8) := Second_Str (2);
205 Result (9) := Second_Str (3);
208 -- Optional sub second processing, positions 11 and 12
210 if Include_Time_Fraction then
212 Result (12) := SS_Str (2);
214 Result (11) := SS_Str (2);
215 Result (12) := SS_Str (3);
219 return Result (Low .. High);
229 Include_Time_Fraction : Boolean := False;
230 Time_Zone : Time_Zones.Time_Offset := 0) return String
233 Month : Month_Number;
236 Minute : Minute_Number;
237 Second : Second_Number;
238 Sub_Second : Duration;
240 Leap_Second : Boolean;
242 Result : String := "0000-00-00 00:00:00.00";
245 Split (Date, Year, Month, Day,
246 Hour, Minute, Second, Sub_Second, Leap_Second, Time_Zone);
248 -- Prevent rounding when converting to natural
250 Sub_Second := Sub_Second * 100.0 - 0.5;
251 SS_Nat := Natural (Sub_Second);
254 Year_Str : constant String := Year_Number'Image (Year);
255 Month_Str : constant String := Month_Number'Image (Month);
256 Day_Str : constant String := Day_Number'Image (Day);
257 Hour_Str : constant String := Hour_Number'Image (Hour);
258 Minute_Str : constant String := Minute_Number'Image (Minute);
259 Second_Str : constant String := Second_Number'Image (Second);
260 SS_Str : constant String := Natural'Image (SS_Nat);
263 -- Year processing, positions 1, 2, 3 and 4
265 Result (1) := Year_Str (2);
266 Result (2) := Year_Str (3);
267 Result (3) := Year_Str (4);
268 Result (4) := Year_Str (5);
270 -- Month processing, positions 6 and 7
273 Result (7) := Month_Str (2);
275 Result (6) := Month_Str (2);
276 Result (7) := Month_Str (3);
279 -- Day processing, positions 9 and 10
282 Result (10) := Day_Str (2);
284 Result (9) := Day_Str (2);
285 Result (10) := Day_Str (3);
288 -- Hour processing, positions 12 and 13
291 Result (13) := Hour_Str (2);
293 Result (12) := Hour_Str (2);
294 Result (13) := Hour_Str (3);
297 -- Minute processing, positions 15 and 16
300 Result (16) := Minute_Str (2);
302 Result (15) := Minute_Str (2);
303 Result (16) := Minute_Str (3);
306 -- Second processing, positions 18 and 19
309 Result (19) := Second_Str (2);
311 Result (18) := Second_Str (2);
312 Result (19) := Second_Str (3);
315 -- Optional sub second processing, positions 21 and 22
317 if Include_Time_Fraction then
319 Result (22) := SS_Str (2);
321 Result (21) := SS_Str (2);
322 Result (22) := SS_Str (3);
327 return Result (1 .. 19);
338 Time_Zone : Time_Zones.Time_Offset := 0) return Minute_Number
346 Ss : Second_Duration;
349 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
359 Time_Zone : Time_Zones.Time_Offset := 0) return Month_Number
367 Ss : Second_Duration;
370 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
378 function Second (Date : Time) return Second_Number is
385 Ss : Second_Duration;
388 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
398 Minute : Minute_Number;
399 Second : Second_Number := 0;
400 Sub_Second : Second_Duration := 0.0) return Day_Duration is
406 or else not Minute'Valid
407 or else not Second'Valid
408 or else not Sub_Second'Valid
410 raise Constraint_Error;
413 return Day_Duration (Hour * 3_600) +
414 Day_Duration (Minute * 60) +
415 Day_Duration (Second) +
424 (Seconds : Day_Duration;
425 Hour : out Hour_Number;
426 Minute : out Minute_Number;
427 Second : out Second_Number;
428 Sub_Second : out Second_Duration)
435 if not Seconds'Valid then
436 raise Constraint_Error;
439 if Seconds = 0.0 then
442 Secs := Natural (Seconds - 0.5);
445 Sub_Second := Second_Duration (Seconds - Day_Duration (Secs));
446 Hour := Hour_Number (Secs / 3_600);
447 Secs := Secs mod 3_600;
448 Minute := Minute_Number (Secs / 60);
449 Second := Second_Number (Secs mod 60);
454 or else not Minute'Valid
455 or else not Second'Valid
456 or else not Sub_Second'Valid
468 Year : out Year_Number;
469 Month : out Month_Number;
470 Day : out Day_Number;
471 Seconds : out Day_Duration;
472 Leap_Second : out Boolean;
473 Time_Zone : Time_Zones.Time_Offset := 0)
479 Tz : constant Long_Integer := Long_Integer (Time_Zone);
482 Formatting_Operations.Split
492 Leap_Sec => Leap_Second,
499 or else not Month'Valid
500 or else not Day'Valid
501 or else not Seconds'Valid
513 Year : out Year_Number;
514 Month : out Month_Number;
515 Day : out Day_Number;
516 Hour : out Hour_Number;
517 Minute : out Minute_Number;
518 Second : out Second_Number;
519 Sub_Second : out Second_Duration;
520 Time_Zone : Time_Zones.Time_Offset := 0)
524 Tz : constant Long_Integer := Long_Integer (Time_Zone);
527 Formatting_Operations.Split
536 Sub_Sec => Sub_Second,
544 or else not Month'Valid
545 or else not Day'Valid
546 or else not Hour'Valid
547 or else not Minute'Valid
548 or else not Second'Valid
549 or else not Sub_Second'Valid
561 Year : out Year_Number;
562 Month : out Month_Number;
563 Day : out Day_Number;
564 Hour : out Hour_Number;
565 Minute : out Minute_Number;
566 Second : out Second_Number;
567 Sub_Second : out Second_Duration;
568 Leap_Second : out Boolean;
569 Time_Zone : Time_Zones.Time_Offset := 0)
572 Tz : constant Long_Integer := Long_Integer (Time_Zone);
575 Formatting_Operations.Split
584 Sub_Sec => Sub_Second,
585 Leap_Sec => Leap_Second,
592 or else not Month'Valid
593 or else not Day'Valid
594 or else not Hour'Valid
595 or else not Minute'Valid
596 or else not Second'Valid
597 or else not Sub_Second'Valid
607 function Sub_Second (Date : Time) return Second_Duration is
614 Ss : Second_Duration;
617 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
627 Month : Month_Number;
629 Seconds : Day_Duration := 0.0;
630 Leap_Second : Boolean := False;
631 Time_Zone : Time_Zones.Time_Offset := 0) return Time
633 Adj_Year : Year_Number := Year;
634 Adj_Month : Month_Number := Month;
635 Adj_Day : Day_Number := Day;
637 H : constant Integer := 1;
638 M : constant Integer := 1;
639 Se : constant Integer := 1;
640 Ss : constant Duration := 0.1;
641 Tz : constant Long_Integer := Long_Integer (Time_Zone);
647 or else not Month'Valid
648 or else not Day'Valid
649 or else not Seconds'Valid
650 or else not Time_Zone'Valid
652 raise Constraint_Error;
655 -- A Seconds value of 86_400 denotes a new day. This case requires an
656 -- adjustment to the input values.
658 if Seconds = 86_400.0 then
659 if Day < Days_In_Month (Month)
660 or else (Is_Leap (Year)
668 Adj_Month := Month + 1;
671 Adj_Year := Year + 1;
677 Formatting_Operations.Time_Of
686 Leap_Sec => Leap_Second,
687 Use_Day_Secs => True,
698 Month : Month_Number;
701 Minute : Minute_Number;
702 Second : Second_Number;
703 Sub_Second : Second_Duration := 0.0;
704 Leap_Second : Boolean := False;
705 Time_Zone : Time_Zones.Time_Offset := 0) return Time
707 Dd : constant Day_Duration := Day_Duration'First;
708 Tz : constant Long_Integer := Long_Integer (Time_Zone);
714 or else not Month'Valid
715 or else not Day'Valid
716 or else not Hour'Valid
717 or else not Minute'Valid
718 or else not Second'Valid
719 or else not Sub_Second'Valid
720 or else not Time_Zone'Valid
722 raise Constraint_Error;
726 Formatting_Operations.Time_Of
734 Sub_Sec => Sub_Second,
735 Leap_Sec => Leap_Second,
736 Use_Day_Secs => False,
747 Time_Zone : Time_Zones.Time_Offset := 0) return Time
749 D : String (1 .. 22);
751 Month : Month_Number;
754 Minute : Minute_Number;
755 Second : Second_Number;
756 Sub_Second : Second_Duration := 0.0;
761 if not Time_Zone'Valid then
762 raise Constraint_Error;
768 and then Date'Length /= 22
770 raise Constraint_Error;
773 -- After the correct length has been determined, it is safe to
774 -- copy the Date in order to avoid Date'First + N indexing.
776 D (1 .. Date'Length) := Date;
780 Check_Char (D, '-', 5);
781 Check_Char (D, '-', 8);
782 Check_Char (D, ' ', 11);
783 Check_Char (D, ':', 14);
784 Check_Char (D, ':', 17);
786 if Date'Length = 22 then
787 Check_Char (D, '.', 20);
790 -- Leading zero checks
798 if Date'Length = 22 then
804 Year := Year_Number (Year_Number'Value (D (1 .. 4)));
805 Month := Month_Number (Month_Number'Value (D (6 .. 7)));
806 Day := Day_Number (Day_Number'Value (D (9 .. 10)));
807 Hour := Hour_Number (Hour_Number'Value (D (12 .. 13)));
808 Minute := Minute_Number (Minute_Number'Value (D (15 .. 16)));
809 Second := Second_Number (Second_Number'Value (D (18 .. 19)));
813 if Date'Length = 22 then
814 Sub_Second := Second_Duration (Second_Duration'Value (D (20 .. 22)));
820 or else not Month'Valid
821 or else not Day'Valid
822 or else not Hour'Valid
823 or else not Minute'Valid
824 or else not Second'Valid
825 or else not Sub_Second'Valid
827 raise Constraint_Error;
830 return Time_Of (Year, Month, Day,
831 Hour, Minute, Second, Sub_Second, False, Time_Zone);
834 when others => raise Constraint_Error;
841 function Value (Elapsed_Time : String) return Duration is
842 D : String (1 .. 11);
844 Minute : Minute_Number;
845 Second : Second_Number;
846 Sub_Second : Second_Duration := 0.0;
851 if Elapsed_Time'Length /= 8
852 and then Elapsed_Time'Length /= 11
854 raise Constraint_Error;
857 -- After the correct length has been determined, it is safe to
858 -- copy the Elapsed_Time in order to avoid Date'First + N indexing.
860 D (1 .. Elapsed_Time'Length) := Elapsed_Time;
864 Check_Char (D, ':', 3);
865 Check_Char (D, ':', 6);
867 if Elapsed_Time'Length = 11 then
868 Check_Char (D, '.', 9);
871 -- Leading zero checks
877 if Elapsed_Time'Length = 11 then
883 Hour := Hour_Number (Hour_Number'Value (D (1 .. 2)));
884 Minute := Minute_Number (Minute_Number'Value (D (4 .. 5)));
885 Second := Second_Number (Second_Number'Value (D (7 .. 8)));
889 if Elapsed_Time'Length = 11 then
890 Sub_Second := Second_Duration (Second_Duration'Value (D (9 .. 11)));
896 or else not Minute'Valid
897 or else not Second'Valid
898 or else not Sub_Second'Valid
900 raise Constraint_Error;
903 return Seconds_Of (Hour, Minute, Second, Sub_Second);
906 when others => raise Constraint_Error;
915 Time_Zone : Time_Zones.Time_Offset := 0) return Year_Number
923 Ss : Second_Duration;
927 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
931 end Ada.Calendar.Formatting;