[C/ Delphi/ WinAPI] Interpretatie van time_t

Pagina: 1
Acties:

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
In de IP Helper API komt de GetAdaptersInfo functie voor, die een IP_ADAPTER_INFO structuur vult. Die ziet er als volgt uit:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct _IP_ADAPTER_INFO {
    struct _IP_ADAPTER_INFO* Next;
    DWORD ComboIndex;
    char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
    char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
    UINT AddressLength;
    BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
    DWORD Index;
    UINT Type;
    UINT DhcpEnabled;
    PIP_ADDR_STRING CurrentIpAddress;
    IP_ADDR_STRING IpAddressList;
    IP_ADDR_STRING GatewayList;
    IP_ADDR_STRING DhcpServer;
    BOOL HaveWins;
    IP_ADDR_STRING PrimaryWinsServer;
    IP_ADDR_STRING SecondaryWinsServer;
    time_t LeaseObtained;
    time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;

Ik kan hiervan in Delphi probleemloos alle velden interpreteren, behalve LeaseObtained en LeaseExpires. Inmiddels ben ik erachter dat het time_t type een 32-bits integer is. Maar hoe interpreteer ik dat ding? :? De documentatie van Microsoft zegt er alleen het volgende over:
LeaseObtained
Time when the current DHCP lease was obtained.
LeaseExpires
Time when the current DHCP lease expires.
Ik gebruik GetAdaptersInfo in Delphi en wil dan ook het time_t formaat kunnen lezen in Delphi. Een simpele conversie naar TDateTime geeft een foutmelding: 'Invalid argument to time encode'. Blijkbaar werkt het volgende dus niet:
Delphi:
1
2
3
4
function MakeDateTime(T: time_t): TDateTime;
begin
  Result := FileDateToDateTime(T);
end;

Via ipconfig /all op de command prompt zie ik het volgende:
Afbeeldingslocatie: http://img180.imageshack.us/img180/4084/cmd9rs.gif

Ik weet inmiddels via wat debugwerk dat de volgende waarden moeten overeenkomen met wat ipconfig meldt over de DHCP leases:
LeaseObtained = 1127077508 dec = 432DD684 hex
LeaseExpires = 1127084708 dec = 432DF2A4 hex

Het verschil tussen deze waarden is 7200, terwijl ipconfig aangeeft dat dit overeenkomt met 2 uur. Anders gesteld: een verandering van 1 in een time_t variable komt overeen met 1 seconde. Maar hoe kom ik nu naar een juiste interpretatie van C-style time_t, bijvoorbeeld als een Delphi-style TDateTime?

Kan iemand me op weg helpen?

Een goede grap mag vrienden kosten.


  • Icelus
  • Registratie: Januari 2004
  • Niet online

Developer Accused Of Unreadable Code Refuses To Comment


  • klinz
  • Registratie: Maart 2002
  • Laatst online: 07-03 16:48

klinz

weet van NIETS

De waarden worden in UNIX-stijl genoteerd en bevatten dus het aantal seconden sinds 1-1-1970. Omrekenen naar een Delphi datum is vrij eenvoudig.

DateTime = (UnixDate / (24 * 60 * 60)) + EncodeDate(1970, 1, 1)

[ Voor 4% gewijzigd door klinz op 19-09-2005 00:10 ]


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
De eerste link van Icelus is al genoeg. Het enige addertje onder het gras is dat je bij een TFileTime rekening moet houden met een conversie van die de tijdzone in acht neemt. Dit werkt probleemloos:
Delphi:
1
2
3
4
5
6
7
8
9
10
function MakeDateTime(T: time_t): TDateTime;
var
  FileTime, LocalFileTime: TFileTime;
  SysTime: TSystemTime;
begin
  FileTime := TFileTime(Int64(T) * 10000000 + 116444736000000000);
  FileTimeToLocalFileTime(FileTime, LocalFileTime);
  FileTimeToSystemTime(LocalFileTime, SysTime);
  Result := SystemTimeToDateTime(SysTime);
end;
Via de manier van Klinz wordt het nog eenvoudiger:
Delphi:
1
2
3
4
function MakeDateTime(T: time_t): TDateTime;
begin
  Result := T / SecsPerDay + EncodeDate(1970, 1, 1);
end;
Allebei bedankt _/-\o_

Een goede grap mag vrienden kosten.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
Maar meteen het laatste probleemgeval erachteraan: GetIfEntry verkrijgt gegevens over een netwerkinterface en retourneert deze gegevens in een MIB_IFROW struct:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef struct _MIB_IFROW
{
    WCHAR   wszName[MAX_INTERFACE_NAME_LEN];
    DWORD    dwIndex;
    DWORD    dwType;
    DWORD    dwMtu;
    DWORD    dwSpeed;
    DWORD    dwPhysAddrLen;
    BYTE    bPhysAddr[MAXLEN_PHYSADDR];
    DWORD    dwAdminStatus;
    DWORD    dwOperStatus;
    DWORD    dwLastChange;
    DWORD    dwInOctets;
    DWORD    dwInUcastPkts;
    DWORD    dwInNUcastPkts;
    DWORD    dwInDiscards;
    DWORD    dwInErrors;
    DWORD    dwInUnknownProtos;
    DWORD    dwOutOctets;
    DWORD    dwOutUcastPkts;
    DWORD    dwOutNUcastPkts;
    DWORD    dwOutDiscards;
    DWORD    dwOutErrors;
    DWORD    dwOutQLen;
    DWORD    dwDescrLen;
    BYTE    bDescr[MAXLEN_IFDESCR];
} MIB_IFROW,*PMIB_IFROW;
dwLastChange
Specifies the last time the operational status changed
dwLastChange van het type DWORD is zeker geen UNIX-style tijd, want dan kom ik bij een testje uit op een datum ergens in februari 2019. Het is ook geen FILEDATE, want bij conversie naar TDateTime krijg ik een foutmelding dat het argument ongeldig is. Voor mijn netwerkkaart krijg ik een DWORD waarde terug van 1550173516 decimaal. Zelfde vraag: hoe interpreteer ik deze waarde?

Een goede grap mag vrienden kosten.


  • klinz
  • Registratie: Maart 2002
  • Laatst online: 07-03 16:48

klinz

weet van NIETS

Als het je helpt, mijn MSDN zegt dit:

dwLastChange
Specifies the length of time, in centaseconds (10^-2 sec), that elapsed between January 1, 1601, and the last change of the operational status of the interface (connection). The value rolls over after 2^32 centaseconds.


Prettig, die uniformiteit :-)

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
Wie verzint nou zoiets :?. Op dit moment kom ik er even niet uit, morgen ga ik er weer eens fris voor zitten.

