0fbb1ab7624b7f0c300dfde2a93f491246b6807b
[platform/upstream/gcc.git] / gcc / ada / libgnat / a-calfor.adb
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-2019, 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 ------------------------------------------------------------------------------
31
32 with Ada.Calendar;            use Ada.Calendar;
33 with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones;
34
35 package body Ada.Calendar.Formatting is
36
37    --------------------------
38    -- Implementation Notes --
39    --------------------------
40
41    --  All operations in this package are target and time representation
42    --  independent, thus only one source file is needed for multiple targets.
43
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.
48
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.
54
55    ----------------
56    -- Check_Char --
57    ----------------
58
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;
65
66    -----------------
67    -- Check_Digit --
68    -----------------
69
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;
76
77    ---------
78    -- Day --
79    ---------
80
81    function Day
82      (Date      : Time;
83       Time_Zone : Time_Zones.Time_Offset := 0) return Day_Number
84    is
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;
93
94       pragma Unreferenced (Y, Mo, H, Mi);
95
96    begin
97       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
98       return D;
99    end Day;
100
101    -----------------
102    -- Day_Of_Week --
103    -----------------
104
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;
109
110    ----------
111    -- Hour --
112    ----------
113
114    function Hour
115      (Date      : Time;
116       Time_Zone : Time_Zones.Time_Offset := 0) return Hour_Number
117    is
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;
126
127       pragma Unreferenced (Y, Mo, D, Mi);
128
129    begin
130       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
131       return H;
132    end Hour;
133
134    -----------
135    -- Image --
136    -----------
137
138    function Image
139      (Elapsed_Time          : Duration;
140       Include_Time_Fraction : Boolean := False) return String
141    is
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;
148
149       --  Determine the two slice bounds for the result string depending on
150       --  whether the input is negative and whether fractions are requested.
151
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);
154
155       Result : String := "-00:00:00.00";
156
157    begin
158       Split (abs (Elapsed_Time), Hour, Minute, Second, Sub_Second);
159
160       --  Hour processing, positions 2 and 3
161
162       Result (2) := To_Char (Hour / 10);
163       Result (3) := To_Char (Hour mod 10);
164
165       --  Minute processing, positions 5 and 6
166
167       Result (5) := To_Char (Minute / 10);
168       Result (6) := To_Char (Minute mod 10);
169
170       --  Second processing, positions 8 and 9
171
172       Result (8) := To_Char (Second / 10);
173       Result (9) := To_Char (Second mod 10);
174
175       --  Optional sub second processing, positions 11 and 12
176
177       if Include_Time_Fraction and then Sub_Second > 0.0 then
178
179          --  Prevent rounding up when converting to natural, avoiding the zero
180          --  case to prevent rounding down to a negative number.
181
182          SS_Nat := Natural (Duration'(Sub_Second * 100.0) - 0.5);
183
184          Result (11) := To_Char (SS_Nat / 10);
185          Result (12) := To_Char (SS_Nat mod 10);
186       end if;
187
188       return Result (First .. Last);
189    end Image;
190
191    -----------
192    -- Image --
193    -----------
194
195    function Image
196      (Date                  : Time;
197       Include_Time_Fraction : Boolean := False;
198       Time_Zone             : Time_Zones.Time_Offset := 0) return String
199    is
200       To_Char : constant array (0 .. 9) of Character := "0123456789";
201
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;
211
212       --  The result length depends on whether fractions are requested.
213
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);
217
218    begin
219       Split (Date, Year, Month, Day,
220              Hour, Minute, Second, Sub_Second, Leap_Second, Time_Zone);
221
222       --  Year processing, positions 1, 2, 3 and 4
223
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);
228
229       --  Month processing, positions 6 and 7
230
231       Result (6) := To_Char (Month / 10);
232       Result (7) := To_Char (Month mod 10);
233
234       --  Day processing, positions 9 and 10
235
236       Result (9)  := To_Char (Day / 10);
237       Result (10) := To_Char (Day mod 10);
238
239       Result (12) := To_Char (Hour / 10);
240       Result (13) := To_Char (Hour mod 10);
241
242       --  Minute processing, positions 15 and 16
243
244       Result (15) := To_Char (Minute / 10);
245       Result (16) := To_Char (Minute mod 10);
246
247       --  Second processing, positions 18 and 19
248
249       Result (18) := To_Char (Second / 10);
250       Result (19) := To_Char (Second mod 10);
251
252       --  Optional sub second processing, positions 21 and 22
253
254       if Include_Time_Fraction and then Sub_Second > 0.0 then
255
256          --  Prevent rounding up when converting to natural, avoiding the zero
257          --  case to prevent rounding down to a negative number.
258
259          SS_Nat := Natural (Duration'(Sub_Second * 100.0) - 0.5);
260
261          Result (21) := To_Char (SS_Nat / 10);
262          Result (22) := To_Char (SS_Nat mod 10);
263       end if;
264
265       return Result (Result'First .. Last);
266    end Image;
267
268    ------------
269    -- Minute --
270    ------------
271
272    function Minute
273      (Date      : Time;
274       Time_Zone : Time_Zones.Time_Offset := 0) return Minute_Number
275    is
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;
284
285       pragma Unreferenced (Y, Mo, D, H);
286
287    begin
288       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
289       return Mi;
290    end Minute;
291
292    -----------
293    -- Month --
294    -----------
295
296    function Month
297      (Date      : Time;
298       Time_Zone : Time_Zones.Time_Offset := 0) return Month_Number
299    is
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;
308
309       pragma Unreferenced (Y, D, H, Mi);
310
311    begin
312       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
313       return Mo;
314    end Month;
315
316    ------------
317    -- Second --
318    ------------
319
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;
329
330       pragma Unreferenced (Y, Mo, D, H, Mi);
331
332    begin
333       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
334       return Se;
335    end Second;
336
337    ----------------
338    -- Seconds_Of --
339    ----------------
340
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
346
347    begin
348       --  Validity checks
349
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;
357
358       return Day_Duration (Hour   * 3_600) +
359              Day_Duration (Minute *    60) +
360              Day_Duration (Second)         +
361              Sub_Second;
362    end Seconds_Of;
363
364    -----------
365    -- Split --
366    -----------
367
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)
374    is
375       Secs : Natural;
376
377    begin
378       --  Validity checks
379
380       if not Seconds'Valid then
381          raise Constraint_Error;
382       end if;
383
384       Secs := (if Seconds = 0.0 then 0 else Natural (Seconds - 0.5));
385
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);
391
392       --  Validity checks
393
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;
402
403    -----------
404    -- Split --
405    -----------
406
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)
415    is
416       H  : Integer;
417       M  : Integer;
418       Se : Integer;
419       Su : Duration;
420       Tz : constant Long_Integer := Long_Integer (Time_Zone);
421
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);
437
438       --  Validity checks
439
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;
448
449    -----------
450    -- Split --
451    -----------
452
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)
463    is
464       Dd : Day_Duration;
465       Le : Boolean;
466       Tz : constant Long_Integer := Long_Integer (Time_Zone);
467
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);
483
484       --  Validity checks
485
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;
497
498    -----------
499    -- Split --
500    -----------
501
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)
513    is
514       Dd : Day_Duration;
515       Tz : constant Long_Integer := Long_Integer (Time_Zone);
516
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);
532
533       --  Validity checks
534
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;
546
547    ----------------
548    -- Sub_Second --
549    ----------------
550
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;
560
561       pragma Unreferenced (Y, Mo, D, H, Mi);
562
563    begin
564       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le);
565       return Ss;
566    end Sub_Second;
567
568    -------------
569    -- Time_Of --
570    -------------
571
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
579    is
580       Adj_Year  : Year_Number  := Year;
581       Adj_Month : Month_Number := Month;
582       Adj_Day   : Day_Number   := Day;
583
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);
589
590    begin
591       --  Validity checks
592
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;
601
602       --  A Seconds value of 86_400 denotes a new day. This case requires an
603       --  adjustment to the input values.
604
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;
613
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;
622
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;
639
640    -------------
641    -- Time_Of --
642    -------------
643
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
654    is
655       Dd : constant Day_Duration := Day_Duration'First;
656       Tz : constant Long_Integer := Long_Integer (Time_Zone);
657
658    begin
659       --  Validity checks
660
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;
672
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;
689
690    -----------
691    -- Value --
692    -----------
693
694    function Value
695      (Date      : String;
696       Time_Zone : Time_Zones.Time_Offset := 0) return Time
697    is
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;
706
707    begin
708       --  Validity checks
709
710       if not Time_Zone'Valid then
711          raise Constraint_Error;
712       end if;
713
714       --  Length checks
715
716       if Date'Length /= 19
717         and then Date'Length /= 22
718       then
719          raise Constraint_Error;
720       end if;
721
722       --  After the correct length has been determined, it is safe to copy the
723       --  Date in order to avoid Date'First + N indexing.
724
725       D (1 .. Date'Length) := Date;
726
727       --  Format checks
728
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);
734
735       if Date'Length = 22 then
736          Check_Char (D, '.', 20);
737       end if;
738
739       --  Leading zero checks
740
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);
746
747       if Date'Length = 22 then
748          Check_Digit (D, 21);
749       end if;
750
751       --  Value extraction
752
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)));
759
760       --  Optional part
761
762       if Date'Length = 22 then
763          Sub_Second := Second_Duration (Second_Duration'Value (D (20 .. 22)));
764       end if;
765
766       --  Sanity checks
767
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;
778
779       return Time_Of (Year, Month, Day,
780                       Hour, Minute, Second, Sub_Second, False, Time_Zone);
781
782    exception
783       when others => raise Constraint_Error;
784    end Value;
785
786    -----------
787    -- Value --
788    -----------
789
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;
796
797    begin
798       --  Length checks
799
800       if Elapsed_Time'Length /= 8
801         and then Elapsed_Time'Length /= 11
802       then
803          raise Constraint_Error;
804       end if;
805
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.
808
809       D (1 .. Elapsed_Time'Length) := Elapsed_Time;
810
811       --  Format checks
812
813       Check_Char (D, ':', 3);
814       Check_Char (D, ':', 6);
815
816       if Elapsed_Time'Length = 11 then
817          Check_Char (D, '.', 9);
818       end if;
819
820       --  Leading zero checks
821
822       Check_Digit (D, 1);
823       Check_Digit (D, 4);
824       Check_Digit (D, 7);
825
826       if Elapsed_Time'Length = 11 then
827          Check_Digit (D, 10);
828       end if;
829
830       --  Value extraction
831
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)));
835
836       --  Optional part
837
838       if Elapsed_Time'Length = 11 then
839          Sub_Second := Second_Duration (Second_Duration'Value (D (9 .. 11)));
840       end if;
841
842       --  Sanity checks
843
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;
851
852       return Seconds_Of (Hour, Minute, Second, Sub_Second);
853
854    exception
855       when others => raise Constraint_Error;
856    end Value;
857
858    ----------
859    -- Year --
860    ----------
861
862    function Year
863      (Date      : Time;
864       Time_Zone : Time_Zones.Time_Offset := 0) return Year_Number
865    is
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;
874
875       pragma Unreferenced (Mo, D, H, Mi);
876
877    begin
878       Split (Date, Y, Mo, D, H, Mi, Se, Ss, Le, Time_Zone);
879       return Y;
880    end Year;
881
882 end Ada.Calendar.Formatting;