Rationale for Ada 2005: Predefined library

RUSTOP
BACKNEXT

ENG

3. Times and dates

@ The first change to note is that the subtype Year_Number in the package Ada.Calendar in Ada 2005 is

  1        subtype Year_Number is Integer range 1901 .. 2399;

@ In Ada 95 (and in Ada 83) the range is 1901 .. 2099. This avoids the leap year complexity caused by the 400 year rule at the expense of the use of dates in the far future. But, the end of the 21st century is perhaps not so far into the future, so it was decided that the 2.1k problem should be solved now rather than later. However, it was decided not to change the lower bound because some systems are known to have used that as a time datum. The upper bound was chosen in order to avoid difficulties for implementations. For example, with one nanosecond for Duration'Small, the type Time can just be squeezed into 64 bits.

@ Having grasped the nettle of doing leap years properly Ada 2005 dives in and deals with leap seconds, time zones and other such matters in pitiless detail.

@ There are three new child packages Calendar.Time_Zones, Calendar.Arithmetic and Calendar.Formatting. We will look at these in turn.

@ The specification of the first is

  1        package Ada.Calendar.Time_Zones is
  2                -- Time zone manipulation:
  3                type Time_Offset is range –28*60 .. 28*60;
  4                Unknown_Zone_Error : exception;
  5                function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;
  6        end Ada.Calendar.Time_Zones;

@ Time zones are described in terms of the number of minutes different from UTC (which curiously is short for Coordinated Universal Time); this is close to but not quite the same as Greenwich Mean Time (GMT) and similarly does not suffer from leaping about in spring and falling about in the autumn. It might have seemed more natural to use hours but some places (for example India) have time zones which are not an integral number of hours different from UTC.

@ Time is an extraordinarily complex subject. The difference between GMT and UTC is never more than one second but at the moment of writing there is a difference of about 0.577 seconds. The BBC broadcast timesignals based on UTC but call them GMT and with digital broadcasting they turn up late anyway. The chronophile might find the website www.merlyn.demon.co.uk/misctime.htm# GMT of interest.

@ So the function UTC_Time_Offset applied in an Ada program in Paris to a value of type Time in summer should return a time offset of 120 (one hour for European Central Time plus one hour for daylight saving or heure d’йtй). Remember that the type Calendar.Time incorporates the date. To find the offset now (that is, at the time of the function call) we simply write

  1        Offset := UTC_Time_Offset;

@ and then Clock is called by default.

@ To find what the offset was on Christmas Day 2000 we write

  1        Offset := UTC_Time_Offset (Time_Of (2000, 12, 25));

@ and this should return 60 in Paris. So the poor function has to remember the whole history of local time changes since 1901 and predict them forward to 2399 – these Ada systems are pretty smart! In reality the intent is to use whatever the underlying operating system provides. If the information is not known then it can raise Unknown_Zone_Error.

@ Note that we are assuming that the package Calendar is set to the local civil (or wall clock) time. It doesn't have to be but one expects that to be the normal situation. Of course it is possible for an Ada system running in California to have Calendar set to the local time in New Zealand but that would be unusual. Equally, Calendar doesn't have to adjust with daylight saving but we expect that it will. (No wonder that Ada.Real_Time was introduced for vital missions such as boiling an egg.) A useful fact is that Clock – Duration (UTC_Time_Offset*60) gives UTC time – provided we don't do this just as daylight saving comes into effect in which case the call of Clock and that of UTC_Time_Offset might not be compatible.

@ More generally the type Time_Offset can be used to represent the difference between two time zones. If we want to work with the difference between New York and Paris then we could say

  1        NY_Paris : Time_Offset := –360;

@ The time offset between two different places can be greater than 24 hours for two reasons. One is that the International Date Line weaves about somewhat and the other is that daylight saving time can extend the difference as well. Differences of 26 hours can easily occur and 27 hours is possible.

@ Accordingly the range of the type Time_Offset allows for a generous 28 hours.

@ The package Calendar.Arithmetic provides some awkward arithmetic operations and also covers leap seconds. Its specification is

  1        package Ada.Calendar.Arithmetic is
  2                -- Arithmetic on days:
  3                type Day_Count is range
  4366*(1+Year_Number'LastYear_Number'First)
  5                        ..
  6                        +366*(1+Year_Number'LastYear_Number'First);
  7                subtype Leap_Seconds_Count is Integer range –2047 .. 2047;
  8                procedure Difference
  9                (       Left, Right  : in Time;
 10                        Days         : out Day_Count;
 11                        Seconds      : out Duration;
 12                        Leap_Seconds : out Leap_Seconds_Count);
 13                function "+" (Left : Time; Right : Day_Count) return Time;
 14                function "+" (Left : Day_Count; Right : Time) return Time;
 15                function "–" (Left : Time; Right : Day_Count) return Time;
 16                function "–" (Left, Right : Time) return Day_Count;
 17        end Ada.Calendar.Arithmetic;

