comp.lang.ada
 help / color / mirror / Atom feed
* Dates and Times in GNAT on Linux
@ 2009-09-13 15:36 Marc A. Criley
  2009-09-13 16:17 ` Dmitry A. Kazakov
  2009-09-13 19:35 ` Jeffrey R. Carter
  0 siblings, 2 replies; 4+ messages in thread
From: Marc A. Criley @ 2009-09-13 15:36 UTC (permalink / raw)


I'm confused about GNAT's implementation of some aspects of date/time 
processing on Linux. I'm running GNAT GPL 2009 on Ubuntu Linux; I also 
live in the US Central Time Zone and Daylight Savings Time is in effect 
as I write this.

This is probably a stupid misunderstanding or mistaken expectations or 
feature misuse on my part, but I would really like to know what's going on.

Basically what I'm trying to do is take the time I got from from 
Ada.Real.Clock and convert it to a date/time rep.  The result I'm coming 
up with is always an hour off. (DST effect? Read on...) Code will follow 
in a bit.

I discovered that GNAT's implementation of Ada.Real_Time.Clock returns 
the number of seconds since the UTC epoch start, which is very nice and 
very handy.  Using Real_Time.Split I can get that number of seconds into 
a visible type, Seconds_Count.

I can then get the start of the Unix/UTC epoch via:
    UTC_Epoch_Start := Calendar.Time_Of(1970, 1, 1)
                         + Duration(Calendar.Time_Zones.UTC_Offset * 60);
    -- For where I live, right now the UTC_Offset is -300 minutes,
    -- i.e. -5 hours.

Adding the Real_Time seconds count (as a Duration) to UTC_Epoch_Start 
gives a Calendar.Time value containing the current UTC time, from which 
I can generate an image...which is an hour off from the correct value.

So I'm thinking it might be a DST problem, but shouldn't the affected 
Calendar procedures be taking that into account?

I saw that the Ada.Calendar package body uses a C function, 
__gnat_localtime_tzoff(), which has "Parameter 'off' captur[ing] the UTC 
offset which is either retrieved from the tm struct or calculated from 
the 'timezone' extern and the tm_isdst flag in the tm struct." Looking 
at the code for this function it appears that _for_Linux_ the 
implementation ignores the "tm_isdst" value and just uses the 
"tm_gmtoff" value, while for some other Unices it does look at tm_isdst.

I'd like to think there's a good reason for doing that--including me 
being an idiot--but I don't see what's going on. And unfortunately I 
don't yet have a support contract with AdaCore :-)

I hacked together some C and Ada examples to illustrate this:

tmt.c:

#include <time.h>
#include <stdio.h>


void tmt_c_time(time_t rt_secs)
{
   struct tm *local;
   time_t t;

   t = time(NULL);
   printf("secs %d\n", t);
   local = localtime(&t);
   printf("Local time and date: %s\n", asctime(local));
   local = gmtime(&t);
   printf("UTC time and date: %s\n", asctime(local));

   t =  rt_secs;
   printf("rt secs %d (from Ada)\n", t);
   local = localtime(&t);
   printf("Local time and date: %s\n", asctime(local));
   local = gmtime(&t);
   printf("UTC time and date: %s\n", asctime(local));
}

$ gcc -c -g tmt.c

tmtada.adb:

with Text_IO; use Text_IO;

with Ada.Real_Time;  use Ada.Real_Time;
with Ada.Calendar.Formatting;  use Ada.Calendar.Formatting;
with Ada.Calendar.Time_Zones;  use Ada.Calendar.Time_Zones;

procedure Tmtada is

    T : Time := Clock;

    Seconds : Seconds_Count;
    Span    : Time_Span;

    Cal_Time : Ada.Calendar.Time;

    UTC_Epoch : Ada.Calendar.Time;

    procedure Tmt_C_Time(Rt_Secs : Seconds_Count);
    pragma Import(C, Tmt_C_Time, "tmt_c_time");

    use Ada.Calendar;

