File: FXSystemTime.cpp

package info (click to toggle)
gogglesmm 1.2.5-6
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 16,812 kB
  • sloc: cpp: 231,960; ansic: 893; xml: 222; makefile: 33
file content (505 lines) | stat: -rw-r--r-- 16,718 bytes parent folder | download | duplicates (2)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
/********************************************************************************
*                                                                               *
*                                T i m e   S t u f f                            *
*                                                                               *
*********************************************************************************
* Copyright (C) 2019,2022 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify          *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* This library is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with this program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#include "xincs.h"
#include "fxver.h"
#include "fxdefs.h"
#include "fxmath.h"
#include "fxascii.h"
#include "FXAtomic.h"
#include "FXElement.h"
#include "FXString.h"
#include "FXSystem.h"


/*
  Notes:
  - Handy functions for manipulating calendar time.
*/


using namespace FX;

/*******************************************************************************/

namespace FX {

// Time units in terms of nanoseconds
static const FXTime seconds=1000000000;
static const FXTime minutes=60*seconds;
static const FXTime hours=60*minutes;
static const FXTime days=24*hours;


// Default formatting string used for time formatting
const FXchar FXSystem::defaultTimeFormat[]="%m/%d/%Y %H:%M:%S";


// ISO 8601 time format (yyyy-mm-ddThh:mm:ss+hhmm) formatting string
const FXchar FXSystem::isoTimeFormat[]="%FT%T%z";


// Cumulative days of the year, for non-leap-years and leap-years
static const FXint days_of_year[2][13]={
  {0,31,59,90,120,151,181,212,243,273,304,334,365},
  {0,31,60,91,121,152,182,213,244,274,305,335,366}
  };


// Is year a leap-year
static FXint is_leap(FXint year){
  return !(year%4) && ((year%100) || !(year%400));
  }


// Returns day of week in civil calendar [0, 6] -> [Sun, Sat],
// from z is number of days since 1970-01-01.
static FXuint weekday_from_days(FXlong z){
  return (FXuint)(z>=-4 ? (z+4)%7 : (z+5)%7+6);
  }


// From year, month (1..12), day (1..31) return year-day (1..366).
static FXint yearday_from_date(FXint y,FXint m,FXint d){
  return days_of_year[is_leap(y)][m-1]+d;
  }

/*******************************************************************************/

// Returns number of days since civil 1970-01-01.  Negative values indicate
// days prior to 1970-01-01.
// y is year, m is month of year (1..12), d is day of month (1..31).
FXlong FXSystem::daysFromCivil(FXint y,FXint m,FXint d){
  y-=(m<=2);                                            // March
  FXlong era=(y>=0?y:y-399)/400;                        // Era
  FXuint yoe=(FXuint)(y-era*400);                       // [0, 399]
  FXuint doy=(153*(m+(m>2?-3:9))+2)/5+d-1;              // [0, 365]
  FXuint doe=yoe*365+yoe/4-yoe/100+doy;                 // [0, 146096]
  return era*146097+doe-719468;                         // Relative to epoch
  }


// Returns year/month/day in civil calendar.
// z is number of days since 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// y is year, m is month of year (1..12), d is day of month (1..31).
void FXSystem::civilFromDays(FXint& y,FXint& m,FXint& d,FXlong z){
  z+=719468;                                            // Relative to era
  FXlong era=(z>=0?z:z-146096)/146097;                  // Era
  FXuint doe=(FXuint)(z-era*146097);                    // [0, 146096]
  FXuint yoe=(doe-doe/1460+doe/36524-doe/146096)/365;   // [0, 399]
  FXuint doy=doe-(365*yoe+yoe/4-yoe/100);               // [0, 365]
  FXuint mp=(5*doy+2)/153;                              // [0, 11]
  y=(FXint)(era*400+yoe);
  d=doy-(153*mp+2)/5+1;                                 // [1, 31]
  m=mp+(mp<10?3:-9);                                    // [1, 12]
  y+=(m<=2);                                            // March
  }

/*******************************************************************************/

// Compute nanoseconds since Unix Epoch from struct tm
FXTime FXSystem::timeFromSystemTime(const Time& st){
  FXint year=st.year;
  FXint month=st.month;
  FXint day=st.mday;
  FXint hour=st.hour;
  FXint min=st.min;
  FXint sec=st.sec;
  FXint nano=st.nano;
  FXint leap;

  // Validate nanoseconds
  if(nano>seconds){
    sec+=nano/seconds;
    nano%=seconds;
    }

  // Validate seconds
  if(sec>=60){
    min+=sec/60;
    sec%=60;
    }

  // Validate minutes
  if(min>=60){
    hour+=min/60;
    min%=60;
    }

  // Validate days
  if(hour>=24){
    day+=hour/24;
    hour%=24;
    }

  // Validate month
  if(month>=13){
    year+=(month-1)/12;
    month=(month-1)%12+1;
    }

  // Is leap year
  leap=is_leap(year);

  // Validate day of month
  while(day>days_of_year[leap][month]){
    day-=days_of_year[leap][month];
    month+=1;
    if(13<=month){
      month=1;
      year+=1;
      leap=is_leap(year);
      }
    }

  // Return nanoseconds since Epoch
  return (((daysFromCivil(year,month,day)*24+hour)*60+min)*60+sec)*seconds+nano;
  }


// Return system time from utc in nanoseconds since Unix Epoch
void FXSystem::systemTimeFromTime(Time& st,FXTime utc){

  // Compute days from nanoseconds, rounding down
  FXlong zz=(0<=utc ? utc : utc-(days-1))/days;

  // Compute date from seconds
  civilFromDays(st.year,st.month,st.mday,zz);

  // Compute day of year
  st.yday=yearday_from_date(st.year,st.month,st.mday);

  // Compute day of week
  st.wday=weekday_from_days(zz);

  // Hours
  utc=utc-zz*days;
  st.hour=(FXint)(utc/hours);

  // Minutes
  utc=utc-st.hour*hours;
  st.min=(FXint)(utc/minutes);

  // Seconds
  utc=utc-st.min*minutes;
  st.sec=(FXint)(utc/seconds);

  // Nanoseconds
  utc=utc-st.sec*seconds;
  st.nano=(FXint)utc;

  // Offset utc
  st.offset=0;
  }

/*******************************************************************************/


// Time zone variables, set only once
static volatile FXuint local_zone_set=0;

#if defined(WIN32)
//
// The Windows TIME_ZONE_INFORMATION struct contains:
//
//   struct TIME_ZONE_INFORMATION {
//     LONG       Bias;                 // UTC = localtime + bias (minutes)
//     WCHAR      StandardName[32];     // Standard time name
//     SYSTEMTIME StandardDate;         // Time when daylight saving to standard time occurs
//     LONG       StandardBias;         // Value added to Bias during standard time (most zones = 0)
//     WCHAR      DaylightName[32];     // Daylight savings time name
//     SYSTEMTIME DaylightDate;         // Time when standard time to daylight savings time occurs
//     LONG       DaylightBias;         // Value added to Bias during daylight savings time (most zones = -60)
//     };
//
// While the Windows SYSTEMTIME struct contains:
//
//   struct SYSTEMTIME {
//     WORD wYear;                      // Year, 1601..
//     WORD wMonth;                     // Month, january..december (1..12)
//     WORD wDayOfWeek;                 // Day of week, sunday..monday (0..7)
//     WORD wDay;                       // Day of month, (1..31) [SEE NOTES BELOW]
//     WORD wHour;                      // Hour (0..23)
//     WORD wMinute;                    // Minutes (0..59)
//     WORD wSecond;                    // Seconds (0..59)
//     WORD wMilliseconds;              // Milliseconds (0..999)
//     };
//
static TIME_ZONE_INFORMATION tzi;
#endif


// Call tzset() only once
static void setuplocaltimezone(){
  if(atomicSet(&local_zone_set,1)==0){
#if defined(WIN32)
    GetTimeZoneInformation(&tzi);
#else
    tzset();
#endif
    }
  }


// Return offset between standard local time zone to UTC, in nanoseconds
FXTime FXSystem::localTimeZoneOffset(){
  setuplocaltimezone();
#if defined(WIN32)
  return minutes*tzi.Bias;              // +minutes*tzi.StandardBias;
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
  return 0;     // FIXME
#else
  return seconds*timezone;
#endif
  }


// Return offset daylight savings time to standard time, in nanoseconds
FXTime FXSystem::daylightSavingsOffset(){
  setuplocaltimezone();
#if defined(WIN32)
  return minutes*tzi.DaylightBias;      // Or difference between standard and daylight bias.
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
  return 0;     // FIXME
#else
  return -hours*daylight;
#endif
  }


// Return time zone name (or daylight savings time time zone name)
FXString FXSystem::localTimeZoneName(FXbool dst){
  setuplocaltimezone();
#if defined(WIN32)
  return FXString(dst?tzi.DaylightName:tzi.StandardName);
#else
  return FXString(tzname[dst]);
#endif
  }


#if defined(WIN32)

// Convert utc (ns) since 01/01/1970 to 100ns since 01/01/1601
//static inline FXTime fxwintime(FXTime utc){
//  const FXTime BIAS=FXLONG(116444736000000000);
//  const FXTime MULT=FXLONG(100);
//  return BIAS+utc/MULT;
//  }

// Convert utc (ns) since 01/01/1970 to 100ns since 01/01/1601
static inline FILETIME fxwintime(FXTime ut){
  const FXTime BIAS=FXLONG(116444736000000000);
  const FXTime MULT=FXLONG(100);
  FILETIME ft;
  ut=BIAS+ut/MULT;
  ft.dwLowDateTime=(DWORD)ut;
  ft.dwHighDateTime=(DWORD)(ut>>32);
  return ft;
  }


// Compare incoming date cur against changeover date chg; there are
// two ways to compare the numbers.
// When chg.wYear!=0, the change over occurs only once on the indicated
// absolute time.
// If chg.wYear==0, the switch between standard and daylight savings time
// occurs yearly, on the nth occurrence of wDayOfWeek, where n is given
// in the wDay variable of the TIME_ZONE_INFORMATION StandardDate or
// DaylightDate struct.
static FXint compare_zone_switch_over(const SYSTEMTIME& cur,const SYSTEMTIME& chg){
  static const FXint month_lengths[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  FXint cursecs,chgsecs,chgday,first,last;

  // Absolute date
  if(chg.wYear){
    if(cur.wYear==chg.wYear){
      if(cur.wMonth==chg.wMonth){
        if(cur.wDay==chg.wDay){
          cursecs=(cur.wHour*60+cur.wMinute)*60+cur.wSecond;
          chgsecs=(chg.wHour*60+chg.wMinute)*60+chg.wSecond;
          return cursecs-chgsecs;
          }
        return cur.wDay-chg.wDay;
        }
      return cur.wMonth-chg.wMonth;
      }
    return cur.wYear-chg.wYear;
    }

  // Relative date
  if(cur.wMonth==chg.wMonth){

    // Calculate the day of the first wDayOfWeek in the month
    first=(6+chg.wDayOfWeek-cur.wDayOfWeek+cur.wDay)%7+1;

    // Check needed for the 5th weekday of the month
    last=month_lengths[cur.wMonth-1]+(cur.wMonth==2 && is_leap(cur.wYear));

    // Switch at the nth occurrence (value in chg.wDay) of certain day of week
    chgday=first+7*(chg.wDay-1);
    if(chgday>last) chgday-=7;

    chgsecs=((chgday*24+chg.wHour)*60+chg.wMinute)*60+chg.wSecond;
    cursecs=((cur.wDay*24+cur.wHour)*60+cur.wMinute)*60+cur.wSecond;
    return cursecs-chgsecs;
    }
  return (FXint)cur.wMonth-(FXint)chg.wMonth;
  }


// Determine if daylight savings in effect
// The daylight savings time switch is tied to local time, and local
// time's year is tied to time zone.  However, time zone is a function
// of the local year [time zone info can change from year to year].
// We assume that the time zone is relatively stable, but we may
// call GetTimeZoneInformationForYear() in a future revision to
// obtain only DST/STD time switches, which are changed more rapidly.
static FXint daylightSavingsState(FXTime utc,FXbool local){

  // Expanded date/time
  SYSTEMTIME loc;

  // Assume local time
  FILETIME ftloc=fxwintime(utc);
  FILETIME ftdst=ftloc;
  FILETIME ftstd=ftloc;

  // If UTC, convert to local using time zone information
  if(!local){
    ftloc=fxwintime(utc-tzi.Bias*minutes);
    ftdst=fxwintime(utc-tzi.Bias*minutes-tzi.DaylightBias*minutes);
    ftstd=fxwintime(utc-tzi.Bias*minutes-tzi.StandardBias*minutes);
    }

  // Get expanded date/time
  FileTimeToSystemTime(&ftloc,&loc);

  // If wMonth is zero then no daylight savings in effect
  if(tzi.DaylightDate.wMonth && tzi.StandardDate.wMonth){

    // Expanded date/time
    SYSTEMTIME dst;
    SYSTEMTIME std;

    // Get expanded date/time
    FileTimeToSystemTime(&ftdst,&dst);
    FileTimeToSystemTime(&ftstd,&std);

    // Daylight savings time prior to switch to standard time
    FXbool before_std_date=(compare_zone_switch_over(dst,tzi.StandardDate)<0);

    // Standard time after switch to daylight savings time
    FXbool after_dst_date=(compare_zone_switch_over(std,tzi.DaylightDate)>=0);

    // Northern hemisphere
    if(tzi.DaylightDate.wMonth<tzi.StandardDate.wMonth){
      if(before_std_date && after_dst_date) return 1;
      }

    // Southern hemisphere
    else{
      if(before_std_date || after_dst_date) return 1;
      }
    }
  return 0;
  }

#endif


// Return 1 if daylight savings time is active at utc in nanoseconds since Unix Epoch
FXTime FXSystem::daylightSavingsActive(FXTime utc){
  setuplocaltimezone();
#if defined(WIN32)
  return daylightSavingsState(utc,false);
#elif defined(HAVE_LOCALTIME_R)
  struct tm tmresult;
  time_t tmp=(time_t)(utc/seconds);
  struct tm* ptm=localtime_r(&tmp,&tmresult);
  FXTRACE((300,"FXSystem::daylightSavingsActive(%lld) = %d\n",utc,ptm && ptm->tm_isdst!=0));
  return ptm && ptm->tm_isdst!=0;
#else
  time_t tmp=(time_t)(utc/seconds);
  struct tm* ptm=localtime(&tmp);
  FXTRACE((300,"FXSystem::daylightSavingsActive(%lld) = %d\n",utc,ptm && ptm->tm_isdst!=0));
  return ptm && ptm->tm_isdst!=0;
#endif
  }

/*******************************************************************************/

// Format utc in nanoseconds since Unix Epoch to date-time string using given format
FXString FXSystem::universalTime(FXTime utc,const FXchar *format){
  FXSystem::Time st={0,0,0,0,0,0,0,0,0,0};
  FXSystem::systemTimeFromTime(st,utc);
  return FXSystem::systemTimeFormat(st,format);
  }


// Parse utc date-time string to UTC nanoseconds since Unix Epoch using given format
// Assume string is time in UTC, unless a time zone offset was parsed; in that
// case, adjust the time according to the zone offset.
FXTime FXSystem::universalTime(const FXchar* string,const FXchar* format){
  FXSystem::Time st={0,0,0,0,0,0,0,0,0,0};
  if(FXSystem::systemTimeParse(st,string,format)){
    return FXSystem::timeFromSystemTime(st)+st.offset*seconds;
    }
  return forever;
  }


// Parse date-time string to UTC nanoseconds since Unix Epoch using given format
FXTime FXSystem::universalTime(const FXString& string,const FXchar* format){
  return FXSystem::universalTime(string.text(),format);
  }

/*******************************************************************************/

// Format utc in nanoseconds since Unix Epoch to local date-time string using given format
FXString FXSystem::localTime(FXTime utc,const FXchar *format){
  FXTime zoneoffset=FXSystem::localTimeZoneOffset()+FXSystem::daylightSavingsOffset()*FXSystem::daylightSavingsActive(utc);
  FXSystem::Time st={0,0,0,0,0,0,0,0,0,(FXint)(zoneoffset/seconds)};
  FXSystem::systemTimeFromTime(st,utc-zoneoffset);
  return FXSystem::systemTimeFormat(st,format);
  }


// Parse local date-time string to UTC nanoseconds since Unix Epoch using given format
// Assume string is time in local time, unless a time zone offset was parsed; in that
// case, adjust the time according to the zone offset.
FXTime FXSystem::localTime(const FXchar* string,const FXchar* format){
  FXSystem::Time st={0,0,0,0,0,0,0,0,0,0};
  if(FXSystem::systemTimeParse(st,string,format)){
    //FXTime zoneoffset=FXSystem::localTimeZoneOffset()+FXSystem::daylightSavingsOffset()*FXSystem::daylightSavingsActive(FXThread::time());
    return FXSystem::timeFromSystemTime(st)+st.offset*seconds;
    }
  return forever;
  }


// Parse local date-time string to UTC nanoseconds since Unix Epoch using given format
FXTime FXSystem::localTime(const FXString& string,const FXchar* format){
  return FXSystem::localTime(string.text(),format);
  }

}