@ The range for Leap_Seconds_Count is generous. It allows for a leap second at least four time a year for the foreseeable future – the somewhat arbitrary range chosen allows the value to be accommodated in 12 bits. And the 366 in Day_Count is also a bit generous – but the true expression would be very unpleasant.

@ One of the problems with the old planet is that it is slowing down and a day as measured by the Earth's rotation is now a bit longer than 86,400 seconds. Naturally enough we have to keep the seconds uniform and so in order to keep worldly clocks synchronized with the natural day, an odd leap second has to be added from time to time. This is always added at midnight UTC (which means it can pop up in the middle of the day in other time zones). The existence of leap seconds makes calculations with times somewhat tricky.

@ The basic trouble is that we want to have our cake and eat it. We want to have the invariant that a day has 86,400 seconds but unfortunately this is not always the case.

@ The procedure Difference operates on two values of type Time and gives the result in three parts, the number of days (an integer), the number of seconds as a Duration and the number of leap seconds (an integer). If Left is later then Right then all three numbers will be nonnegative; if earlier, then nonpositive.

@ Remember that Difference like all these other operations always works on local time as defined by the clock in Calendar (unless stated otherwise).

@ Suppose we wanted to find the difference between noon on June 1st 1982 and 2pm on July 1st 1985 according to a system set to UTC. We might write

  1        Days  : Day_Count;
  2        Secs  : Duration;
  3        Leaps : Leap_Seconds_Count;
  4        ...
  5        Difference (Time_Of (1985, 7, 1, 14*3600.0),
  6        Time_Of (1982, 6, 1, 12*3600.0), Days, Secs, Leaps);

@ The results should be

  1        Days = 365+366+365+30 = 1126,
  2        Secs = 7200.0,
  3        Leaps = 2.

@ There were leap seconds on 30 June 1983 and 30 June 1985.

@ The functions "+" and "–" apply to values of type Time and Day_Count (whereas those in the parent Calendar apply only to Time and Duration and thus only work for intervals of a day or so). Note that the function "–" between two values of type Time in this child package produces the same value for the number of days as the corresponding call of the function Difference – leap seconds are completely ignored. Leap seconds are in fact ignored in all the operations "+" and "–" in the child package.

@ However, it should be noted that Calendar."–" counts the true seconds and so the expression

  1        Calendar."–" (Time_Of (1985, 7, 1, 1*3600.0), Time_Of (1985, 6, 30, 23*3600.0))

@ has the Duration value 7201.0 and not 7200.0 because of the leap second at midnight that night. (We are assuming that our Ada system is running at UTC.) The same calculation in New York will produce 7200.0 because the leap second doesn't occur until 4 am in EST (with daylight saving).

@ Note also that

  1        Calendar."–" (Time_Of (1985, 7, 1, 0.0), Time_Of (1985, 6, 30, 0.0))

@ in Paris where the leap second occurs at 10pm returns 86401.0 whereas the same calculation in New York will return 86400.0.