begin
    Split(T, Seconds, Span);
    Put_Line("Seconds since epoch:" & Seconds'Img);

    UTC_Epoch := Ada.Calendar.Time_Of(1970, 1, 1) +
      Duration(UTC_Time_Offset * 60);
    Cal_Time := UTC_Epoch + Duration(Seconds);

    Put_Line("Time Image          " & Image(Cal_Time));

    New_Line;
    Put_Line("Via C...");
    Tmt_C_Time(Seconds);
end Tmtada;

$ gnatmake -g tmtada -largs tmt.o

Here's what I get from a run:

[95] Marc say: ./tmtada
Seconds since epoch: 1252855814
Time Image          2009-09-13 16:30:14

Via C...
secs 1252855814
Local time and date: Sun Sep 13 10:30:14 2009

UTC time and date: Sun Sep 13 15:30:14 2009

rt secs 1252855814 (from Ada)
Local time and date: Sun Sep 13 10:30:14 2009

UTC time and date: Sun Sep 13 15:30:14 2009

Maybe my conversion from Real_Time.Time to Calendar.Time is flawed...I 
don't know.  Any explanation or clarifications would be much appreciated.

Marc A. Criley
Mckae Technologies
www.mckae.com



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dates and Times in GNAT on Linux
  2009-09-13 15:36 Dates and Times in GNAT on Linux Marc A. Criley
@ 2009-09-13 16:17 ` Dmitry A. Kazakov
  2009-09-13 19:35 ` Jeffrey R. Carter
  1 sibling, 0 replies; 4+ messages in thread
From: Dmitry A. Kazakov @ 2009-09-13 16:17 UTC (permalink / raw)


On Sun, 13 Sep 2009 10:36:58 -0500, Marc A. Criley wrote:

> I'm confused about GNAT's implementation of some aspects of date/time 
> processing on Linux. I'm running GNAT GPL 2009 on Ubuntu Linux; I also 
> live in the US Central Time Zone and Daylight Savings Time is in effect 
> as I write this.
> 
> This is probably a stupid misunderstanding or mistaken expectations or 
> feature misuse on my part, but I would really like to know what's going on.
> 
> Basically what I'm trying to do is take the time I got from from 
> Ada.Real.Clock and convert it to a date/time rep.  The result I'm coming 
> up with is always an hour off. (DST effect? Read on...) Code will follow 
> in a bit.
> 
> I discovered that GNAT's implementation of Ada.Real_Time.Clock returns 
> the number of seconds since the UTC epoch start, which is very nice and 
> very handy.  Using Real_Time.Split I can get that number of seconds into 
> a visible type, Seconds_Count.
> 
> I can then get the start of the Unix/UTC epoch via:
>     UTC_Epoch_Start := Calendar.Time_Of(1970, 1, 1)
>                          + Duration(Calendar.Time_Zones.UTC_Offset * 60);
>     -- For where I live, right now the UTC_Offset is -300 minutes,
>     -- i.e. -5 hours.
> 
> Adding the Real_Time seconds count (as a Duration) to UTC_Epoch_Start 
> gives a Calendar.Time value containing the current UTC time, from which 
> I can generate an image...which is an hour off from the correct value.
> 
> So I'm thinking it might be a DST problem, but shouldn't the affected 
> Calendar procedures be taking that into account?

Hmm, it seems otherwise. It is DST now, and no DST for the time point you
calculated the epoch. So the epoch time is incoherent to the time now. Your
calculation presumes that (T + D1) + D2 = T + (D1 + D2) holds for any T, a
political time and any D1, D2, durations. That is wrong for political time.
(Ada.Calendar arithmetic is broken, when Ada.Calendar.Time is political
time) The problem is that UTC_Offset is not a constant, but itself is a
function of UTC time.

> Maybe my conversion from Real_Time.Time to Calendar.Time is flawed...I 
> don't know.  Any explanation or clarifications would be much appreciated.

You have to sum all clock skews (forth and back) within the interval
between the epoch and the time being converted. These skews will sum up to
either 0 or 1 hour, or maybe 1 hour + possibly n leap seconds. Not an easy
task.

P.S. I wished Ada.Calendar.Time_Zones weren't broken. This was discussed in
c.l.a. a couple of months ago.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dates and Times in GNAT on Linux
  2009-09-13 15:36 Dates and Times in GNAT on Linux Marc A. Criley
  2009-09-13 16:17 ` Dmitry A. Kazakov
@ 2009-09-13 19:35 ` Jeffrey R. Carter
  2009-09-13 20:41   ` Marc A. Criley
  1 sibling, 1 reply; 4+ messages in thread
From: Jeffrey R. Carter @ 2009-09-13 19:35 UTC (permalink / raw)


Marc A. Criley wrote:
> 
> I can then get the start of the Unix/UTC epoch via:
>    UTC_Epoch_Start := Calendar.Time_Of(1970, 1, 1)
>                         + Duration(Calendar.Time_Zones.UTC_Offset * 60);
>    -- For where I live, right now the UTC_Offset is -300 minutes,
>    -- i.e. -5 hours.

This is your problem. The offset on 1970 Jan 01 00:00:00.00 was -6 hrs.

Of course, any attempt to relate an Ada.Real_Time.Time to an Ada.Calendar.Time 
is completely compiler dependent, is unlikely to work with any other compiler, 
and could stop working with a different version of the same compiler.

-- 
Jeff Carter
"C++: The power, elegance and simplicity of a hand grenade."
Ole-Hjalmar Kristensen
90



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dates and Times in GNAT on Linux
  2009-09-13 19:35 ` Jeffrey R. Carter
@ 2009-09-13 20:41   ` Marc A. Criley
  0 siblings, 0 replies; 4+ messages in thread
From: Marc A. Criley @ 2009-09-13 20:41 UTC (permalink / raw)


Jeffrey R. Carter wrote:
> Marc A. Criley wrote:
>>
>> I can then get the start of the Unix/UTC epoch via:
>>    UTC_Epoch_Start := Calendar.Time_Of(1970, 1, 1)
>>                         + Duration(Calendar.Time_Zones.UTC_Offset * 60);
>>    -- For where I live, right now the UTC_Offset is -300 minutes,
>>    -- i.e. -5 hours.
> 
> This is your problem. The offset on 1970 Jan 01 00:00:00.00 was -6 hrs.

Yep. I'm retrieving the UTC_Offset in effect today (which is the default 
value of the UTC_Offset argument), not what it was at that date/time.

Thanks! I knew I was missing something basic here.

> Of course, any attempt to relate an Ada.Real_Time.Time to an 
> Ada.Calendar.Time is completely compiler dependent, is unlikely to work 
> with any other compiler, and could stop working with a different version 
> of the same compiler.

Yep. But that's an issue I can deal with here.

Marc



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-09-13 20:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-13 15:36 Dates and Times in GNAT on Linux Marc A. Criley
2009-09-13 16:17 ` Dmitry A. Kazakov
2009-09-13 19:35 ` Jeffrey R. Carter
2009-09-13 20:41   ` Marc A. Criley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox