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;
39 package body GNAT.Calendar.Time_IO is
55 type Padding_Mode is (None, Zero, Space);
57 type Sec_Number is mod 2 ** 64;
58 -- Type used to compute the number of seconds since 01/01/1970. A 32 bit
59 -- number will cover only a period of 136 years. This means that for date
60 -- past 2106 the computation is not possible. A 64 bits number should be
61 -- enough for a very large period of time.
63 -----------------------
64 -- Local Subprograms --
65 -----------------------
67 function Am_Pm (H : Natural) return String;
68 -- Return AM or PM depending on the hour H
70 function Hour_12 (H : Natural) return Positive;
71 -- Convert a 1-24h format to a 0-12 hour format
73 function Image (Str : String; Length : Natural := 0) return String;
74 -- Return Str capitalized and cut to length number of characters. If
75 -- length is 0, then no cut operation is performed.
79 Padding : Padding_Mode := Zero;
80 Length : Natural := 0) return String;
81 -- Return image of N. This number is eventually padded with zeros or spaces
82 -- depending of the length required. If length is 0 then no padding occurs.
86 Padding : Padding_Mode := Zero;
87 Length : Natural := 0) return String;
88 -- As above with N provided in Integer format
94 function Am_Pm (H : Natural) return String is
96 if H = 0 or else H > 12 then
107 function Hour_12 (H : Natural) return Positive is
124 Length : Natural := 0) return String
126 use Ada.Characters.Handling;
127 Local : constant String :=
128 To_Upper (Str (Str'First)) &
129 To_Lower (Str (Str'First + 1 .. Str'Last));
134 return Local (1 .. Length);
144 Padding : Padding_Mode := Zero;
145 Length : Natural := 0) return String
148 return Image (Sec_Number (N), Padding, Length);
153 Padding : Padding_Mode := Zero;
154 Length : Natural := 0) return String
156 function Pad_Char return String;
162 function Pad_Char return String is
165 when None => return "";
166 when Zero => return "00";
167 when Space => return " ";
171 NI : constant String := Sec_Number'Image (N);
172 NIP : constant String := Pad_Char & NI (2 .. NI'Last);
174 -- Start of processing for Image
177 if Length = 0 or else Padding = None then
178 return NI (2 .. NI'Last);
180 return NIP (NIP'Last - Length + 1 .. NIP'Last);
189 (Date : Ada.Calendar.Time;
190 Picture : Picture_String) return String
192 Padding : Padding_Mode := Zero;
193 -- Padding is set for one directive
195 Result : Unbounded_String;
198 Month : Month_Number;
201 Minute : Minute_Number;
202 Second : Second_Number;
203 Sub_Second : Second_Duration;
208 -- Get current time in split format
210 Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
212 -- Null picture string is error
215 raise Picture_Error with "null picture string";
218 -- Loop through characters of picture string, building result
220 Result := Null_Unbounded_String;
222 while P <= Picture'Last loop
224 -- A directive has the following format "%[-_]."
226 if Picture (P) = '%' then
229 if P = Picture'Last then
230 raise Picture_Error with "picture string ends with '%";
233 -- Check for GNU extension to change the padding
235 if Picture (P + 1) = '-' then
239 elsif Picture (P + 1) = '_' then
244 if P = Picture'Last then
245 raise Picture_Error with "picture string ends with '- or '_";
248 case Picture (P + 1) is
253 Result := Result & '%';
258 Result := Result & ASCII.LF;
263 Result := Result & ASCII.HT;
268 Result := Result & Image (Hour, Padding, 2);
273 Result := Result & Image (Hour_12 (Hour), Padding, 2);
278 Result := Result & Image (Hour, Space, 2);
283 Result := Result & Image (Hour_12 (Hour), Space, 2);
288 Result := Result & Image (Minute, Padding, 2);
293 Result := Result & Am_Pm (Hour);
295 -- Time, 12-hour (hh:mm:ss [AP]M)
299 Image (Hour_12 (Hour), Padding, Length => 2) & ':' &
300 Image (Minute, Padding, Length => 2) & ':' &
301 Image (Second, Padding, Length => 2) & ' ' &
304 -- Seconds since 1970-01-01 00:00:00 UTC
305 -- (a nonstandard extension)
309 -- Compute the number of seconds using Ada.Calendar.Time
310 -- values rather than Julian days to account for Daylight
313 Neg : Boolean := False;
314 Sec : Duration := Date - Time_Of (1970, 1, 1, 0.0);
317 -- Avoid rounding errors and perform special processing
318 -- for dates earlier than the Unix Epoc.
324 Sec := abs (Sec + 0.5);
327 -- Prepend a minus sign to the result since Sec_Number
328 -- cannot handle negative numbers.
332 Result & "-" & Image (Sec_Number (Sec), None);
334 Result := Result & Image (Sec_Number (Sec), None);
341 Result := Result & Image (Second, Padding, Length => 2);
343 -- Milliseconds (3 digits)
344 -- Microseconds (6 digits)
345 -- Nanoseconds (9 digits)
347 when 'i' | 'e' | 'o' =>
349 Sub_Sec : constant Long_Integer :=
350 Long_Integer (Sub_Second * 1_000_000_000);
352 Img1 : constant String := Sub_Sec'Img;
353 Img2 : constant String :=
354 "00000000" & Img1 (Img1'First + 1 .. Img1'Last);
355 Nanos : constant String :=
356 Img2 (Img2'Last - 8 .. Img2'Last);
359 case Picture (P + 1) is
362 Nanos (Nanos'First .. Nanos'First + 2);
366 Nanos (Nanos'First .. Nanos'First + 5);
369 Result := Result & Nanos;
376 -- Time, 24-hour (hh:mm:ss)
380 Image (Hour, Padding, Length => 2) & ':' &
381 Image (Minute, Padding, Length => 2) & ':' &
382 Image (Second, Padding, Length => 2);
384 -- Locale's abbreviated weekday name (Sun..Sat)
388 Image (Day_Name'Image (Day_Of_Week (Date)), 3);
390 -- Locale's full weekday name, variable length
391 -- (Sunday..Saturday)
395 Image (Day_Name'Image (Day_Of_Week (Date)));
397 -- Locale's abbreviated month name (Jan..Dec)
401 Image (Month_Name'Image (Month_Name'Val (Month - 1)), 3);
403 -- Locale's full month name, variable length
404 -- (January..December).
408 Image (Month_Name'Image (Month_Name'Val (Month - 1)));
410 -- Locale's date and time (Sat Nov 04 12:02:33 EST 1989)
415 Result := Result & Image (Date, "%a %b %d %T %Y");
417 Result := Result & Image (Date, "%a %b %_d %_T %Y");
419 Result := Result & Image (Date, "%a %b %-d %-T %Y");
422 -- Day of month (01..31)
425 Result := Result & Image (Day, Padding, 2);
431 Image (Month, Padding, 2) & '/' &
432 Image (Day, Padding, 2) & '/' &
433 Image (Year, Padding, 2);
435 -- Day of year (001..366)
438 Result := Result & Image (Day_In_Year (Date), Padding, 3);
443 Result := Result & Image (Month, Padding, 2);
445 -- Week number of year with Sunday as first day of week
450 Offset : constant Natural :=
451 (Julian_Day (Year, 1, 1) + 1) mod 7;
453 Week : constant Natural :=
454 1 + ((Day_In_Year (Date) - 1) + Offset) / 7;
457 Result := Result & Image (Week, Padding, 2);
460 -- Day of week (0..6) with 0 corresponding to Sunday
464 DOW : Natural range 0 .. 6;
467 if Day_Of_Week (Date) = Sunday then
470 DOW := Day_Name'Pos (Day_Of_Week (Date));
473 Result := Result & Image (DOW, Length => 1);
476 -- Week number of year with Monday as first day of week
480 Result := Result & Image (Week_In_Year (Date), Padding, 2);
482 -- Last two digits of year (00..99)
486 Y : constant Natural := Year - (Year / 100) * 100;
488 Result := Result & Image (Y, Padding, 2);
494 Result := Result & Image (Year, None, 4);
497 raise Picture_Error with
498 "unknown format character in picture string";
502 -- Skip past % and format character
506 -- Character other than % is copied into the result
509 Result := Result & Picture (P);
514 return To_String (Result);
521 function Value (Date : String) return Ada.Calendar.Time is
522 D : String (1 .. 19);
523 D_Length : constant Natural := Date'Length;
526 Month : Month_Number;
529 Minute : Minute_Number;
530 Second : Second_Number;
531 Sub_Second : Second_Duration;
533 procedure Extract_Date
534 (Year : out Year_Number;
535 Month : out Month_Number;
536 Day : out Day_Number;
537 Y2K : Boolean := False);
538 -- Try and extract a date value from string D. Set Y2K to True to
539 -- account for the 20YY case. Raise Constraint_Error if the portion
540 -- of D corresponding to the date is not well formatted.
542 procedure Extract_Time
544 Hour : out Hour_Number;
545 Minute : out Minute_Number;
546 Second : out Second_Number;
547 Check_Space : Boolean := False);
548 -- Try and extract a time value from string D starting from position
549 -- Index. Set Check_Space to True to check whether the character at
550 -- Index - 1 is a space. Raise Constraint_Error if the portion of D
551 -- corresponding to the date is not well formatted.
557 procedure Extract_Date
558 (Year : out Year_Number;
559 Month : out Month_Number;
560 Day : out Day_Number;
561 Y2K : Boolean := False)
563 Delim_Index : Positive := 5;
570 if (D (Delim_Index) /= '-' or else D (Delim_Index + 3) /= '-')
572 (D (Delim_Index) /= '/' or else D (Delim_Index + 3) /= '/')
574 raise Constraint_Error;
578 Year := Year_Number'Value ("20" & D (1 .. 2));
579 Month := Month_Number'Value (D (4 .. 5));
580 Day := Day_Number'Value (D (7 .. 8));
582 Year := Year_Number'Value (D (1 .. 4));
583 Month := Month_Number'Value (D (6 .. 7));
584 Day := Day_Number'Value (D (9 .. 10));
592 procedure Extract_Time
594 Hour : out Hour_Number;
595 Minute : out Minute_Number;
596 Second : out Second_Number;
597 Check_Space : Boolean := False) is
600 if Check_Space and then D (Index - 1) /= ' ' then
601 raise Constraint_Error;
604 if D (Index + 2) /= ':' or else D (Index + 5) /= ':' then
605 raise Constraint_Error;
608 Hour := Hour_Number'Value (D (Index .. Index + 1));
609 Minute := Minute_Number'Value (D (Index + 3 .. Index + 4));
610 Second := Second_Number'Value (D (Index + 6 .. Index + 7));
613 -- Start of processing for Value
616 Split (Clock, Year, Month, Day, Hour, Minute, Second, Sub_Second);
622 and then D_Length /= 10
623 and then D_Length /= 17
624 and then D_Length /= 19
626 raise Constraint_Error;
629 -- After the correct length has been determined, it is safe to create
630 -- a local string copy in order to avoid String'First N arithmetic.
632 D (1 .. D_Length) := Date;
642 Extract_Time (1, Hour, Minute, Second);
644 Extract_Date (Year, Month, Day, True);
654 elsif D_Length = 10 then
655 Extract_Date (Year, Month, Day);
664 elsif D_Length = 17 then
665 Extract_Date (Year, Month, Day, True);
666 Extract_Time (10, Hour, Minute, Second, True);
670 -- yyyy*mm*dd hh:mm:ss
673 Extract_Date (Year, Month, Day);
674 Extract_Time (12, Hour, Minute, Second, True);
680 or else not Month'Valid
681 or else not Day'Valid
682 or else not Hour'Valid
683 or else not Minute'Valid
684 or else not Second'Valid
686 raise Constraint_Error;
689 return Time_Of (Year, Month, Day, Hour, Minute, Second, Sub_Second);
697 (Date : Ada.Calendar.Time;
698 Picture : Picture_String)
701 Ada.Text_IO.Put (Image (Date, Picture));
704 end GNAT.Calendar.Time_IO;