@ The third child package Calendar.Formatting has a variety of functions. Its specification is

  1        with Ada.Calendar.Time_Zones;
  2        use Ada.Calendar.Time_Zones;
  3        package Ada.Calendar.Formatting is
  4                -- Day of the week:
  5                type Day_Name is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
  6                function Day_Of_Week (Date : Time) return Day_Name;
  7                -- Hours:Minutes:Seconds access:
  8                subtype Hour_Number is Natural range 0 .. 23;
  9                subtype Minute_Number is Natural range 0 .. 59;
 10                subtype Second_Number is Natural range 0 .. 59;
 11                subtype Second_Duration is Day_Duration range 0.0 .. 1.0;
 12                function Year (Date : Time; Time_Zone : Time_Offset := 0) return Year_Number;
 13                -- similarly functions Month, Day, Hour, Minute
 14                function Second (Date : Time) return Second_Number;
 15                function Sub_Second (Date : Time) return Second_Duration;
 16                function Seconds_Of
 17                (       Hour       : Hour_Number;
 18                        Minute     : Minute_Number;
 19                        Second     : Second_Number := 0;
 20                        Sub_Second : Second_Duration := 0.0) return Day_Duration;
 21                procedure Split
 22                (       Seconds    : in Day_Duration; -- (1)
 23                        Hour       : out Hour_Number;
 24                        Minute     : out Minute_Number;
 25                        Second     : out Second_Number;
 26                        Sub_Second : out Second_Duration);
 27                procedure Split
 28                (       Date       : in Time; -- (2)
 29                        Year       : out Year_Number;
 30                        Month      : out Month_Number;
 31                        Day        : out Day_Number;
 32                        Hour       : out Hour_Number;
 33                        Minute     : out Minute_Number;
 34                        Second     : out Second_Number;
 35                        Sub_Second : out Second_Duration;
 36                        Time_Zone  : in Time_Offset := 0);
 37                function Time_Of
 38                (       Year        : Year_Number;
 39                        Month       : Month_Number;
 40                        Day         : Day_Number;
 41                        Hour        : Hour_Number;
 42                        Minute      : Minute_Number;
 43                        Second      : Second_Number;
 44                        Sub_Second  : Second_Duration := 0.0;
 45                        Leap_Second : Boolean := False;
 46                        Time_Zone   : Time_Offset := 0) return Time;
 47                function Time_Of
 48                (       Year        : Year_Number;
 49                        Month       : Month_Number;
 50                        Day         : Day_Number;
 51                        Seconds     : Day_Duration;
 52                        Leap_Second : Boolean := False;
 53                        Time_Zone   : Time_Offset := 0) return Time;
 54                procedure Split
 55                (       Date        : in Time; -- (3)
 56                        ... -- as (2) but with additional parameter
 57                        Leap_Second : out Boolean;
 58                        Time_Zone   : in Time_Offset := 0);
 59                procedure Split
 60                (       Date        : in Time; -- (4)
 61                        ... -- as Calendar.Split
 62                        ... -- but with additional parameters
 63                        Leap_Second : out Boolean;
 64                        Time_Zone   : in Time_Offset := 0);
 65                -- Simple image and value:
 66                function Image
 67                (       Date                  : Time;
 68                        Include_Time_Fraction : Boolean := False;
 69                        Time_Zone             : Time_Offset := 0) return String;
 70                function Value (Date : String; Time_Zone : Time_Offset := 0) return Time;
 71                function Image (Elapsed_Time : Duration;
 72                Include_Time_Fraction : Boolean := False) return String;
 73                function Value (Elapsed_Time : String) return Duration;
 74        end Ada.Calendar.Formatting;

@ The function Day_Of_Week will be much appreciated. It is a nasty calculation.

@ Then there are functions Year, Month, Day, Hour, Minute, Second and Sub_Second which return the corresponding parts of a Time taking account of the time zone given as necessary. It is unfortunate that functions returning the parts of a time (as opposed to the parts of a date) were not provided in Calendar originally. All that Calendar provides is Seconds which gives the number of seconds from midnight and leaves users to chop it up for themselves. Note that Calendar.Second returns a Duration whereas the function in this child package is Seconds which returns an Integer. The fraction of a second is returned by Sub_Second.

@ Most of these functions have an optional parameter which is a time zone offset. Wherever in the world we are running, if we want to know the hour according to UTC then we write

  1        Hour (Clock, UTC_Time_Offset)

@ If we are in New York and want to know the hour in Paris then we write

  1        Hour (Clock, –360)

@ since New York is 6 hours (360 minutes) behind Paris.

@ Note that Second and Sub_Second do not have the optional Time_Offset parameter because offsets are an integral number of minutes and so the number of seconds does not depend upon the time zone.

@ The package also generously provides four procedures Split and two procedures Time_Of. These have the same general purpose as those in Calendar. There is also a function Seconds_Of. We will consider them in the order of declaration in the package specification above.

@ The function Seconds_Of creates a value of type Duration from components Hour, Minute, Second and Sub_Second. Note that we can use this together with Calendar.Time_Of to create a value of type Time. For example

  1        T := Time_Of (2005, 4, 2, Seconds_Of (22, 4, 10, 0.5));

@ makes the time of the instant when I (originally) typed that last semicolon.

@ The first procedure Split is the reverse of Seconds_Of. It decomposes a value of type Duration into Hour, Minute, Second and Sub_Second. It is useful with the function Calendar.Split thus

  1        Split (Some_Time, Y, M, D, Secs); -- split time
  2        Split (Secs, H, M, S, SS); -- split secs

@ The next procedure Split (no 2) takes a Time and a Time_Offset (optional) and decomposes the time into its seven components. Note that the optional parameter is last for convenience. The normal rule for parameters of predefined procedures is that parameters of mode in are first and parameters of mode out are last. But this is a nuisance if parameters of mode in have defaults since this forces named notation if the default is used.

@ There are then two functions Time_Of which compose a Time from its various constituents and the Time_Offset (optional). One takes seven components (with individual Hour, Minute etc) whereas the other takes just four components (with Seconds in the whole day). An interesting feature of these two functions is that they also have a Boolean parameter Leap_Second which by default is False.

