표준 라이브러리: 날짜와 시간

표준 라이브러리: 날짜와 시간 (Standard library: Dates & Times)

표준 라이브러리는 두 가지 접근 방식을 사용해 날짜와 시간 처리를 지원해요:

  • 캘린더(Calendar) 접근 방식 — 일반적으로 날짜와 시간을 다루기에 적합해요
  • 실시간(Real-time) 접근 방식 — 향상된 정밀도가 필요한 실시간 애플리케이션에 더 적합해요 (예: 절대 클록에 접근하고 시간 간격을 처리). 이 접근 방식은 시간만 지원하고 날짜는 지원하지 않는다는 점에 주의하세요.

다음 절들에서 이 두 접근 방식을 제시할게요.

출처: 표준 라이브러리: 날짜와 시간 문서

본문

날짜와 시간 처리 (Date and time handling)

Ada.Calendar 패키지는 날짜와 시간 처리를 지원해요. 간단한 예시를 볼게요:

with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

with Ada.Calendar.Formatting;
use  Ada.Calendar.Formatting;

procedure Display_Current_Time is
   Now : Time := Clock;
begin
   Put_Line ("Current time: " & Image (Now));
end Display_Current_Time;

이 예시는 Clock 함수 호출로 검색한 현재 날짜와 시간을 표시해요. Ada.Calendar.Formatting 패키지의 Image 함수를 호출해 현재 날짜와 시간에 대한 String을 얻어요. 대신 Split 함수를 사용해 각 구성 요소를 검색할 수도 있어요. 예를 들어:

with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure Display_Current_Year is
   Now         : Time := Clock;

   Now_Year    : Year_Number;
   Now_Month   : Month_Number;
   Now_Day     : Day_Number;
   Now_Seconds : Day_Duration;