Een goede grap mag vrienden kosten.


  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

tomatoman schreef op maandag 19 september 2005 @ 02:35:
Wie verzint nou zoiets :?. Op dit moment kom ik er even niet uit, morgen ga ik er weer eens fris voor zitten.
Bestaat al sinds de middeleeuwen en is een verdomd handige manier om een tot op de seconde accurate, tijdzone-onafhankelijke datum/tijd in een 32-bit int te krijgen. (D'r zijn ook accuratere versies, maar dat past niet meer in 32 bits).

Dat jij er nooit van gehoord had betekent dat je niet genoeg met UNIX bezig geweest bent ;)

[ Voor 7% gewijzigd door CyBeR op 19-09-2005 02:41 ]

All my posts are provided as-is. They come with NO WARRANTY at all.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
CyBeR schreef op maandag 19 september 2005 @ 02:41:
Bestaat al sinds de middeleeuwen en is een verdomd handige manier om een tot op de seconde accurate, tijdzone-onafhankelijke datum/tijd in een 32-bit int te krijgen. (D'r zijn ook accuratere versies, maar dat past niet meer in 32 bits).
Lekker handig dit systeem, vooral nu een uptime van meer dan een jaar niet ongewoon meer is. Dit getal wordt na iets meer dan een jaar gereset naar nul. Dan lijkt het alsof dwLastChange gisteren plaatsvond, maar eigenlijk was het vorig jaar 8)7

Nou vooruit, hier is de oplossing.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function LastChangeAsDateTime(dwLastChange: Cardinal): TDateTime;
const
  CSecsPerDay = 100 * SecsPerDay;
var
  BaseDate, LastCycleStart: TDateTime;
  CyclesSinceBaseDate: Int64;
begin
  { dwLastChange specifies the length of time, in centaseconds (10^-2 sec),
    that elapsed between January 1, 1601, and the last change of the
    operational status of the interface (connection). The value rolls over
    after 2^32 centaseconds. }
  BaseDate := EncodeDate(1601, 1, 1);
  CyclesSinceBaseDate := Trunc((Now - BaseDate) * CSecsPerDay / $FFFFFFFF);
  LastCycleStart := BaseDate + CyclesSinceBaseDate * $FFFFFFFF / CSecsPerDay;
  Result := LastCycleStart + dwLastChange / CSecsPerDay;
end;

Een goede grap mag vrienden kosten.


  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

tomatoman schreef op maandag 19 september 2005 @ 03:20:
[...]
Lekker handig dit systeem, vooral nu een uptime van meer dan een jaar niet ongewoon meer is. Dit getal wordt na iets meer dan een jaar gereset naar nul. Dan lijkt het alsof dwLastChange gisteren plaatsvond, maar eigenlijk was het vorig jaar 8)7
:?

Met een accuraatheid van een seconde kun je hier (uitgaande van een unsigned int) 60 jaar in proppen... Uiteraard wordt dat minder naarmate je er meer in wilt proppen (msecs, etc.)

[ Voor 8% gewijzigd door CyBeR op 19-09-2005 09:11 ]

All my posts are provided as-is. They come with NO WARRANTY at all.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 28-04 18:15

Tomatoman

Fulltime prutser

Topicstarter
Er wordt gewerkt met een eenheid van 1/100ste seconde, zodat je in een unsigned integer maximaal 493 dagen kunt stoppen. Als dwLastChange de datum van gisteren (18 september 2005) aangeeft, zou het daarom evengoed 494 dagen geleden kunnen zijn (dat is 13 mei 2004). Het zou zelfs 5 januari 2003 kunnen zijn. Daarom vind ik de gekozen eenheid van 1/100ste seconde opgeslagen in een 32-bits getal maar raar, ze hadden veel beter een kleinere resolutie kunnen kiezen of de waarde moeten opslaan in een 64-bits int.

Hoe dan ook, de datum is ontcijferd. :)

Een goede grap mag vrienden kosten.

Pagina: 1