@ The purpose of this parameter needs to be understood carefully. Making up a typical time will have this parameter as False. But suppose we need to compose the time midway through the leap second that occurred on 30 June 1985 and assign it to a variable Magic_Moment. We will assume that our Calendar is in New York and set to EST with daylight saving (and so midnight UTC is 8pm in New York). We would write

  1        Magic_Moment : Time := Time_Of (1985, 6, 30, 19, 59, 59, 0.5, True);

@ In a sense there were two 19:59:59 that day in New York. The proper one and then the leap one; the parameter distinguishes them. So the moment one second earlier is given by

  1        Normal_Moment : Time := Time_Of (1985, 6, 30, 19, 59, 59, 0.5, False);

@ We could have followed ISO and used 23:59:60 UTC and so have subtype Second_Number is Natural range 0 .. 60; but this would have produced an incompatibility with Ada 95.

@ Note that if the parameter Leap_Second is True and the other parameters do not identify a time of a leap second then Time_Error is raised.

@ There are then two corresponding procedures Split (nos 3 and 4) with an out parameter Leap_Second. One produces seven components and the other just four. The difference between this seven-component procedure Split (no 3) and the earlier Split (no 2) is that this one has the out parameter Leap_Second whereas the other does not. Writing

  1        Split (Magic_Moment, 0, Y, M, D, H, M, S, SS, Leap);

@ results in Leap being True whereas

  1        Split (Normal_Moment, 0, Y, M, D, H, M, S, SS, Leap);

@ results in Leap being False but gives all the other out parameters (Y, ... , SS) exactly the same values.

@ On the other hand calling the version of Split (no 2) without the parameter Leap_Second thus

  1        Split (Magic_Moment , 0, Y, M, D, H, M, S, SS);
  2        Split (Normal_Moment, 0, Y, M, D, H, M, S, SS);

@ produces exactly the same results.

@ The reader might wonder why there are two Splits on Time with Leap_Second but only one without.

@ This is because the parent package Calendar already has the other one (although without the time zone parameter). Another point is that in the case of Time_Of, we can default the Leap parameter being of mode in but in the case of Split the parameter has mode out and cannot be omitted. It would be bad practice to encourage the use of a dummy parameter which is ignored and hence there have to be additional versions of Split.

@ Finally, there are two pairs of functions Image and Value. The first pair works with values of type Time. A call of Image returns a date and time value in the standard ISO 8601 format. Thus taking the Normal_Moment above

  1        Image (Normal_Moment)

@ returns the following string "1985-06-30 19:59:59" -- in New York If we set the optional parameter Include_Time_Fraction to True thus

  1        Image (Normal_Moment, True)

@ then we get "1985-06-30 19:59:59.50" There is also the usual optional Time_Zone parameter so we could produce the time in Paris (from the program in New York) thus

  1        Image (Normal_Moment, True, –360)

@ giving "1985-07-01 02:59:59.50" -- in Paris The matching Value function works in reverse as expected.

@ We would expect to get exactly the same results with Magic_Moment. However, since some implementations might have an ISO function available in their operating system it is also allowed to produce "1985-06-30 19:59:60" -- in New York The other Image and Value pair work on values of type Duration thus

  1        Image (10_000.0) -- "02:46:40"

@ with the optional parameter Include_Time_Fraction as before. Again the corresponding function Value works in reverse.

Rationale for Ada 2005: Predefined library

@ENGRUSTOPBACKNEXT

3. Дата и время

@ Первое изменение в этой области в Аде 2005, которое следует упомянуть это подтип Year_Number в пакете Ada.Calendar.

  1        subtype Year_Number is Integer range 1901 .. 2399;

@ В Аде 95 (и в Аде 83) этот диапазон - 1901.. 2099. Это нововведение позволяет избежать проблем с высокосным годом в соответствии с 400-летним правилом за счет использования дат в далеком будущем. Но, конец 21-ого столетия, возможно, не такое уж далёкое будущее и, таким образом, проблема 2100 года должна быть решена уже теперь, а не когда-нибудь потом. Однако, было решено не изменять нижнюю границу, потому что некоторые системы, как известно, использовали её как базовую точку отсчёта времени. Верхняя граница была выбрана чтобы избежать трудностей при реализаций. Например, с одной наносекундой для Duration'Small, тип Time может уместиться в 64 бита.

@ Взявшись должным образом за проблему високосных годов Ада 2005 имеет дело с скачками секунд, часовыми поясами и другими такими делами.

@ Есть три новых дочерних пакета Calendar.Time_Zones, Calendar.Arithmetic и Calendar.Formatting.

