7

I have a time value represented in SYSTEMTIME, i want to add/subtract 1 hour from it and get the newly obtained SYSTEMTIME. I want the conversion should take care of the date change on addition/subtraction or month change or e1 year change .

Can someone help me with this if there is some windows api which does arithmetic on SYSTEMTIME

tshepang
  • 12,111
  • 21
  • 91
  • 136
Peter
  • 2,719
  • 4
  • 25
  • 55

3 Answers3

13

If you're using C# (or VB.NET, or ASP.NET) you can use

DateTime dt = DateTime.Now.AddHours(1);

You can use negative numbers to subtract:

DateTime dt = DateTime.Now.AddHours(-1);

EDITED: I extract an asnwer from this post

They suggest converting SYSTEMTIME to FILETIME, which is a number of ticks since an epoch. You can then add the required number of 'ticks' (i.e. 100ns intervals) to indicate your time, and convert back to SYSTEMTIME.

The ULARGE_INTEGER struct is a union with a QuadPart member, which is a 64bit number, that can be directly added to (on recent hardware).

SYSTEMTIME add( SYSTEMTIME s, double seconds ) {

    FILETIME f;
    SystemTimeToFileTime( &s, &f );

    ULARGE_INTEGER u  ; 
    memcpy( &u  , &f , sizeof( u ) );

    const double c_dSecondsPer100nsInterval = 100. * 1.E-9;
    u.QuadPart += seconds / c_dSecondsPer100nsInterval; 

    memcpy( &f, &u, sizeof( f ) );

    FileTimeToSystemTime( &f, &s );
    return s;
 }

If you want to add an hour use SYSTEMTIME s2 = add(s1, 60*60)

Community
  • 1
  • 1
Marco
  • 56,740
  • 14
  • 129
  • 152
7

To add signed seconds (forward or backward in time) in C++:

const double clfSecondsPer100ns = 100. * 1.E-9;
void iAddSecondsToSystemTime(SYSTEMTIME* timeIn, SYSTEMTIME* timeOut, double tfSeconds)
{
    union {
        ULARGE_INTEGER li;
        FILETIME       ft;
    };

    // Convert timeIn to filetime
    SystemTimeToFileTime(timeIn, &ft);

    // Add in the seconds
    li.QuadPart += tfSeconds / clfSecondsPer100ns;

    // Convert back to systemtime
    FileTimeToSystemTime(&ft, timeOut);
}
Rick C. Hodgin
  • 483
  • 5
  • 11
  • What is this trick with the `union`? You make no calculus on `ft`, but it is you result back... I don't understand. – Sandburg Apr 04 '18 at 07:56
  • 1
    The byte/bit patterns of the 64-bit FILETIME is the same as those of the ULARGE_INTEGER. By populating ft, you are also populating the portions of li, which allows you to use li at that point. The location in memory just examines the data there in the two different ways. By using ft you see it as a FILETIME. By using li you see it as a ULARGE_INTEGER. – Rick C. Hodgin Apr 25 '18 at 13:33
  • This (interpreting FILETIME as ULARGE_INTEGER) is exactly what is discouraged by MSDN, though. – CookiePLMonster Jun 03 '18 at 18:34
  • They discourage it because they don't intend to stay on x86 forever. They want you to use functions that can be ported to other architectures without any changes. That's all find and dandy if you have that need (other architecture portability). But if you're strictly x86 (or more generically, little endian), this method works properly. – Rick C. Hodgin Jun 04 '18 at 19:07
  • 1
    The MSDN warning is related to unaligned access of a `FILETIME`. However, C++ unions are aligned to the strictest alignment necessary. Hence, this solution prevents the problem MSDN warns about. – MSalters Jul 11 '18 at 09:41
  • MSaltes, the compiler will automatically align variables unless explicitly told to pack things on a 1- or 2-byte boundary. Local variables like this on the stack are always (at a minimum) DWORD-aligned in 32-bit code, and QWORD-aligned in 64-bit code. In 32-bit code it will be two 32-bit accesses, which will be aligned. In 64-bit code it will be one 64-bit access, which will be aligned. – Rick C. Hodgin Apr 28 '23 at 13:58
5
#include <stdio.h>
#include <windows.h>
#define NSEC 60*60

main()
{
SYSTEMTIME st;
FILETIME ft;

// Get local time from system
GetLocalTime(&st);

printf("%02d/%02d/%04d %02d:%02d:%02d\n",
  st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute,st.wSecond);

// Convert to filetime
SystemTimeToFileTime(&st,&ft);

// Add NSEC seconds
((ULARGE_INTEGER *)&ft)->QuadPart +=(NSEC*10000000LLU);

// Convert back to systemtime
FileTimeToSystemTime(&ft,&st);

printf("%02d/%02d/%04d %02d:%02d:%02d\n",
  st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute,st.wSecond);
}
puccipucci
  • 59
  • 1
  • 1
  • I'm using VS2008 and getting "bad suffix error on the line from where you're adding seconds. – Nikunj Chaklasiya Aug 16 '19 at 07:47
  • https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime Do not cast a pointer to a FILETIME structure to either a ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows. – Piotr May 06 '21 at 20:58
  • Piortr, in 64-bit windows, in a 64-bit app, the ft local variable will be on the stack, and it will be QWORD-aligned (8-byte aligned). It won't ever be misaligned. In a 32-bit app, it will be DWORD-aligned minimally, and accesses to the data will be in two 32-bit reads / writes, so it also won't be misaligned. The 64-bit pointer cast warning is in general. It has cases like this (and many others) where it's not an issue. You have to know your data. – Rick C. Hodgin Apr 28 '23 at 13:55