PR c++/54038
[official-gcc.git] / gcc / ada / a-calfor.adb
blob1376f9367afe64aa99b6970c1ddc183aadcbbf38
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME COMPONENTS --
4 -- --
5 -- A D A . C A L E N D A R . F O R M A T T I N G --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2006-2012, Free Software Foundation, Inc. --
10 -- --
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 3, 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. --
17 -- --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
21 -- --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
26 -- --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
30 ------------------------------------------------------------------------------
32 with Ada.Calendar; use Ada.Calendar;
33 with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones;
35 package body Ada.Calendar.Formatting is
37 --------------------------
38 -- Implementation Notes --
39 --------------------------
41 -- All operations in this package are target and time representation
42 -- independent, thus only one source file is needed for multiple targets.
44 procedure Check_Char (S : String; C : Character; Index : Integer);
45 -- Subsidiary to the two versions of Value. Determine whether the input
46 -- string S has character C at position Index. Raise Constraint_Error if
47 -- there is a mismatch.
49 procedure Check_Digit (S : String; Index : Integer);
50 -- Subsidiary to the two versions of Value. Determine whether the character
51 -- of string S at position Index is a digit. This catches invalid input
52 -- such as 1983-*1-j3 u5:n7:k9 which should be 1983-01-03 05:07:09. Raise
53 -- Constraint_Error if there is a mismatch.
55 ----------------
56 -- Check_Char --
57 ----------------
59 procedure Check_Char (S : String; C : Character; Index : Integer) is
60 begin
61 if S (Index) /= C then
62 raise Constraint_Error;
63 end if;
64 end Check_Char;
66 -----------------
67 -- Check_Digit --
68 -----------------
70 procedure Check_Digit (S : String; Index : Integer) is
71 begin
72 if S (Index) not in '0' .. '9' then
73 raise Constraint_Error;
74 end if;
75 end Check_Digit;
77 ---------
78 -- Day --
79 ---------
81 function Day
82 (Date : Time;
83 Time_Zone : Time_Zones.Time_Offset := 0) return Day_Number
85 Y : Year_Number;
86 Mo : Month_Number;
87 D : Day_Number;
88 H : Hour_Number;
89 Mi : Minute_Number;
90 Se : Second_Number;
91 Ss : Second_Duration;
92 Le : Boolean;
94 pragma Unreferenced (Y, Mo, H, Mi);
96 begin
97 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
98 return D;
99 end Day;
101 -----------------
102 -- Day_Of_Week --
103 -----------------
105 function Day_Of_Week (Date : Time) return Day_Name is
106 begin
107 return Day_Name'Val (Formatting_Operations.Day_Of_Week (Date));
108 end Day_Of_Week;
110 ----------
111 -- Hour --
112 ----------
114 function Hour
115 (Date : Time;
116 Time_Zone : Time_Zones.Time_Offset := 0) return Hour_Number
118 Y : Year_Number;
119 Mo : Month_Number;
120 D : Day_Number;
121 H : Hour_Number;
122 Mi : Minute_Number;
123 Se : Second_Number;
124 Ss : Second_Duration;
125 Le : Boolean;
127 pragma Unreferenced (Y, Mo, D, Mi);
129 begin
130 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
131 return H;
132 end Hour;
134 -----------
135 -- Image --
136 -----------
138 function Image
139 (Elapsed_Time : Duration;
140 Include_Time_Fraction : Boolean := False) return String
142 To_Char : constant array (0 .. 9) of Character := "0123456789";
143 Hour : Hour_Number;
144 Minute : Minute_Number;
145 Second : Second_Number;
146 Sub_Second : Duration;
147 SS_Nat : Natural;
149 -- Determine the two slice bounds for the result string depending on
150 -- whether the input is negative and whether fractions are requested.
152 First : constant Integer := (if Elapsed_Time < 0.0 then 1 else 2);
153 Last : constant Integer := (if Include_Time_Fraction then 12 else 9);
155 Result : String := "-00:00:00.00";
157 begin
158 Split (abs (Elapsed_Time), Hour, Minute, Second, Sub_Second);
160 -- Hour processing, positions 2 and 3
162 Result (2) := To_Char (Hour / 10);
163 Result (3) := To_Char (Hour mod 10);
165 -- Minute processing, positions 5 and 6
167 Result (5) := To_Char (Minute / 10);
168 Result (6) := To_Char (Minute mod 10);
170 -- Second processing, positions 8 and 9
172 Result (8) := To_Char (Second / 10);
173 Result (9) := To_Char (Second mod 10);
175 -- Optional sub second processing, positions 11 and 12
177 if Include_Time_Fraction and then Sub_Second > 0.0 then
179 -- Prevent rounding up when converting to natural, avoiding the zero
180 -- case to prevent rounding down to a negative number.
182 SS_Nat := Natural (Duration'(Sub_Second * 100.0) - 0.5);
184 Result (11) := To_Char (SS_Nat / 10);
185 Result (12) := To_Char (SS_Nat mod 10);
186 end if;
188 return Result (First .. Last);
189 end Image;
191 -----------
192 -- Image --
193 -----------
195 function Image
196 (Date : Time;
197 Include_Time_Fraction : Boolean := False;
198 Time_Zone : Time_Zones.Time_Offset := 0) return String
200 To_Char : constant array (0 .. 9) of Character := "0123456789";
202 Year : Year_Number;
203 Month : Month_Number;
204 Day : Day_Number;
205 Hour : Hour_Number;
206 Minute : Minute_Number;
207 Second : Second_Number;
208 Sub_Second : Duration;
209 SS_Nat : Natural;
210 Leap_Second : Boolean;
212 -- The result length depends on whether fractions are requested.
214 Result : String := "0000-00-00 00:00:00.00";
215 Last : constant Positive :=
216 Result'Last - (if Include_Time_Fraction then 0 else 3);
218 begin
219 Split (Date, Year, Month, Day,
220 Hour, Minute, Second, Sub_Second, Leap_Second, Time_Zone);
222 -- Year processing, positions 1, 2, 3 and 4
224 Result (1) := To_Char (Year / 1000);
225 Result (2) := To_Char (Year / 100 mod 10);
226 Result (3) := To_Char (Year / 10 mod 10);
227 Result (4) := To_Char (Year mod 10);
229 -- Month processing, positions 6 and 7
231 Result (6) := To_Char (Month / 10);
232 Result (7) := To_Char (Month mod 10);
234 -- Day processing, positions 9 and 10
236 Result (9) := To_Char (Day / 10);
237 Result (10) := To_Char (Day mod 10);
239 Result (12) := To_Char (Hour / 10);
240 Result (13) := To_Char (Hour mod 10);
242 -- Minute processing, positions 15 and 16
244 Result (15) := To_Char (Minute / 10);
245 Result (16) := To_Char (Minute mod 10);
247 -- Second processing, positions 18 and 19
249 Result (18) := To_Char (Second / 10);
250 Result (19) := To_Char (Second mod 10);
252 -- Optional sub second processing, positions 21 and 22
254 if Include_Time_Fraction and then Sub_Second > 0.0 then
256 -- Prevent rounding up when converting to natural, avoiding the zero
257 -- case to prevent rounding down to a negative number.
259 SS_Nat := Natural (Duration'(Sub_Second * 100.0) - 0.5);
261 Result (21) := To_Char (SS_Nat / 10);
262 Result (22) := To_Char (SS_Nat mod 10);
263 end if;
265 return Result (Result'First .. Last);
266 end Image;
268 ------------
269 -- Minute --
270 ------------
272 function Minute
273 (Date : Time;
274 Time_Zone : Time_Zones.Time_Offset := 0) return Minute_Number
276 Y : Year_Number;
277 Mo : Month_Number;
278 D : Day_Number;
279 H : Hour_Number;
280 Mi : Minute_Number;
281 Se : Second_Number;
282 Ss : Second_Duration;
283 Le : Boolean;
285 pragma Unreferenced (Y, Mo, D, H);
287 begin
288 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
289 return Mi;
290 end Minute;
292 -----------
293 -- Month --
294 -----------
296 function Month
297 (Date : Time;
298 Time_Zone : Time_Zones.Time_Offset := 0) return Month_Number
300 Y : Year_Number;
301 Mo : Month_Number;
302 D : Day_Number;
303 H : Hour_Number;
304 Mi : Minute_Number;
305 Se : Second_Number;
306 Ss : Second_Duration;
307 Le : Boolean;
309 pragma Unreferenced (Y, D, H, Mi);
311 begin
312 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
313 return Mo;
314 end Month;
316 ------------
317 -- Second --
318 ------------
320 function Second (Date : Time) return Second_Number is
321 Y : Year_Number;
322 Mo : Month_Number;
323 D : Day_Number;
324 H : Hour_Number;
325 Mi : Minute_Number;
326 Se : Second_Number;
327 Ss : Second_Duration;
328 Le : Boolean;
330 pragma Unreferenced (Y, Mo, D, H, Mi);
332 begin
333 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
334 return Se;
335 end Second;
337 ----------------
338 -- Seconds_Of --
339 ----------------
341 function Seconds_Of
342 (Hour : Hour_Number;
343 Minute : Minute_Number;
344 Second : Second_Number := 0;
345 Sub_Second : Second_Duration := 0.0) return Day_Duration is
347 begin
348 -- Validity checks
350 if not Hour'Valid
351 or else not Minute'Valid
352 or else not Second'Valid
353 or else not Sub_Second'Valid
354 then
355 raise Constraint_Error;
356 end if;
358 return Day_Duration (Hour * 3_600) +
359 Day_Duration (Minute * 60) +
360 Day_Duration (Second) +
361 Sub_Second;
362 end Seconds_Of;
364 -----------
365 -- Split --
366 -----------
368 procedure Split
369 (Seconds : Day_Duration;
370 Hour : out Hour_Number;
371 Minute : out Minute_Number;
372 Second : out Second_Number;
373 Sub_Second : out Second_Duration)
375 Secs : Natural;
377 begin
378 -- Validity checks
380 if not Seconds'Valid then
381 raise Constraint_Error;
382 end if;
384 Secs := (if Seconds = 0.0 then 0 else Natural (Seconds - 0.5));
386 Sub_Second := Second_Duration (Seconds - Day_Duration (Secs));
387 Hour := Hour_Number (Secs / 3_600);
388 Secs := Secs mod 3_600;
389 Minute := Minute_Number (Secs / 60);
390 Second := Second_Number (Secs mod 60);
392 -- Validity checks
394 if not Hour'Valid
395 or else not Minute'Valid
396 or else not Second'Valid
397 or else not Sub_Second'Valid
398 then
399 raise Time_Error;
400 end if;
401 end Split;
403 -----------
404 -- Split --
405 -----------
407 procedure Split
408 (Date : Time;
409 Year : out Year_Number;
410 Month : out Month_Number;
411 Day : out Day_Number;
412 Seconds : out Day_Duration;
413 Leap_Second : out Boolean;
414 Time_Zone : Time_Zones.Time_Offset := 0)
416 H : Integer;
417 M : Integer;
418 Se : Integer;
419 Su : Duration;
420 Tz : constant Long_Integer := Long_Integer (Time_Zone);
422 begin
423 Formatting_Operations.Split
424 (Date => Date,
425 Year => Year,
426 Month => Month,
427 Day => Day,
428 Day_Secs => Seconds,
429 Hour => H,
430 Minute => M,
431 Second => Se,
432 Sub_Sec => Su,
433 Leap_Sec => Leap_Second,
434 Use_TZ => True,
435 Is_Historic => True,
436 Time_Zone => Tz);
438 -- Validity checks
440 if not Year'Valid
441 or else not Month'Valid
442 or else not Day'Valid
443 or else not Seconds'Valid
444 then
445 raise Time_Error;
446 end if;
447 end Split;
449 -----------
450 -- Split --
451 -----------
453 procedure Split
454 (Date : Time;
455 Year : out Year_Number;
456 Month : out Month_Number;
457 Day : out Day_Number;
458 Hour : out Hour_Number;
459 Minute : out Minute_Number;
460 Second : out Second_Number;
461 Sub_Second : out Second_Duration;
462 Time_Zone : Time_Zones.Time_Offset := 0)
464 Dd : Day_Duration;
465 Le : Boolean;
466 Tz : constant Long_Integer := Long_Integer (Time_Zone);
468 begin
469 Formatting_Operations.Split
470 (Date => Date,
471 Year => Year,
472 Month => Month,
473 Day => Day,
474 Day_Secs => Dd,
475 Hour => Hour,
476 Minute => Minute,
477 Second => Second,
478 Sub_Sec => Sub_Second,
479 Leap_Sec => Le,
480 Use_TZ => True,
481 Is_Historic => True,
482 Time_Zone => Tz);
484 -- Validity checks
486 if not Year'Valid
487 or else not Month'Valid
488 or else not Day'Valid
489 or else not Hour'Valid
490 or else not Minute'Valid
491 or else not Second'Valid
492 or else not Sub_Second'Valid
493 then
494 raise Time_Error;
495 end if;
496 end Split;
498 -----------
499 -- Split --
500 -----------
502 procedure Split
503 (Date : Time;
504 Year : out Year_Number;
505 Month : out Month_Number;
506 Day : out Day_Number;
507 Hour : out Hour_Number;
508 Minute : out Minute_Number;
509 Second : out Second_Number;
510 Sub_Second : out Second_Duration;
511 Leap_Second : out Boolean;
512 Time_Zone : Time_Zones.Time_Offset := 0)
514 Dd : Day_Duration;
515 Tz : constant Long_Integer := Long_Integer (Time_Zone);
517 begin
518 Formatting_Operations.Split
519 (Date => Date,
520 Year => Year,
521 Month => Month,
522 Day => Day,
523 Day_Secs => Dd,
524 Hour => Hour,
525 Minute => Minute,
526 Second => Second,
527 Sub_Sec => Sub_Second,
528 Leap_Sec => Leap_Second,
529 Use_TZ => True,
530 Is_Historic => True,
531 Time_Zone => Tz);
533 -- Validity checks
535 if not Year'Valid
536 or else not Month'Valid
537 or else not Day'Valid
538 or else not Hour'Valid
539 or else not Minute'Valid
540 or else not Second'Valid
541 or else not Sub_Second'Valid
542 then
543 raise Time_Error;
544 end if;
545 end Split;
547 ----------------
548 -- Sub_Second --
549 ----------------
551 function Sub_Second (Date : Time) return Second_Duration is
552 Y : Year_Number;
553 Mo : Month_Number;
554 D : Day_Number;
555 H : Hour_Number;
556 Mi : Minute_Number;
557 Se : Second_Number;
558 Ss : Second_Duration;
559 Le : Boolean;
561 pragma Unreferenced (Y, Mo, D, H, Mi);
563 begin
564 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
565 return Ss;
566 end Sub_Second;
568 -------------
569 -- Time_Of --
570 -------------
572 function Time_Of
573 (Year : Year_Number;
574 Month : Month_Number;
575 Day : Day_Number;
576 Seconds : Day_Duration := 0.0;
577 Leap_Second : Boolean := False;
578 Time_Zone : Time_Zones.Time_Offset := 0) return Time
580 Adj_Year : Year_Number := Year;
581 Adj_Month : Month_Number := Month;
582 Adj_Day : Day_Number := Day;
584 H : constant Integer := 1;
585 M : constant Integer := 1;
586 Se : constant Integer := 1;
587 Ss : constant Duration := 0.1;
588 Tz : constant Long_Integer := Long_Integer (Time_Zone);
590 begin
591 -- Validity checks
593 if not Year'Valid
594 or else not Month'Valid
595 or else not Day'Valid
596 or else not Seconds'Valid
597 or else not Time_Zone'Valid
598 then
599 raise Constraint_Error;
600 end if;
602 -- A Seconds value of 86_400 denotes a new day. This case requires an
603 -- adjustment to the input values.
605 if Seconds = 86_400.0 then
606 if Day < Days_In_Month (Month)
607 or else (Is_Leap (Year)
608 and then Month = 2)
609 then
610 Adj_Day := Day + 1;
611 else
612 Adj_Day := 1;
614 if Month < 12 then
615 Adj_Month := Month + 1;
616 else
617 Adj_Month := 1;
618 Adj_Year := Year + 1;
619 end if;
620 end if;
621 end if;
623 return
624 Formatting_Operations.Time_Of
625 (Year => Adj_Year,
626 Month => Adj_Month,
627 Day => Adj_Day,
628 Day_Secs => Seconds,
629 Hour => H,
630 Minute => M,
631 Second => Se,
632 Sub_Sec => Ss,
633 Leap_Sec => Leap_Second,
634 Use_Day_Secs => True,
635 Use_TZ => True,
636 Is_Historic => True,
637 Time_Zone => Tz);
638 end Time_Of;
640 -------------
641 -- Time_Of --
642 -------------
644 function Time_Of
645 (Year : Year_Number;
646 Month : Month_Number;
647 Day : Day_Number;
648 Hour : Hour_Number;
649 Minute : Minute_Number;
650 Second : Second_Number;
651 Sub_Second : Second_Duration := 0.0;
652 Leap_Second : Boolean := False;
653 Time_Zone : Time_Zones.Time_Offset := 0) return Time
655 Dd : constant Day_Duration := Day_Duration'First;
656 Tz : constant Long_Integer := Long_Integer (Time_Zone);
658 begin
659 -- Validity checks
661 if not Year'Valid
662 or else not Month'Valid
663 or else not Day'Valid
664 or else not Hour'Valid
665 or else not Minute'Valid
666 or else not Second'Valid
667 or else not Sub_Second'Valid
668 or else not Time_Zone'Valid
669 then
670 raise Constraint_Error;
671 end if;
673 return
674 Formatting_Operations.Time_Of
675 (Year => Year,
676 Month => Month,
677 Day => Day,
678 Day_Secs => Dd,
679 Hour => Hour,
680 Minute => Minute,
681 Second => Second,
682 Sub_Sec => Sub_Second,
683 Leap_Sec => Leap_Second,
684 Use_Day_Secs => False,
685 Use_TZ => True,
686 Is_Historic => True,
687 Time_Zone => Tz);
688 end Time_Of;
690 -----------
691 -- Value --
692 -----------
694 function Value
695 (Date : String;
696 Time_Zone : Time_Zones.Time_Offset := 0) return Time
698 D : String (1 .. 22);
699 Year : Year_Number;
700 Month : Month_Number;
701 Day : Day_Number;
702 Hour : Hour_Number;
703 Minute : Minute_Number;
704 Second : Second_Number;
705 Sub_Second : Second_Duration := 0.0;
707 begin
708 -- Validity checks
710 if not Time_Zone'Valid then
711 raise Constraint_Error;
712 end if;
714 -- Length checks
716 if Date'Length /= 19
717 and then Date'Length /= 22
718 then
719 raise Constraint_Error;
720 end if;
722 -- After the correct length has been determined, it is safe to copy the
723 -- Date in order to avoid Date'First + N indexing.
725 D (1 .. Date'Length) := Date;
727 -- Format checks
729 Check_Char (D, '-', 5);
730 Check_Char (D, '-', 8);
731 Check_Char (D, ' ', 11);
732 Check_Char (D, ':', 14);
733 Check_Char (D, ':', 17);
735 if Date'Length = 22 then
736 Check_Char (D, '.', 20);
737 end if;
739 -- Leading zero checks
741 Check_Digit (D, 6);
742 Check_Digit (D, 9);
743 Check_Digit (D, 12);
744 Check_Digit (D, 15);
745 Check_Digit (D, 18);
747 if Date'Length = 22 then
748 Check_Digit (D, 21);
749 end if;
751 -- Value extraction
753 Year := Year_Number (Year_Number'Value (D (1 .. 4)));
754 Month := Month_Number (Month_Number'Value (D (6 .. 7)));
755 Day := Day_Number (Day_Number'Value (D (9 .. 10)));
756 Hour := Hour_Number (Hour_Number'Value (D (12 .. 13)));
757 Minute := Minute_Number (Minute_Number'Value (D (15 .. 16)));
758 Second := Second_Number (Second_Number'Value (D (18 .. 19)));
760 -- Optional part
762 if Date'Length = 22 then
763 Sub_Second := Second_Duration (Second_Duration'Value (D (20 .. 22)));
764 end if;
766 -- Sanity checks
768 if not Year'Valid
769 or else not Month'Valid
770 or else not Day'Valid
771 or else not Hour'Valid
772 or else not Minute'Valid
773 or else not Second'Valid
774 or else not Sub_Second'Valid
775 then
776 raise Constraint_Error;
777 end if;
779 return Time_Of (Year, Month, Day,
780 Hour, Minute, Second, Sub_Second, False, Time_Zone);
782 exception
783 when others => raise Constraint_Error;
784 end Value;
786 -----------
787 -- Value --
788 -----------
790 function Value (Elapsed_Time : String) return Duration is
791 D : String (1 .. 11);
792 Hour : Hour_Number;
793 Minute : Minute_Number;
794 Second : Second_Number;
795 Sub_Second : Second_Duration := 0.0;
797 begin
798 -- Length checks
800 if Elapsed_Time'Length /= 8
801 and then Elapsed_Time'Length /= 11
802 then
803 raise Constraint_Error;
804 end if;
806 -- After the correct length has been determined, it is safe to copy the
807 -- Elapsed_Time in order to avoid Date'First + N indexing.
809 D (1 .. Elapsed_Time'Length) := Elapsed_Time;
811 -- Format checks
813 Check_Char (D, ':', 3);
814 Check_Char (D, ':', 6);
816 if Elapsed_Time'Length = 11 then
817 Check_Char (D, '.', 9);
818 end if;
820 -- Leading zero checks
822 Check_Digit (D, 1);
823 Check_Digit (D, 4);
824 Check_Digit (D, 7);
826 if Elapsed_Time'Length = 11 then
827 Check_Digit (D, 10);
828 end if;
830 -- Value extraction
832 Hour := Hour_Number (Hour_Number'Value (D (1 .. 2)));
833 Minute := Minute_Number (Minute_Number'Value (D (4 .. 5)));
834 Second := Second_Number (Second_Number'Value (D (7 .. 8)));
836 -- Optional part
838 if Elapsed_Time'Length = 11 then
839 Sub_Second := Second_Duration (Second_Duration'Value (D (9 .. 11)));
840 end if;
842 -- Sanity checks
844 if not Hour'Valid
845 or else not Minute'Valid
846 or else not Second'Valid
847 or else not Sub_Second'Valid
848 then
849 raise Constraint_Error;
850 end if;
852 return Seconds_Of (Hour, Minute, Second, Sub_Second);
854 exception
855 when others => raise Constraint_Error;
856 end Value;
858 ----------
859 -- Year --
860 ----------
862 function Year
863 (Date : Time;
864 Time_Zone : Time_Zones.Time_Offset := 0) return Year_Number
866 Y : Year_Number;
867 Mo : Month_Number;
868 D : Day_Number;
869 H : Hour_Number;
870 Mi : Minute_Number;
871 Se : Second_Number;
872 Ss : Second_Duration;
873 Le : Boolean;
875 pragma Unreferenced (Mo, D, H, Mi);
877 begin
878 Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
879 return Y;
880 end Year;
882 end Ada.Calendar.Formatting;