@ Спецификация первого

  1        package Ada.Calendar.Time_Zones is
  2                -- Time zone manipulation:
  3                type Time_Offset is range –28*60 .. 28*60;
  4                Unknown_Zone_Error : exception;
  5                function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;
  6        end Ada.Calendar.Time_Zones;

@ Часовые пояса отсчитываются в минутах относительно UTC (Координированное Среднее Время); это почти тоже самое что и GMT (Среднее время по Гринвичу) и оно так же не страдает от перевода часов весной и осенью. Возможно, казалось более естественным мерять часовые пояса в часах, но у некоторых мест (например в Индии) есть часовые пояса, которые не являются целым числом часов относительно UTC.

@ Время - необычайно сложный предмет. Различие между GMT и UTC не больше секунды, а в настоящий момент она составляет приблизительно 0.577 секунды. BBC передаёт проверки точного времени основанные на UTC, но называет их GMT, и с цифровой передачей они поднимаются поздно так или иначе. Хронофилы могут зайти на вебсайт www.merlyn.demon.co.uk/misctime.htm # GMT для интереса.

@ Таким образом, функция UTC_Time_Offset, примененная в Ада - программе в Париже к значению типа Time летом должна возвратить смещение времени 120 минут (один час в средне-европейского времени плюс один час поправки для летнего времени). Напомним что тип Calendar.Time включает и дату. Чтобы теперь найти смещение (то есть, во время вызова функции) мы просто пишем:

  1        Offset := UTC_Time_Offset;

@ и тогда часы вызываются по умолчанию.

@ Чтобы найти смещение которое было на Рождество 2000 мы пишем:

  1        Offset := UTC_Time_Offset (Time_Of (2000, 12, 25));

@ и это должно возвратить 60 в Париже. Таким образом, плохая функция должна помнить целую хронологию изменений местного времени с 1901 и предсказывать их плодь до 2399 года - эти системы Ады довольно умны! В действительности намерение состоит в том, чтобы использовать возможности операционной системы. Если информация не известна тогда, она может возбудить исключение Unknown_Zone_Error.

@ Отметим, что мы предполагаем, что пакет Calendar установлен на местное время (или настенные часы). Это вполне ожидаемая нормальная ситуация. Конечно, вполне возможно чтобы для работающей Ада-системы в Калифорнии установить Календарь на местное время Новой Зеландии, но согласитесь, это была бы далеко необычная ситуация. Естественно, Календарь не должен корректироваться с переходом на летнее и зимнее время, как мы и ожидали. (Неудивительно что Ада.Real_Time был введен для жизненных миссий, таких как кипячение яйца). Полезный факт состоит в том, что Clock - Duration (UTC_Time_Offset*60) дает UTC время - вне зависимости от перехода на зимнее и летнее время, иначе бы запрос Clock и UTC_Time_Offset не были бы совместимыми.

@ Более широко тип Time_Offset может использоваться, чтобы представить различие между двумя часовыми поясами. Если мы хотим работать с различием между Нью-Йорком и Парижем тогда, мы могли бы написать:

  1        NY_Paris : Time_Offset := –360;

@ Смещение времени между двумя различными местами может быть больше 24 часов в двух случаях. Во-первых, когда Демаркационная линия времени несколько раз переплетается, и во-вторых, в случае перехода на летнее время. Различия в 26 и даже в 27 часов могут легко произойти.

@ Соответственно диапазон типа Time_Offset щедро определён аж до 28 часов.

@ Пакет Calendar.Arithmetic обеспечивает некоторые неуклюжие арифметические операции и также покрывает скачки секунд. Его спецификация:

  1        package Ada.Calendar.Arithmetic is
  2                -- Arithmetic on days:
  3                type Day_Count is range
  4366*(1+Year_Number'LastYear_Number'First)
  5                        ..
  6                        +366*(1+Year_Number'LastYear_Number'First);
  7                subtype Leap_Seconds_Count is Integer range –2047 .. 2047;
  8                procedure Difference
  9                (       Left, Right  : in Time;
 10                        Days         : out Day_Count;
 11                        Seconds      : out Duration;
 12                        Leap_Seconds : out Leap_Seconds_Count);
 13                function "+" (Left : Time; Right : Day_Count) return Time;
 14                function "+" (Left : Day_Count; Right : Time) return Time;
 15                function "–" (Left : Time; Right : Day_Count) return Time;
 16                function "–" (Left, Right : Time) return Day_Count;
 17        end Ada.Calendar.Arithmetic;

@ Диапазон для Leap_Seconds_Count щедр. Он обеспечивает скачки секунд по крайней мере до четырёх раз в год для обозримого будущего - несколько произвольно выбранный диапазон кодируется 12 битами. Также предел 366 для Day_Count также немного щедр - но истинное выражение было бы очень неприятно.

