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-2006, 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
;
205 P
: Positive := Picture
'First;
208 Split
(Date
, Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
211 -- A directive has the following format "%[-_]."
213 if Picture
(P
) = '%' then
216 if P
= Picture
'Last then
220 -- Check for GNU extension to change the padding
222 if Picture
(P
+ 1) = '-' then
225 elsif Picture
(P
+ 1) = '_' then
230 if P
= Picture
'Last then
234 case Picture
(P
+ 1) is
239 Result
:= Result
& '%';
244 Result
:= Result
& ASCII
.LF
;
249 Result
:= Result
& ASCII
.HT
;
254 Result
:= Result
& Image
(Hour
, Padding
, 2);
259 Result
:= Result
& Image
(Hour_12
(Hour
), Padding
, 2);
264 Result
:= Result
& Image
(Hour
, Space
, 2);
269 Result
:= Result
& Image
(Hour_12
(Hour
), Space
, 2);
274 Result
:= Result
& Image
(Minute
, Padding
, 2);
279 Result
:= Result
& Am_Pm
(Hour
);
281 -- Time, 12-hour (hh:mm:ss [AP]M)
285 Image
(Hour_12
(Hour
), Padding
, Length
=> 2) & ':' &
286 Image
(Minute
, Padding
, Length
=> 2) & ':' &
287 Image
(Second
, Padding
, Length
=> 2) & ' ' &
290 -- Seconds since 1970-01-01 00:00:00 UTC
291 -- (a nonstandard extension)
295 Sec
: constant Sec_Number
:=
296 Sec_Number
(Julian_Day
(Year
, Month
, Day
)
297 - Julian_Day
(1970, 1, 1)) * 86_400
298 + Sec_Number
(Hour
) * 3_600
299 + Sec_Number
(Minute
) * 60
300 + Sec_Number
(Second
);
303 Result
:= Result
& Image
(Sec
, None
);
309 Result
:= Result
& Image
(Second
, Padding
, Length
=> 2);
311 -- Milliseconds (3 digits)
312 -- Microseconds (6 digits)
313 -- Nanoseconds (9 digits)
315 when 'i' |
'e' |
'o' =>
317 Sub_Sec
: constant Long_Integer :=
318 Long_Integer (Sub_Second
* 1_000_000_000
);
320 Img1
: constant String := Sub_Sec
'Img;
321 Img2
: constant String :=
322 "00000000" & Img1
(Img1
'First + 1 .. Img1
'Last);
323 Nanos
: constant String :=
324 Img2
(Img2
'Last - 8 .. Img2
'Last);
327 case Picture
(P
+ 1) is
330 Nanos
(Nanos
'First .. Nanos
'First + 2);
334 Nanos
(Nanos
'First .. Nanos
'First + 5);
337 Result
:= Result
& Nanos
;
344 -- Time, 24-hour (hh:mm:ss)
348 Image
(Hour
, Padding
, Length
=> 2) & ':' &
349 Image
(Minute
, Padding
, Length
=> 2) & ':' &
350 Image
(Second
, Padding
, Length
=> 2);
352 -- Locale's abbreviated weekday name (Sun..Sat)
356 Image
(Day_Name
'Image (Day_Of_Week
(Date
)), 3);
358 -- Locale's full weekday name, variable length
359 -- (Sunday..Saturday)
363 Image
(Day_Name
'Image (Day_Of_Week
(Date
)));
365 -- Locale's abbreviated month name (Jan..Dec)
369 Image
(Month_Name
'Image (Month_Name
'Val (Month
- 1)), 3);
371 -- Locale's full month name, variable length
372 -- (January..December).
376 Image
(Month_Name
'Image (Month_Name
'Val (Month
- 1)));
378 -- Locale's date and time (Sat Nov 04 12:02:33 EST 1989)
383 Result
:= Result
& Image
(Date
, "%a %b %d %T %Y");
385 Result
:= Result
& Image
(Date
, "%a %b %_d %_T %Y");
387 Result
:= Result
& Image
(Date
, "%a %b %-d %-T %Y");
390 -- Day of month (01..31)
393 Result
:= Result
& Image
(Day
, Padding
, 2);
399 Image
(Month
, Padding
, 2) & '/' &
400 Image
(Day
, Padding
, 2) & '/' &
401 Image
(Year
, Padding
, 2);
403 -- Day of year (001..366)
406 Result
:= Result
& Image
(Day_In_Year
(Date
), Padding
, 3);
411 Result
:= Result
& Image
(Month
, Padding
, 2);
413 -- Week number of year with Sunday as first day of week
418 Offset
: constant Natural :=
419 (Julian_Day
(Year
, 1, 1) + 1) mod 7;
421 Week
: constant Natural :=
422 1 + ((Day_In_Year
(Date
) - 1) + Offset
) / 7;
425 Result
:= Result
& Image
(Week
, Padding
, 2);
428 -- Day of week (0..6) with 0 corresponding to Sunday
432 DOW
: Natural range 0 .. 6;
435 if Day_Of_Week
(Date
) = Sunday
then
438 DOW
:= Day_Name
'Pos (Day_Of_Week
(Date
));
441 Result
:= Result
& Image
(DOW
, Length
=> 1);
444 -- Week number of year with Monday as first day of week
448 Result
:= Result
& Image
(Week_In_Year
(Date
), Padding
, 2);
450 -- Last two digits of year (00..99)
454 Y
: constant Natural := Year
- (Year
/ 100) * 100;
456 Result
:= Result
& Image
(Y
, Padding
, 2);
462 Result
:= Result
& Image
(Year
, None
, 4);
471 Result
:= Result
& Picture
(P
);
475 exit when P
> Picture
'Last;
479 return To_String
(Result
);
486 function Value
(Date
: String) return Ada
.Calendar
.Time
is
487 D
: String (1 .. 19);
488 D_Length
: constant Natural := Date
'Length;
491 Month
: Month_Number
;
494 Minute
: Minute_Number
;
495 Second
: Second_Number
;
496 Sub_Second
: Second_Duration
;
498 procedure Extract_Date
499 (Year
: out Year_Number
;
500 Month
: out Month_Number
;
501 Day
: out Day_Number
;
502 Y2K
: Boolean := False);
503 -- Try and extract a date value from string D. Set Y2K to True to
504 -- account for the 20YY case. Raise Constraint_Error if the portion
505 -- of D corresponding to the date is not well formatted.
507 procedure Extract_Time
509 Hour
: out Hour_Number
;
510 Minute
: out Minute_Number
;
511 Second
: out Second_Number
;
512 Check_Space
: Boolean := False);
513 -- Try and extract a time value from string D starting from position
514 -- Index. Set Check_Space to True to check whether the character at
515 -- Index - 1 is a space. Raise Constraint_Error if the portion of D
516 -- corresponding to the date is not well formatted.
522 procedure Extract_Date
523 (Year
: out Year_Number
;
524 Month
: out Month_Number
;
525 Day
: out Day_Number
;
526 Y2K
: Boolean := False)
528 Delim_Index
: Positive := 5;
535 if (D
(Delim_Index
) /= '-' or else D
(Delim_Index
+ 3) /= '-')
537 (D
(Delim_Index
) /= '/' or else D
(Delim_Index
+ 3) /= '/')
539 raise Constraint_Error
;
543 Year
:= Year_Number
'Value ("20" & D
(1 .. 2));
544 Month
:= Month_Number
'Value (D
(4 .. 5));
545 Day
:= Day_Number
'Value (D
(7 .. 8));
547 Year
:= Year_Number
'Value (D
(1 .. 4));
548 Month
:= Month_Number
'Value (D
(6 .. 7));
549 Day
:= Day_Number
'Value (D
(9 .. 10));
557 procedure Extract_Time
559 Hour
: out Hour_Number
;
560 Minute
: out Minute_Number
;
561 Second
: out Second_Number
;
562 Check_Space
: Boolean := False) is
565 if Check_Space
and then D
(Index
- 1) /= ' ' then
566 raise Constraint_Error
;
569 if D
(Index
+ 2) /= ':' or else D
(Index
+ 5) /= ':' then
570 raise Constraint_Error
;
573 Hour
:= Hour_Number
'Value (D
(Index
.. Index
+ 1));
574 Minute
:= Minute_Number
'Value (D
(Index
+ 3 .. Index
+ 4));
575 Second
:= Second_Number
'Value (D
(Index
+ 6 .. Index
+ 7));
578 -- Start of processing for Value
581 Split
(Clock
, Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
587 and then D_Length
/= 10
588 and then D_Length
/= 17
589 and then D_Length
/= 19
591 raise Constraint_Error
;
594 -- After the correct length has been determined, it is safe to create
595 -- a local string copy in order to avoid String'First N arithmetic.
597 D
(1 .. D_Length
) := Date
;
607 Extract_Time
(1, Hour
, Minute
, Second
);
609 Extract_Date
(Year
, Month
, Day
, True);
619 elsif D_Length
= 10 then
620 Extract_Date
(Year
, Month
, Day
);
629 elsif D_Length
= 17 then
630 Extract_Date
(Year
, Month
, Day
, True);
631 Extract_Time
(10, Hour
, Minute
, Second
, True);
635 -- yyyy*mm*dd hh:mm:ss
638 Extract_Date
(Year
, Month
, Day
);
639 Extract_Time
(12, Hour
, Minute
, Second
, True);
645 or else not Month
'Valid
646 or else not Day
'Valid
647 or else not Hour
'Valid
648 or else not Minute
'Valid
649 or else not Second
'Valid
651 raise Constraint_Error
;
654 return Time_Of
(Year
, Month
, Day
, Hour
, Minute
, Second
, Sub_Second
);
662 (Date
: Ada
.Calendar
.Time
;
663 Picture
: Picture_String
)
666 Ada
.Text_IO
.Put
(Image
(Date
, Picture
));
669 end GNAT
.Calendar
.Time_IO
;