begin
   Split (Now,
          Now_Year,
          Now_Month,
          Now_Day,
          Now_Seconds);

   Put_Line ("Current year  is: "
             & Year_Number'Image (Now_Year));
   Put_Line ("Current month is: "
             & Month_Number'Image (Now_Month));
   Put_Line ("Current day   is: "
             & Day_Number'Image (Now_Day));
end Display_Current_Year;

여기서 각 요소를 검색해 개별적으로 표시해요.

날짜로 지연 (Delaying using date)

특정 날짜와 시간에 재시작되도록 애플리케이션을 지연시킬 수 있어요. 태스킹 챕터에서 비슷한 것을 봤었어요. delay until 문으로 이걸 해요. 예를 들어:

with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

with Ada.Calendar.Formatting;
use  Ada.Calendar.Formatting;

with Ada.Calendar.Time_Zones;
use  Ada.Calendar.Time_Zones;

procedure Display_Delay_Next_Specific_Time is
   TZ   : Time_Offset := UTC_Time_Offset;
   Next : Time        :=
     Ada.Calendar.Formatting.Time_Of
       (Year        => 2018,
        Month       => 5,
        Day         => 1,
        Hour        => 15,
        Minute      => 0,
        Second      => 0,
        Sub_Second  => 0.0,
        Leap_Second => False,
        Time_Zone   => TZ);

   --  Next = 2018-05-01 15:00:00.00
   --         (현지 시간대)
begin
   Put_Line ("Let's wait until...");
   Put_Line (Image (Next, True, TZ));

   delay until Next;

   Put_Line ("Enough waiting!");
end Display_Delay_Next_Specific_Time;

이 예시에서 날짜의 다양한 구성 요소(년, 월 등)를 받아 Time 타입의 요소를 반환하는 Time_Of 호출로 Next를 초기화해 날짜와 시간을 지정해요. 지정된 날짜가 과거이므로 delay until 문은 눈에 띄는 효과를 내지 못해요. 하지만 미래의 날짜를 전달하면 프로그램은 그 특정 날짜와 시간이 도착할 때까지 기다려요.

여기서 시간을 현지 시간대로 변환해요. 시간대를 지정하지 않으면 기본적으로 협정 세계시(Coordinated Universal Time) (UTC로 약칭)가 사용돼요. Ada.Calendar.Time_Zones 패키지의 UTC_Time_Offset 호출로 UTC에 대한 시간 오프셋을 검색해 TZ를 초기화하고 Time_Of 호출에서 사용할 수 있어요. Time_Of에 제공된 정보를 현지 시간대 기준으로 만들려면 이 정도면 충분해요.

NextString으로 초기화해 비슷한 결과를 얻을 수도 있어요. Ada.Calendar.Formatting 패키지의 Value 호출로 이걸 할 수 있어요. 수정된 코드는 다음과 같아요:

with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

with Ada.Calendar.Formatting;
use  Ada.Calendar.Formatting;

with Ada.Calendar.Time_Zones;
use  Ada.Calendar.Time_Zones;

procedure Display_Delay_Next_Specific_Time is
   TZ   : Time_Offset := UTC_Time_Offset;
   Next : Time        :=
     Ada.Calendar.Formatting.Value
       ("2018-05-01 15:00:00.00", TZ);

   --  Next = 2018-05-01 15:00:00.00
   --         (현지 시간대)
begin
   Put_Line ("Let's wait until...");
   Put_Line (Image (Next, True, TZ));

   delay until Next;

   Put_Line ("Enough waiting!");
end Display_Delay_Next_Specific_Time;

이 예시에서도 Value 호출의 TZ를 사용해 입력 시간을 현재 시간대로 조정해요.

위 예시들에서는 특정 날짜와 시간으로 지연했어요. 태스킹 챕터에서 봤듯이 현재 시간을 기준으로 지연을 지정할 수도 있어요. 예를 들어 현재 시간을 사용해 5초를 지연시킬 수 있어요:

with Ada.Calendar; use Ada.Calendar;
with Ada.Text_IO;  use Ada.Text_IO;

procedure Display_Delay_Next is
   D    : Duration := 5.0;
   --                 ^ 초
   Now  : Time     := Clock;
   Next : Time     := Now + D;
   --                       ^ 다음 시점을
   --                         지정하는 데
   --                         duration 사용
begin
   Put_Line ("Let's wait "
             & Duration'Image (D)
             & " seconds...");
   delay until Next;
   Put_Line ("Enough waiting!");
end Display_Delay_Next;

여기서 D에 5초의 시간을 지정하고, Now의 현재 시간에 더해 그 합을 Next에 저장해요. 그런 다음 delay until 문에서 사용해요.

실시간 (Real-time)

Ada.Calendar 외에도 표준 라이브러리는 실시간 애플리케이션을 위한 시간 연산을 지원해요. 이는 Ada.Real_Time 패키지에 포함돼 있어요. 이 패키지에도 Time 타입이 있어요. 하지만 Ada.Real_Time 패키지에서 Time 타입은 절대 클록을 나타내고 시간 간격을 다루는 데 사용돼요. 이는 날짜와 시간을 나타내는 데 Time 타입을 사용하는 Ada.Calendar와 대조돼요.

이전 절에서 Ada.CalendarTime 타입과 delay until 문을 사용해 애플리케이션을 5초 지연했었어요. 대신 Ada.Real_Time 패키지를 사용할 수도 있었어요. 그 예시를 수정해 볼게요:

with Ada.Text_IO;   use Ada.Text_IO;
with Ada.Real_Time; use Ada.Real_Time;

procedure Display_Delay_Next_Real_Time is
   D     : Time_Span := Seconds (5);
   Next  : Time      := Clock + D;
begin
   Put_Line ("Let's wait "
             & Duration'Image (To_Duration (D))
             & " seconds...");
   delay until Next;
   Put_Line ("Enough waiting!");
end Display_Delay_Next_Real_Time;

주요 차이점은 D가 이제 Ada.Real_Time 패키지에 정의된 Time_Span 타입의 변수라는 것이에요. Seconds 함수를 호출해 D를 초기화하는데, 대신 Nanoseconds를 호출하면 더 미세한 단위를 얻을 수 있었어요. 또한 표시하기 전에 To_Duration으로 DDuration 타입으로 변환해야 해요.

벤치마킹 (Benchmarking)

Ada.Real_Time 패키지를 사용하는 흥미로운 응용은 벤치마킹이에요. 태스킹을 논의할 때 이전 절에서 그 패키지를 사용한 적이 있어요. 벤치마킹 예시를 볼게요:

with Ada.Text_IO;   use Ada.Text_IO;
with Ada.Real_Time; use Ada.Real_Time;

procedure Display_Benchmarking is

   procedure Computational_Intensive_App is
   begin
      delay 5.0;
   end Computational_Intensive_App;

   Start_Time, Stop_Time : Time;
   Elapsed_Time          : Time_Span;

begin
   Start_Time := Clock;

   Computational_Intensive_App;

   Stop_Time    := Clock;
   Elapsed_Time := Stop_Time - Start_Time;

   Put_Line ("Elapsed time: "
             & Duration'Image
                 (To_Duration (Elapsed_Time))
             & " seconds");
end Display_Benchmarking;

이 예시는 간단한 delay 문으로 구현된 더미 Computational_Intensive_App을 정의해요. Start_TimeStop_Time을 당시 클록에서 초기화하고 경과 시간을 계산해요. 프로그램을 실행하면 시간이 대략 5초임을 알 수 있는데, 이는 delay 문 때문이에요.

유사한 응용은 CPU 시간 벤치마킹이에요. Execution_Time 패키지를 사용해 구현할 수 있어요. CPU 시간을 측정하도록 이전 예시를 수정해 볼게요:

with Ada.Text_IO;        use Ada.Text_IO;
with Ada.Real_Time;      use Ada.Real_Time;
with Ada.Execution_Time; use Ada.Execution_Time;

procedure Display_Benchmarking_CPU_Time is

   procedure Computational_Intensive_App is
   begin
      delay 5.0;
   end Computational_Intensive_App;

   Start_Time, Stop_Time : CPU_Time;
   Elapsed_Time          : Time_Span;

begin
   Start_Time := Clock;

   Computational_Intensive_App;

   Stop_Time    := Clock;
   Elapsed_Time := Stop_Time - Start_Time;

   Put_Line ("CPU time: "
             & Duration'Image
                 (To_Duration (Elapsed_Time))
             & " seconds");
end Display_Benchmarking_CPU_Time;

이 예시에서 Start_TimeStop_TimeTime 대신 CPU_Time 타입이에요. 하지만 두 변수를 초기화하고 경과 시간을 계산하는 데는 여전히 Clock 함수를 호출해요. 프로그램을 실행하면 CPU 시간이 이전에 본 5초보다 상당히 낮다는 것을 알 수 있어요. delay 문이 많은 CPU 시간을 요구하지 않기 때문이에요. Computational_Intensive_App의 구현을 긴 루프의 수학 함수를 사용하도록 바꾸면 결과가 달라져요. 예를 들어:

with Ada.Text_IO;        use Ada.Text_IO;
with Ada.Real_Time;      use Ada.Real_Time;
with Ada.Execution_Time; use Ada.Execution_Time;

with Ada.Numerics.Generic_Elementary_Functions;

procedure Display_Benchmarking_Math is

   procedure Computational_Intensive_App is
      package Funcs is new
        Ada.Numerics.Generic_Elementary_Functions
          (Float_Type => Long_Long_Float);
      use Funcs;

      X : Long_Long_Float;
   begin
      for I in 0 .. 1_000_000 loop
         X := Tan (Arctan
                (Tan (Arctan
                  (Tan (Arctan
                    (Tan (Arctan
                      (Tan (Arctan
                        (Tan (Arctan
                          (0.577))))))))))));
      end loop;
   end Computational_Intensive_App;

   procedure Benchm_Elapsed_Time is
      Start_Time, Stop_Time : Time;
      Elapsed_Time          : Time_Span;

   begin
      Start_Time := Clock;

      Computational_Intensive_App;

      Stop_Time    := Clock;
      Elapsed_Time := Stop_Time - Start_Time;

      Put_Line ("Elapsed time: "
                & Duration'Image
                    (To_Duration (Elapsed_Time))
                & " seconds");
   end Benchm_Elapsed_Time;

   procedure Benchm_CPU_Time is
      Start_Time, Stop_Time : CPU_Time;
      Elapsed_Time          : Time_Span;

   begin
      Start_Time := Clock;

      Computational_Intensive_App;

      Stop_Time    := Clock;
      Elapsed_Time := Stop_Time - Start_Time;

      Put_Line ("CPU time: "
                & Duration'Image
                    (To_Duration (Elapsed_Time))
                & " seconds");
   end Benchm_CPU_Time;
begin
   Benchm_Elapsed_Time;
   Benchm_CPU_Time;
end Display_Benchmarking_Math;

이제 더미 Computational_Intensive_App이 상당한 CPU 시간을 요구하는 수학 연산을 포함하므로, 측정된 경과 시간과 CPU 시간이 이전보다 훨씬 더 가까워져요.

더 알아보기 (Learn more)