@ Одна из проблем с нашей планетой состоит в том, что она замедляется, и день который определяется вращением Земли теперь немного больше 86400 секунд. Достаточно естественно мы должны сохранить униформу секунд и так, чтобы сохранить мирские часы синхронизированными с естественным днем, нечетный секундный скачёк должен время от времени добавляться. Он всегда добавляется в полночь UTC (что означает то что он может призойти в середине дня в других часовых поясах). Существование скачков секунд делает вычисление времени довольно хитрым занятием.

@ Основная неприятность состоит в том, что мы хотим одновременно и иметь наш пирог и съесть его. Мы хотим иметь инвариант, что у дня есть 86400 секунд, но к сожалению это - не всегда возможно.

@ Процедура Difference принимает два параметра типа Time и выдает результат в виде трех значений, числа дней (целое число), числа секунд как Duration и числа скачков секунд (целое число). Если Left раньше чем Right тогда, все три числа будут неотрицательными; если посже, то неположительными.

@ Напомним, что Difference как все эти другие операции всегда воздействует на местное время как определено часами в Calendar (если не заявлено иначе).

@ Предположим, что мы хотим найти различие между полуднем 1-ого июня 1982 и 2pm 1-ого июля 1985 согласно системному набору к UTC. Мы могли бы написать:

  1        Days  : Day_Count;
  2        Secs  : Duration;
  3        Leaps : Leap_Seconds_Count;
  4        ...
  5        Difference (Time_Of (1985, 7, 1, 14*3600.0),
  6        Time_Of (1982, 6, 1, 12*3600.0), Days, Secs, Leaps);

@ Результат должен быть такой:

  1        Days = 365+366+365+30 = 1126,
  2        Secs = 7200.0,
  3        Leaps = 2.

@ Т.е. имеются скачки секунд 30 июня 1983 и 30 июня 1985.

@ Функции "+" и "-" применимы к значениям типа Time и Day_Count (тогда как в родительском пакете Calendar применяются только к Time и Duration, и таким образом, работают только на интервалах приблизительно одного дня). Отметим, что функция "-" между двумя значениями типа Time в этом дочернем пакете производит то же самое значение для числа дней как и соответствующий вызов функции Difference - скачки секунд полностью игнорируются. Скачки секунд фактически игнорируются и во всех операциях "+" и "-" в дочернем пакете.

@ Однако, надо отметить что Calendar."-" считает истинные секунды и так выражение:

  1        Calendar."–" (Time_Of (1985, 7, 1, 1*3600.0), Time_Of (1985, 6, 30, 23*3600.0))

@ имеет значение Duration 7201.0 а не 7200.0 из-за скачка секунды в полночь той ночью. (Мы предполагаем, что наша Ада-система работает в UTC). То же самое вычисление в Нью-Йорке выдаст 7200.0, потому что скачок секунды не происходит до 4:00 в EST (летнего времени).

@ Отметим также что:

  1        Calendar."–" (Time_Of (1985, 7, 1, 0.0), Time_Of (1985, 6, 30, 0.0))

@ в Париже, где скачок секунды происходит в 10:00 результат будет 86401.0, тогда как то же самое вычисление в Нью-Йорке выдаст 86400.0.

@ Третий дочерний пакет Calendar.Formatting имеет множество функций. Его спецификация:

  1        with Ada.Calendar.Time_Zones;
  2        use Ada.Calendar.Time_Zones;
  3        package Ada.Calendar.Formatting is
  4                -- Day of the week:
  5                type Day_Name is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
  6                function Day_Of_Week (Date : Time) return Day_Name;
  7                -- Hours:Minutes:Seconds access:
  8                subtype Hour_Number is Natural range 0 .. 23;
  9                subtype Minute_Number is Natural range 0 .. 59;
 10                subtype Second_Number is Natural range 0 .. 59;
 11                subtype Second_Duration is Day_Duration range 0.0 .. 1.0;
 12                function Year (Date : Time; Time_Zone : Time_Offset := 0) return Year_Number;
 13                -- similarly functions Month, Day, Hour, Minute
 14                function Second (Date : Time) return Second_Number;
 15                function Sub_Second (Date : Time) return Second_Duration;
 16                function Seconds_Of
 17                (       Hour       : Hour_Number;
 18                        Minute     : Minute_Number;
 19                        Second     : Second_Number := 0;
 20                        Sub_Second : Second_Duration := 0.0) return Day_Duration;
 21                procedure Split
 22                (       Seconds    : in Day_Duration; -- (1)
 23                        Hour       : out Hour_Number;
 24                        Minute     : out Minute_Number;
 25                        Second     : out Second_Number;
 26                        Sub_Second : out Second_Duration);
 27                procedure Split
 28                (       Date       : in Time; -- (2)
 29                        Year       : out Year_Number;
 30                        Month      : out Month_Number;
 31                        Day        : out Day_Number;
 32                        Hour       : out Hour_Number;
 33                        Minute     : out Minute_Number;
 34                        Second     : out Second_Number;
 35                        Sub_Second : out Second_Duration;
 36                        Time_Zone  : in Time_Offset := 0);
 37                function Time_Of
 38                (       Year        : Year_Number;
 39                        Month       : Month_Number;
 40                        Day         : Day_Number;
 41                        Hour        : Hour_Number;
 42                        Minute      : Minute_Number;
 43                        Second      : Second_Number;
 44                        Sub_Second  : Second_Duration := 0.0;
 45                        Leap_Second : Boolean := False;
 46                        Time_Zone   : Time_Offset := 0) return Time;
 47                function Time_Of
 48                (       Year        : Year_Number;
 49                        Month       : Month_Number;
 50                        Day         : Day_Number;
 51                        Seconds     : Day_Duration;
 52                        Leap_Second : Boolean := False;
 53                        Time_Zone   : Time_Offset := 0) return Time;
 54                procedure Split
 55                (       Date        : in Time; -- (3)
 56                        ... -- as (2) but with additional parameter
 57                        Leap_Second : out Boolean;
 58                        Time_Zone   : in Time_Offset := 0);
 59                procedure Split
 60                (       Date        : in Time; -- (4)
 61                        ... -- as Calendar.Split
 62                        ... -- but with additional parameters
 63                        Leap_Second : out Boolean;
 64                        Time_Zone   : in Time_Offset := 0);
 65                -- Simple image and value:
 66                function Image
 67                (       Date                  : Time;
 68                        Include_Time_Fraction : Boolean := False;
 69                        Time_Zone             : Time_Offset := 0) return String;
 70                function Value (Date : String; Time_Zone : Time_Offset := 0) return Time;
 71                function Image (Elapsed_Time : Duration;
 72                Include_Time_Fraction : Boolean := False) return String;
 73                function Value (Elapsed_Time : String) return Duration;
 74        end Ada.Calendar.Formatting;

@ Функция Day_Of_Week будет очень цениться. Ибо это весьма противное вычисление.

@ Имеются функции Year, Month, Day, Hour, Minute, Second и Sub_Second, которые выдают соответствующие части Time, принимающего во внимание часовой пояс, данный по мере необходимости. Очень жаль, что функции выдающие части времени (в противоположность частям даты) не были предоставлены в пакете Calendar первоначально. Всё что обеспечивает Calendar это функция Seconds которая выдает число секунд после полуночи предоставляя пользователям самим нарубить время на часы. Отметим что Calendar.Second возвращает Duration, тогда как функция Seconds в дочернем пакете возвращает Integer. Доли секунды выдаёт функция Sub_Second.

@ У большинства этих функций есть дополнительный параметр, который является смещением часового пояса. В любом месте мира где бы мы ни работали, если мы хотим знать час согласно UTC мы пишем:

  1        Hour (Clock, UTC_Time_Offset)

@ Если же мы находимся в Нью-Йорке и хотим знать час в Париже тогда, мы пишем:

  1        Hour (Clock, –360)

@ так как разница с Нью-Йорком и Парижем составляет 6 часов (360 минут).

@ Отметим, что у Second и у Sub_Second нет дополнительного параметра Time_Offset, потому что смещения - целое число минут и, таким образом, число секунд не зависит от часового пояса.

@ Пакет также великодушно предоставляет четыре процедуры Split и две процедуры Time_Of. Они имеют то же самое предназначение что и в пакете Calendar. Имеется также функция Seconds_Of. Мы рассмотрим их в порядке объявления в спецификации пакета выше.

@ Функция Seconds_Of создаёт значение типа Duration из компонентов Hour, Minute, Second и Sub_Second. Отметим, что мы можем использовать её вместе с Calendar.Time_Of для создания значения типа Time. Например:

  1        T := Time_Of (2005, 4, 2, Seconds_Of (22, 4, 10, 0.5));

@ делает время момента, когда я (первоначально) напечатал ту последнюю точку с запятой.

@ Первая процедура Split является обратной Seconds_Of. Она разбирает значение типа Duration на Hour, Minute, Second и Sub_Second. Её совместное использование с функцией Calendar.Split весьма полезно:

  1        Split (Some_Time, Y, M, D, Secs); -- split time
  2        Split (Secs, H, M, S, SS); -- split secs

@ Следующая процедура Split (номер 2) берёт Time и Time_Offset (опционально) и разбивает время на семь компонентов. Отметим, что дополнительный параметр является последним для удобства. Нормальное правило для параметров предопределенных процедур состоит в том, что параметры режима in являются первыми, и параметры режима out являются последними. Но это - неприятность, если у параметров режима in есть значения по умолчанию, так как это требует именнованного назначения остальных параметров, если значение по умолчанию используется.

@ Ещё имеются две функции Time_Of, которые составляют Time из его различных непосредственных составляющих и Time_Offset (опционально). Каждая берет семь компонентов (с индивидуальным Hour, Minute и т.д) тогда как другие берут только четыре компонента (с Seconds в целый день). Интересная особенность этих двух функций состоит в том, что у них также есть параметр Leap_Second Boolean, который по умолчанию является False.

@ Назначение этого параметра должна быть ясно понято. В большинстве случаев при получении времени этот параметр будет False. Но предположим, что мы должны составить время на полпути в течение скачка секунды, который произошоёл 30 июня 1985 и назначать его переменной Magic_Moment:

  1        Magic_Moment : Time := Time_Of (1985, 6, 30, 19, 59, 59, 0.5, True);

@ В некотором смысле были два момента 19:59:59 в тот день в Нью-Йорке. Надлежащий и затем после скачка секунды; параметр различает их. Таким образом, моментом одной секундой ранее является:

  1        Normal_Moment : Time := Time_Of (1985, 6, 30, 19, 59, 59, 0.5, False);

@ Мы могли бы руководствоатся правилами ISO и использовать 23:59:60 UTC и иметь подтип Second_Number с Natural range 0.. 60; но это произвело бы к несовместимости с Адой 95.

@ Отметим, что, если параметр Leap_Second - True а другие параметры не идентифицируют время скачка секунды тогда возбуждается исключение Time_Error.

@ Также имеются две соответствующие процедуры Split (номер 3 и 4) с выходным параметром Leap_Second. Первая производит семь компонентов, а другая четыре. Различие между процедурой Split с семью компонентами (номер 3) и более ранней Split (номер 2) состоит в том, что она имеет параметр Leap_Second. Написав:

  1        Split (Magic_Moment, 0, Y, M, D, H, M, S, SS, Leap);

@ результат в Leap будет True, тогда как:

  1        Split (Normal_Moment, 0, Y, M, D, H, M, S, SS, Leap);

@ результат в Leap будет False, но все Остальные параметры (Y..., SS) будут такими же.

@ С другой стороны вызывая версию Split (номер 2) без параметра Leap_Second таким образом

  1        Split (Magic_Moment , 0, Y, M, D, H, M, S, SS);
  2        Split (Normal_Moment, 0, Y, M, D, H, M, S, SS);

@ приводит точно к тем же самым результатам.

@ Читатель мог бы задаться вопросом, почему есть два Разбиения (Splits) времени (Time) с Leap_Second, но только однин без.

@ Дело в том, что у родительского пакета Calendar уже есть такая функция (хотя без параметра часового пояса). Другой момент состоит в том, что в случае Time_Of, мы можем иметь значение по умолчанию для параметра Leap, т.к. он передаётся в режиме in, но в случае Split параметр имеет режим out и не может быть опущен. Это была бы плохая практика поощрять использование фиктивного параметра, который игнорируется и следовательно должны быть дополнительные версии Split.

@ Наконец, есть две пары функций Image и Value. Первая пара работает со значениями типа Time. Вызов Image возвращает значение даты и времени в формате стандарта ISO 8601. Таким образом, беря Normal_Moment выше

  1        Image (Normal_Moment)

@ мы получаем следующую строку "1985-06-30 19:59:59" - в Нью-Йорке, Если мы устанавливаем опциональный параметр Include_Time_Fraction в True то

  1        Image (Normal_Moment, True)

@ получаем "1985-06-30 19:59:59.50". Имеется также обычный опциональный параметр Time_Zone, и таким образом, мы можем получить время в Париже (в программе работающей в Нью-Йорке) таким образом:

  1        Image (Normal_Moment, True, –360)

@ В результате получим "1985-07-01 02:59:59.50" -- в Париже. Соответственно функция Value работает в обратном направлении как ожидается.

@ Мы ожидали бы получать точно такие же результаты с Magic_Moment. Однако, так как некоторые реализации могут иметь в наличии ISO функцию в их операционной системе, которая также позволяет произвести "1985-06-30 19:59:60" -- в Нью-Йорке. Другая пара Image и Value работают над значениями типа Duration таким образом:

  1        Image (10_000.0) -- "02:46:40"

@ с опциональным параметром Include_Time_Fraction как и прежде. Снова соответствующая функция Value работает в обратном направлении.

@ ENG RUS

TOP BACK NEXT

2010-10-24 00:26:57

. .