Related
mktime returns for july 3rd 1941 (00:00:00) and july 4th 1941 (00:00:00) are unexpected.
The difference between the two is 82800 seconds, lacking a full hour (3600).
The C program diff1941.c (see below) shows the following :
$> diff1941
july3=-899337600i
diff:82800 should be 86400
At first I thought it was a TZ Database hour shift, but as far as I understand, and according to zdump command, there is no such shift for 1941.
zdump -v -c 1940,1943 /etc/localtime
/etc/localtime Sun Feb 25 01:59:59 1940 UT = Sun Feb 25 01:59:59 1940 WET isdst=0 gmtoff=0
/etc/localtime Sun Feb 25 02:00:00 1940 UT = Sun Feb 25 03:00:00 1940 WEST isdst=1 gmtoff=3600
/etc/localtime Fri Jun 14 21:59:59 1940 UT = Fri Jun 14 22:59:59 1940 WEST isdst=1 gmtoff=3600
/etc/localtime Fri Jun 14 22:00:00 1940 UT = Sat Jun 15 00:00:00 1940 CEST isdst=1 gmtoff=7200
/etc/localtime Mon Nov 2 00:59:59 1942 UT = Mon Nov 2 02:59:59 1942 CEST isdst=1 gmtoff=7200
/etc/localtime Mon Nov 2 01:00:00 1942 UT = Mon Nov 2 02:00:00 1942 CET isdst=0 gmtoff=3600
So at this point I am confused. Either my program has a bug that I can not see (possible), or there is a bug in lib C mktime function(unlikely), or there is something subtle in the TZ database and I can not find it (probable): what do you think of it ?
I am using:
Ubuntu 20.04 64 bits,
libc 2.31-0ubuntu9,
tzdata 2019c-3ubuntu1
/etc/localtime points on /usr/share/zoneinfo/Europe/Paris
diff1941.c:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
long int stamp(int d,int m,int y)
{
struct tm date;
memset(&date,0,sizeof(date));
date.tm_mday=d;
date.tm_mon=m-1;
date.tm_year=y-1900;
return mktime(&date);
}
int main(int argc, char **argv)
{
if (argc>1)
setenv("TZ","0",1);
long int july3=stamp(3,7,1941);
long int july4=stamp(4,7,1941);
printf("july3=%ldi\n",july3);
printf("diff:%ld should be 86400\n",july4-july3);
}
Usually, when you see 1 hour difference in time, that means you should take a look at daylight saving time settings.
On both dates 3rd July 1941 and 4th July 1941 daylight saving time was in effect in France. You specify tm_isdst = 0, that means the daylight saving time is not in effect for your dates. So your dates are invalid - there was no such time as 3rd July 1941 with 00:00:00 with no DST.
Glibc mktime tries it's best to determine what time do you have in mind. Actually it determines the first of the dates as 3rd July 1941 02:00:00 and the second of the dates as 4th July 1941 01:00:00. The difference is one day minus one hour.
Either set isdst=-1 to let mktime "automagically" determine the current DST for your input times. In France it was DST all the time in 1941, it will determine isdst=1. Or explicit specify that you want DST by setting date.isdst=1.
When using mktime(), the .tm_isdst member, when 0 or 1, results in a struct tm --> time_t difference of one 1 hour or the day light savings time shift for many zones such as America/ Los_Angeles.
This is as expected and consistent with most time-zones. For 2 struct tm that differ only in .tm_isdst = 0 or 1, the result of mktime() differ by the daylight shift.
With a few zones, including ones which employ daylight savings times, (e. g. America/Dawson_Creek) the mktime() result seems to ignore .tm_isdst for most struct tm times unless it is that one hour of that 25-hour day when going off daylight savings time.
1) Why do some timezones ignore .tm_isdst? (Even though they use DST) Perhaps I have missed something.
My opinion is that mktime() is broke for some minor zones that derive data from the major zones.
2) If indeed a gcc mktime() bug, what are the next steps?
Note: No issues seen when .tm_idst < 0.
System call mktime ignores tm_isdst flag does not apply here as the struct tm is refreshed before each mktime() call.
Output (Scroll down and notice the * * * lines)
time_t (and delta from previous) via various .isdst settings.
Unchanged after localtime(), -1 , 0 , 1
America/Los_Angeles
No trouble here, each delta goes up by 3600 or the one-time 0 for .isdst=0, .isdst=1
gmt:Sun Oct 25 05:30:00 1970 +0000 GMT 0
local:Sat Oct 24 22:30:00 1970 -0700 PDT 1
time_t:25680600,25680600 , 25680600 , 25684200 , 25680600 ,
gmt:Sun Oct 25 06:30:00 1970 +0000 GMT 0
local:Sat Oct 24 23:30:00 1970 -0700 PDT 1
time_t:25684200,25684200 3600, 25684200 3600, 25687800 3600, 25684200 3600,
gmt:Sun Oct 25 07:30:00 1970 +0000 GMT 0
local:Sun Oct 25 00:30:00 1970 -0700 PDT 1
time_t:25687800,25687800 3600, 25687800 3600, 25691400 3600, 25687800 3600,
gmt:Sun Oct 25 08:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0700 PDT 1
time_t:25691400,25691400 3600, 25695000 7200, 25695000 3600, 25691400 3600,
gmt:Sun Oct 25 09:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0800 PST 0
time_t:25695000,25695000 3600, 25695000 0, 25695000 0, 25691400 0,
gmt:Sun Oct 25 10:30:00 1970 +0000 GMT 0
local:Sun Oct 25 02:30:00 1970 -0800 PST 0
time_t:25698600,25698600 3600, 25698600 3600, 25698600 3600, 25695000 3600,
gmt:Sun Oct 25 11:30:00 1970 +0000 GMT 0
local:Sun Oct 25 03:30:00 1970 -0800 PST 0
time_t:25702200,25702200 3600, 25702200 3600, 25702200 3600, 25698600 3600,
Africa/Juba
Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour. else time_t same without regard to `.isdst.
gmt:Wed Oct 14 17:30:00 1970 +0000 GMT 0
local:Wed Oct 14 20:30:00 1970 +0300 CAST 1
time_t:24773400,24773400 , 24773400 , 24773400 , 24773400 ,
gmt:Wed Oct 14 18:30:00 1970 +0000 GMT 0
local:Wed Oct 14 21:30:00 1970 +0300 CAST 1
time_t:24777000,24777000 3600, 24777000 3600, 24777000 3600, 24777000 3600,
gmt:Wed Oct 14 19:30:00 1970 +0000 GMT 0
local:Wed Oct 14 22:30:00 1970 +0300 CAST 1
time_t:24780600,24780600 3600, 24780600 3600, 24780600 3600, 24780600 3600,
gmt:Wed Oct 14 20:30:00 1970 +0000 GMT 0
local:Wed Oct 14 23:30:00 1970 +0300 CAST 1
time_t:24784200,24784200 3600, 24784200 3600, 24787800 7200, 24784200 3600, ***
gmt:Wed Oct 14 21:30:00 1970 +0000 GMT 0
local:Wed Oct 14 23:30:00 1970 +0200 CAT 0
time_t:24787800,24787800 3600, 24784200 0, 24787800 0, 24784200 0,
gmt:Wed Oct 14 22:30:00 1970 +0000 GMT 0
local:Thu Oct 15 00:30:00 1970 +0200 CAT 0
time_t:24791400,24791400 3600, 24791400 7200, 24791400 3600, 24791400 7200, ***
gmt:Wed Oct 14 23:30:00 1970 +0000 GMT 0
local:Thu Oct 15 01:30:00 1970 +0200 CAT 0
time_t:24795000,24795000 3600, 24795000 3600, 24795000 3600, 24795000 3600,
America/Dawson_Creek
Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour. else time_t same without regard to `.isdst.
gmt:Sun Oct 25 05:30:00 1970 +0000 GMT 0
local:Sat Oct 24 22:30:00 1970 -0700 PDT 1
time_t:25680600,25680600 , 25680600 , 25680600 , 25680600 ,
gmt:Sun Oct 25 06:30:00 1970 +0000 GMT 0
local:Sat Oct 24 23:30:00 1970 -0700 PDT 1
time_t:25684200,25684200 3600, 25684200 3600, 25684200 3600, 25684200 3600,
gmt:Sun Oct 25 07:30:00 1970 +0000 GMT 0
local:Sun Oct 25 00:30:00 1970 -0700 PDT 1
time_t:25687800,25687800 3600, 25687800 3600, 25687800 3600, 25687800 3600,
gmt:Sun Oct 25 08:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0700 PDT 1
time_t:25691400,25691400 3600, 25695000 7200, 25695000 7200, 25691400 3600, ***
gmt:Sun Oct 25 09:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0800 PST 0
time_t:25695000,25695000 3600, 25695000 0, 25695000 0, 25691400 0,
gmt:Sun Oct 25 10:30:00 1970 +0000 GMT 0
local:Sun Oct 25 02:30:00 1970 -0800 PST 0
time_t:25698600,25698600 3600, 25698600 3600, 25698600 3600, 25698600 7200, ***
gmt:Sun Oct 25 11:30:00 1970 +0000 GMT 0
local:Sun Oct 25 03:30:00 1970 -0800 PST 0
time_t:25702200,25702200 3600, 25702200 3600, 25702200 3600, 25702200 3600,
America/Fort_Nelson
Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour. else time_t same without regard to `.isdst.
gmt:Sun Oct 25 05:30:00 1970 +0000 GMT 0
local:Sat Oct 24 22:30:00 1970 -0700 PDT 1
time_t:25680600,25680600 , 25680600 , 25680600 , 25680600 ,
gmt:Sun Oct 25 06:30:00 1970 +0000 GMT 0
local:Sat Oct 24 23:30:00 1970 -0700 PDT 1
time_t:25684200,25684200 3600, 25684200 3600, 25684200 3600, 25684200 3600,
gmt:Sun Oct 25 07:30:00 1970 +0000 GMT 0
local:Sun Oct 25 00:30:00 1970 -0700 PDT 1
time_t:25687800,25687800 3600, 25687800 3600, 25687800 3600, 25687800 3600,
gmt:Sun Oct 25 08:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0700 PDT 1
time_t:25691400,25691400 3600, 25695000 7200, 25695000 7200, 25691400 3600, ***
gmt:Sun Oct 25 09:30:00 1970 +0000 GMT 0
local:Sun Oct 25 01:30:00 1970 -0800 PST 0
time_t:25695000,25695000 3600, 25695000 0, 25695000 0, 25691400 0,
gmt:Sun Oct 25 10:30:00 1970 +0000 GMT 0
local:Sun Oct 25 02:30:00 1970 -0800 PST 0
time_t:25698600,25698600 3600, 25698600 3600, 25698600 3600, 25698600 7200, ***
gmt:Sun Oct 25 11:30:00 1970 +0000 GMT 0
local:Sun Oct 25 03:30:00 1970 -0800 PST 0
time_t:25702200,25702200 3600, 25702200 3600, 25702200 3600, 25702200 3600,
America/Punta_Arenas
Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour. else time_t same without regard to `.isdst.
gmt:Sat Mar 28 23:30:00 1970 +0000 GMT 0
local:Sat Mar 28 20:30:00 1970 -0300 -03 1
time_t: 7515000, 7515000 , 7515000 , 7515000 , 7515000 ,
gmt:Sun Mar 29 00:30:00 1970 +0000 GMT 0
local:Sat Mar 28 21:30:00 1970 -0300 -03 1
time_t: 7518600, 7518600 3600, 7518600 3600, 7518600 3600, 7518600 3600,
gmt:Sun Mar 29 01:30:00 1970 +0000 GMT 0
local:Sat Mar 28 22:30:00 1970 -0300 -03 1
time_t: 7522200, 7522200 3600, 7522200 3600, 7522200 3600, 7522200 3600,
gmt:Sun Mar 29 02:30:00 1970 +0000 GMT 0
local:Sat Mar 28 23:30:00 1970 -0300 -03 1
time_t: 7525800, 7525800 3600, 7529400 7200, 7529400 7200, 7525800 3600, ***
gmt:Sun Mar 29 03:30:00 1970 +0000 GMT 0
local:Sat Mar 28 23:30:00 1970 -0400 -04 0
time_t: 7529400, 7529400 3600, 7529400 0, 7529400 0, 7525800 0,
gmt:Sun Mar 29 04:30:00 1970 +0000 GMT 0
local:Sun Mar 29 00:30:00 1970 -0400 -04 0
time_t: 7533000, 7533000 3600, 7533000 3600, 7533000 3600, 7533000 7200, ***
gmt:Sun Mar 29 05:30:00 1970 +0000 GMT 0
local:Sun Mar 29 01:30:00 1970 -0400 -04 0
time_t: 7536600, 7536600 3600, 7536600 3600, 7536600 3600, 7536600 3600,
Test Code
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
// Some code to set the time zone
extern void tzset(void);
int set_tz(const char *tz) {
//tz = getenv("TZ");
//if (tz) {
printf("%s\n", tz);
if (setenv("TZ", tz, 1))
return 1;
tzset();
return 0;
}
// Print time info around the potential mistake
void time_oops(const char *tz, time_t t0, time_t t1) {
time_t before[4] = {0};
if (set_tz(tz)) {
return;
}
set_tz(tz);
for (time_t t = t0; t <= t1; t += 3600) {
char buf[200];
struct tm *tm = gmtime(&t);
strftime(buf, sizeof buf, "%c %z %Z", tm);
printf(" gmt:%-36s %d\n", buf, tm->tm_isdst);
tm = localtime(&t);
strftime(buf, sizeof buf, "%c %z %Z", tm);
printf("local:%-36s %d\n", buf, tm->tm_isdst);
printf("time_t:%8jd,", (intmax_t) t);
for (int dst = -2; dst <= 1; dst++) {
tm = localtime(&t);
if (dst > -2)
tm->tm_isdst = dst;
time_t t_make = mktime(tm);
printf("%8jd", (intmax_t) t_make);
if (t == t0) {
printf(" %5s, ", "");
} else {
printf(" %5jd, ", (intmax_t) (t_make - before[dst + 2]));
}
before[dst + 2] = t_make;
}
puts("");
}
}
// Run some tests
int main(void) {
int h = 60 * 60;
time_oops("America/Los_Angeles", 25662600 + 5 * h, 25662600 + 11 * h);
time_oops("Africa/Juba", 24784200 - 3 * h, 24784200 + 3 * h);
time_oops("America/Dawson_Creek", 25662600 + 5 * h, 25662600 + 11 * h);
time_oops("America/Fort_Nelson", 25687800 - 2 * h, 25687800 + 4 * h);
time_oops("America/Punta_Arenas", 7522200 - 2 * h, 7522200 + 4 * h);
return 0;
}
Some compiler output
make all
Building file: ../test.c
Invoking: Cygwin C Compiler
gcc -O0 -g3 -pedantic -Wall -Wextra -Wconversion -c -fmessage-length=0 -v -MMD -MP -MF"test.d" -MT"test.o" -o "test.o" "../test.c"
Using built-in specs.
COLLECT_GCC=gcc
Target: x86_64-pc-cygwin
Configured with: /cygdrive/i/szsz/tmpp/gcc/gcc-7.3.0-3.x86_64/src/gcc-7.3.0/configure --srcdir=/cygdrive/i/szsz/tmpp/gcc/gcc-7.3.0-3.x86_64/src/gcc-7.3.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libcilkrts --enable-libgomp --enable-libitm --enable-libquadmath --enable-libquadmath-support --disable-libssp --enable-libada --disable-symvers --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
gcc version 7.3.0 (GCC)
COLLECT_GCC_OPTIONS='-O0' '-g3' '-Wpedantic' '-Wall' '-Wextra' '-Wconversion' '-c' '-fmessage-length=0' '-v' '-MMD' '-MP' '-MF' 'test.d' '-MT' 'test.o' '-o' 'test.o' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/cc1.exe -quiet -v -MMD test.d -MF test.d -MP -MT test.o -dD -idirafter /usr/lib/gcc/x86_64-pc-cygwin/7.3.0/../../../../lib/../include/w32api -idirafter /usr/lib/gcc/x86_64-pc-cygwin/7.3.0/../../../../x86_64-pc-cygwin/lib/../lib/../../include/w32api ../test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase-strip test.o -g3 -O0 -Wpedantic -Wall -Wextra -Wconversion -version -fmessage-length=0 -o /cygdrive/c/Users/TPC/AppData/Local/Temp/cc3JcC90.s
GNU C11 (GCC) version 7.3.0 (x86_64-pc-cygwin)
compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1-p6, MPC version 1.1.0, isl version isl-0.16.1-GMP
I used zdump to dump zone data yet found no difference for zones that work as expected and those that did not.
Some time zone data dump.
zdump -v /usr/share/zoneinfo/America/Los_Angeles
zdump -v /usr/share/zoneinfo/America/Dawson_Creek
/usr/share/zoneinfo/America/Los_Angeles Sun Apr 26 09:59:59 1970 UT = Sun Apr 26 01:59:59 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Los_Angeles Sun Apr 26 10:00:00 1970 UT = Sun Apr 26 03:00:00 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Los_Angeles Sun Oct 25 08:59:59 1970 UT = Sun Oct 25 01:59:59 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Los_Angeles Sun Oct 25 09:00:00 1970 UT = Sun Oct 25 01:00:00 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Dawson_Creek Sun Apr 26 09:59:59 1970 UT = Sun Apr 26 01:59:59 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Dawson_Creek Sun Apr 26 10:00:00 1970 UT = Sun Apr 26 03:00:00 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Dawson_Creek Sun Oct 25 08:59:59 1970 UT = Sun Oct 25 01:59:59 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Dawson_Creek Sun Oct 25 09:00:00 1970 UT = Sun Oct 25 01:00:00 1970 PST isdst=0 gmtoff=-28800
I was trying to find a way to receive a notification from the system (Linux) when daylight savings are applied, but I do not seem to be able to find anything like that.
Consider a program sits on a pselect() waiting for a number of timer fd's, all which have exactly 24-hour intervals, but differing start times, which are defined by a user; "07:00 ON, 07:25 OFF" (for example, if it were a coffee maker).
Because the user gives these times in local time and Linux runs on UTC, the time zone adjusted timer fd's need to be readjusted each time a daylight savings occure. (user expects coffee when his daylight savings compliant alarm clock has woken him up...)
Intelligent way to go about this, as I would imagine, would be to register to the system/kernel/init/whatever to be notified when daylight savings are applied, and avoid getting into the messy business of trying to determine such dates and times yourself and hope the system agrees with your results (ie. your resync actions and actual daylight savings happen in the same time).
Is there any way to be notified on DST changes? Or perhaps on any changes to local time (assuming DST change modifies that)?
Consider a program sits on a pselect() waiting for a number of timer fd's, all which have exactly 24-hour intervals, but differing start times
Therein lies your fundamental problem. All days are not exactly 24 hours long -- sometimes they are off by an hour (daylight savings time), or by seconds (leap seconds); just like not every February has 28 days.
A much simpler and lightweight (less resources consumed) way is to use a min-heap of future events in UTC, something like
struct trigger {
/* Details on how the event is defined;
for example, "each day at 07:00 local time".
*/
};
struct utc_event {
struct trigger *trigger;
time_t when;
};
struct event_min_heap {
size_t max_events;
size_t num_events;
struct utc_event event[];
};
The event C99 flexible array member in struct event_min_heap is an array with num_events events (memory allocated for max_events; can be reallocated if more events are needed) in a min heap keyed by the when field in each event entry. That is, the earliest event is always at the root.
Whenever current time is at least event[0].when, it is "triggered" -- meaning whatever action is to be taken, is taken --, and based on the struct trigger it refers to, the time of the next occurrence of that event is updated to event[0], then it is percolated down in the heap to its proper place. Note that you simply use mktime() to obtain the UTC time from broken-down local time fields.
(If this were a multi-user service, then you can support multiple concurrent timezones, one for each trigger, by setting the TZ environment variable to the respective timezone definition, and calling tzset() before the call to mktime(). Because the environment is shared by all threads in the process, you would need to ensure only one thread does this at a time, if you have a multithreaded process. Normally, stuff like this is perfectly implementable using a single-threaded process.)
When the event in the root (event[0]) is deleted or percolated (sifted), the event with the next smallest when will be at the root. If when is equal or less to current time in UTC, it too is triggered.
When the next when is in the future, the process can sleep the remaining interval.
That is all there is to it. You don't need multiple timers -- which are a system-wide finite resource --, and you don't need to worry about whether some local time is daylight savings time or not; the C library mktime() will take care of such details for you.
Now, if you don't like this approach (which, again, uses fewer resources than the approach you outlined in your question), contact the SystemD developers. If you kiss up to them obsequiously enough, I'm sure they'll provide a dbus signal for you. It's not like there is any sanity in its current design, and one more wart certainly won't make it any worse. Switching to C# is likely to be considered a plus.
It is crucial to understand that mktime() computes the Unix Epoch time (time_t) for the specified moment, applying daylight savings time if it applies at that specific moment. It does not matter whether daylight savings time is in effect when the function is called!
Also, UTC time is Coordinated Universal Time, and is not subject to timezones or daylight savings time.
Consider the following program, mktime-example.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
static time_t epoch(struct tm *const tm,
const int year, const int month, const int day,
const int hour, const int minute, const int second,
const int isdst)
{
struct tm temp;
time_t result;
memset(&temp, 0, sizeof temp);
temp.tm_year = year - 1900;
temp.tm_mon = month - 1;
temp.tm_mday = day;
temp.tm_hour = hour;
temp.tm_min = minute;
temp.tm_sec = second;
temp.tm_isdst = isdst;
result = mktime(&temp);
if (isdst >= 0 && isdst != temp.tm_isdst) {
/* The caller is mistaken about DST, and mktime()
* adjusted the time. We readjust it. */
temp.tm_year = year - 1900;
temp.tm_mon = month - 1;
temp.tm_mday = day;
temp.tm_hour = hour;
temp.tm_min = minute;
temp.tm_sec = second;
/* Note: tmp.tm_isdst is kept unchanged. */
result = mktime(&temp);
}
if (tm)
memcpy(tm, &temp, sizeof temp);
return result;
}
static void show(const time_t t, const struct tm *const tm)
{
printf("(time_t)%lld = %04d-%02d-%02d %02d:%02d:%02d",
(long long)t, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
if (tm->tm_isdst == 1)
printf(", DST in effect");
else
if (tm->tm_isdst == 0)
printf(", DST not in effect");
else
if (tm->tm_isdst == -1)
printf(", Unknown if DST in effect");
if (tzname[0] && tzname[0][0])
printf(", %s timezone", tzname[0]);
printf("\n");
fflush(stdout);
}
int main(int argc, char *argv[])
{
struct tm tm;
time_t t;
long long secs;
int arg, year, month, day, hour, min, sec, isdst, n;
char ch;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s [ :REGION/CITY | =TIMEZONE ] #EPOCH | YYYYMMDD-HHMMSS[+-] ...\n", argv[0]);
fprintf(stderr, "Where:\n");
fprintf(stderr, " EPOCH is in UTC seconds since 19700101T000000,\n");
fprintf(stderr, " + after time indicates you prefer daylight savings time,\n");
fprintf(stderr, " - after time indicates you prefer standard time.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
for (arg = 1; arg < argc; arg++) {
if (argv[arg][0] == ':') {
if (argv[arg][1])
setenv("TZ", argv[arg], 1);
else
unsetenv("TZ");
tzset();
continue;
}
if (argv[arg][0] == '=') {
if (argv[arg][1])
setenv("TZ", argv[arg] + 1, 1);
else
unsetenv("TZ");
tzset();
continue;
}
if (argv[arg][0] == '#') {
if (sscanf(argv[arg] + 1, " %lld %c", &secs, &ch) == 1) {
t = (time_t)secs;
if (localtime_r(&t, &tm)) {
show(t, &tm);
continue;
}
}
}
n = sscanf(argv[arg], " %04d %02d %02d %*[-Tt] %02d %02d %02d %c",
&year, &month, &day, &hour, &min, &sec, &ch);
if (n >= 6) {
if (n == 6)
isdst = -1;
else
if (ch == '+')
isdst = +1; /* DST */
else
if (ch == '-')
isdst = 0; /* Not DST */
else
isdst = -1;
t = epoch(&tm, year, month, day, hour, min, sec, isdst);
if (t != (time_t)-1) {
show(t, &tm);
continue;
}
}
fflush(stdout);
fprintf(stderr, "%s: Cannot parse parameter.\n", argv[arg]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Compile it using e.g.
gcc -Wall -O2 mktime-example.c -o mktime-example
Run it without arguments to see the command-line usage. Run
./mktime-example :Europe/Helsinki 20161030-035959+ 20161030-030000- 20161030-030000+ 20161030-035959- 20161030-040000-
to examine the Unix timestamps around the time when DST ends in 2016 in Helsinki, Finland. The command will output
(time_t)1477789199 = 2016-10-30 03:59:59, DST in effect, EET timezone
(time_t)1477789200 = 2016-10-30 03:00:00, DST not in effect, EET timezone
(time_t)1477785600 = 2016-10-30 03:00:00, DST in effect, EET timezone
(time_t)1477792799 = 2016-10-30 03:59:59, DST not in effect, EET timezone
(time_t)1477792800 = 2016-10-30 04:00:00, DST not in effect, EET timezone
The output will be the same regardless of whether at the time of running this DST is in effect in some timezone or not!
When calling mktime() with .tm_isdst = 0 or .tm_isdst = 1, and mktime() changes it, it also changes the time specified (by the daylight savings time). When .tm_isdst = -1, it means caller is unaware of whether DST is applied or not, and the library will find out; but if there is both a valid standard time and DST time, the C library will pick one (you should assume it does so randomly). The epoch() function above corrects for this when necessary, un-adjusting the time if the user is not correct about DST.
Unix/linux systems only deal with UTC, and they use the time_t data (the number of seconds since 00:00h jan, 1st of 1970 UTC till now) as the internal time. Conversions to local time (with the complexities due to exceptions, variations for summer-winter periods, etc.) is done only when displaying the information to the user, so only on converting to local time it is done. As said, no provision to schedule something or preparation for it is made in the unix system.
From zdump(1) you can get all the info you want, per timezone, and use it to construct a crontab to notify you when the switch is to be made. It consults the local database of timezones and extracts all the info about switching (including historic) from winter to summer or the reverse.
$ zdump -v Europe/Madrid
Europe/Madrid Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 20:45:52 1901 WET isdst=0 gmtoff=0
Europe/Madrid Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 20:45:52 1901 WET isdst=0 gmtoff=0
Europe/Madrid Sat May 5 22:59:59 1917 UTC = Sat May 5 22:59:59 1917 WET isdst=0 gmtoff=0
Europe/Madrid Sat May 5 23:00:00 1917 UTC = Sun May 6 00:00:00 1917 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 6 22:59:59 1917 UTC = Sat Oct 6 23:59:59 1917 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 6 23:00:00 1917 UTC = Sat Oct 6 23:00:00 1917 WET isdst=0 gmtoff=0
Europe/Madrid Mon Apr 15 22:59:59 1918 UTC = Mon Apr 15 22:59:59 1918 WET isdst=0 gmtoff=0
Europe/Madrid Mon Apr 15 23:00:00 1918 UTC = Tue Apr 16 00:00:00 1918 WEST isdst=1 gmtoff=3600
Europe/Madrid Sun Oct 6 22:59:59 1918 UTC = Sun Oct 6 23:59:59 1918 WEST isdst=1 gmtoff=3600
Europe/Madrid Sun Oct 6 23:00:00 1918 UTC = Sun Oct 6 23:00:00 1918 WET isdst=0 gmtoff=0
Europe/Madrid Sat Apr 5 22:59:59 1919 UTC = Sat Apr 5 22:59:59 1919 WET isdst=0 gmtoff=0
Europe/Madrid Sat Apr 5 23:00:00 1919 UTC = Sun Apr 6 00:00:00 1919 WEST isdst=1 gmtoff=3600
Europe/Madrid Mon Oct 6 22:59:59 1919 UTC = Mon Oct 6 23:59:59 1919 WEST isdst=1 gmtoff=3600
Europe/Madrid Mon Oct 6 23:00:00 1919 UTC = Mon Oct 6 23:00:00 1919 WET isdst=0 gmtoff=0
Europe/Madrid Wed Apr 16 22:59:59 1924 UTC = Wed Apr 16 22:59:59 1924 WET isdst=0 gmtoff=0
Europe/Madrid Wed Apr 16 23:00:00 1924 UTC = Thu Apr 17 00:00:00 1924 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 4 22:59:59 1924 UTC = Sat Oct 4 23:59:59 1924 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 4 23:00:00 1924 UTC = Sat Oct 4 23:00:00 1924 WET isdst=0 gmtoff=0
Europe/Madrid Sat Apr 17 22:59:59 1926 UTC = Sat Apr 17 22:59:59 1926 WET isdst=0 gmtoff=0
Europe/Madrid Sat Apr 17 23:00:00 1926 UTC = Sun Apr 18 00:00:00 1926 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 2 22:59:59 1926 UTC = Sat Oct 2 23:59:59 1926 WEST isdst=1 gmtoff=3600
Europe/Madrid Sat Oct 2 23:00:00 1926 UTC = Sat Oct 2 23:00:00 1926 WET isdst=0 gmtoff=0
Europe/Madrid Sat Apr 9 22:59:59 1927 UTC = Sat Apr 9 22:59:59 1927 WET isdst=0 gmtoff=0
...
By the way, if you want to be advised of an imminent localtime change, you can use the previous info to construct a crontab file, including all the info, or simply construct a crontab file that include the rules that apply at your localty. For example, if I want to be advised one day before a switch change in Spain (it changes on last sunday of march/october, at 02/03h) you can add some rules in your crontab file:
0 0 24-30 3,10 5 echo Time daylight savings change scheduled for tomorrow | mail $USER#your.domain.com
and a mail will be sent to you in on every saturday(5) that happens to be in the week from 24-30th of march and october (3,10 part) of each year at 00:00h (localtime). I'm sure you'll be able to adapt this example to your localty or time of advance (so, the day before a time change happens).
How to find the most recent date from an array like the one below?
Tue Jun 2 17:59:54 GMT+0200 2013
Tue Jun 5 18:00:10 GMT+0200 2013
Tue Jun 1 12:27:14 GMT+0200 2013
Tue Jun 3 17:26:58 GMT+0200 2013
Tue Jun 9 17:27:49 GMT+0200 2013
Tue Jun 1 13:27:39 GMT+0200 2015
Tue Jun 3 12:27:59 GMT+0200 2013
Tue Jun 6 15:27:22 GMT+0200 2014
Tue Jun 2 17:27:30 GMT+0200 2014
Assuming your array is full of AS3 native Date objects, you could simply do this:
array.sortOn("time",Array.DESCENDING);
trace("Most Recent:",array[0]);
You cannot use array.sort (unless you use the Array.NUMERIC flag) because it will sort the string representation of the date. So all your days of the week would then be grouped together instead of the actual date.
If your dates are strings, then you will need to convert them to Date objects prior to sorting:
//assuming your posted array is in a var called 'stringArray'
var dateArray:Array = []; //a new array to hold the converted strings
for(var i:int=0;i<stringArray.length;i++){
dateArray.push(new Date(stringArray[i]));
}
dateArray.sortOn("time",Array.DESCENDING);
trace("Most Recent Date:",dateArray[0]);
To show this in a concrete example, here is your posted dates - copy paste this code to produce the same results:
var arr:Array = new Array(
new Date("Tue Jun 2 17:59:54 GMT+0200 2013"),
new Date("Tue Jun 5 18:00:10 GMT+0200 2013"),
new Date("Tue Jun 1 12:27:14 GMT+0200 2013"),
new Date("Tue Jun 3 17:26:58 GMT+0200 2013"),
new Date("Tue Jun 9 17:27:49 GMT+0200 2013"),
new Date("Tue Jun 1 13:27:39 GMT+0200 2015"),
new Date("Tue Jun 3 12:27:59 GMT+0200 2013"),
new Date("Tue Jun 6 15:27:22 GMT+0200 2014"),
new Date("Tue Jun 2 17:27:30 GMT+0200 2014")
);
arr.sort(Array.DESCENDING);
trace("SORT:");
traceDates();
arr.sortOn("time",Array.DESCENDING);
trace("\nSORT ON:");
traceDates();
function traceDates(){
for(var i:int=0;i<arr.length;i++){
trace(" ",arr[i].fullYear + "-" + arr[i].month + "-" + arr[i].day);
}
}
//OUTPUT:
/*
SORT:
2013-5-3
2013-5-0
2013-5-0
2013-5-6
2013-5-1
2013-5-1
2014-5-1
2015-5-1 //most recent date, second to LAST item in the array
2014-5-5
SORT ON:
2015-5-1 //June 1st is the most recent date (first item in the array)
2014-5-5
2014-5-1
2013-5-0
2013-5-3
2013-5-1
2013-5-1
2013-5-0
2013-5-6
*/
Are they Date objects?
If so, you can compare the time property of each. It will give you the number of milliseconds since Jan 1, 1970. The highest number will be the most recent.
Something along these lines:
var mostRecentDate:Date = dateArray[0];
for(var i:int = 0; i < dateArray.length; i++){
if(dateArray[i].time > mostRecentDate.time){
mostRecentDate = dateArray[i];
}
}
Date objects act like simple Number when it comes to sorting or comparison. All you have to do is treat them like Numbers. So taken from Cadin answer:
dateArray.sort();
var oldestDate:Date = dateArray[0];
Will get you the oldest Date while:
dateArray.sort(Array.DESCENDING);
var mostRecentDate:Date = dateArray[0];
Will get you the most recent one.
For LDMS, this is what I got:
var firstdate:Date = new Date();
var seconddate:Date = new Date();
var thirddate:Date = new Date();
seconddate.time = firstdate.time + 5000000;
thirddate.time = firstdate.time + 50000000;
trace(seconddate > firstdate)//true
trace(firstdate > seconddate)//false
trace(seconddate.time > firstdate.time)//true
var array:Array = [thirddate, firstdate, seconddate];
trace(array)
//Wed Jun 3 03:37:40 GMT-0400 2015,Tue Jun 2 13:44:20 GMT-0400 2015,Tue Jun 2 15:07:40 GMT-0400 2015
array.sort();
trace(array)
//Tue Jun 2 13:44:20 GMT-0400 2015,Tue Jun 2 15:07:40 GMT-0400 2015,Wed Jun 3 03:37:40 GMT-0400 2015
array.sort(Array.DESCENDING);
trace(array)
//Wed Jun 3 03:37:40 GMT-0400 2015,Tue Jun 2 15:07:40 GMT-0400 2015,Tue Jun 2 13:44:20 GMT-0400 2015
Sort the array and grab the first (descending) or last (ascending) element.
Edit: 2 down-votes because I didn't provide an example, or because people don't know you can sort dates? This works:
var dates:Array = [
"Tue Jun 2 17:59:54 GMT+0200 2013",
"Tue Jun 5 18:00:10 GMT+0200 2013",
"Tue Jun 1 12:27:14 GMT+0200 2013",
"Tue Jun 3 17:26:58 GMT+0200 2013",
"Tue Jun 9 17:27:49 GMT+0200 2013",
"Tue Jun 1 13:27:39 GMT+0200 2015",
"Tue Jun 3 12:27:59 GMT+0200 2013",
"Tue Jun 6 15:27:22 GMT+0200 2014",
"Tue Jun 2 17:27:30 GMT+0200 2014"
].map(function(s:String, i:int, a:Array):Date {
return new Date(s);
}).sort(Array.NUMERIC | Array.DESCENDING);
var latest:Date = dates[0]; // Mon Jun 1 07:27:39 GMT-0400 2015
The problem is the OP did not make it clear what kind of data they are working with (strings or Date objects) so the exact solution code is unknown.
I am returning a collection from the query. The query is in this thread.
So my result is like below in my log:
[{"id"=>1, "name"=>"ASP.NET-WebForms", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:04:18 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:04:18 UTC +00:00, "skill_type"=>"Serverside-Framework", :assigned=>true}, {"id"=>2, "name"=>"ASP.NET-MVC", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:05:01 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:05:01 UTC +00:00, "skill_type"=>"Serverside-Framework", :assigned=>true}, {"id"=>3, "name"=>"RUBY-ON-RAILS", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:05:20 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:05:20 UTC +00:00, "skill_type"=>"Serverside-Framework", :assigned=>false}, {"id"=>4, "name"=>"C#", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>2, "created_at"=>Thu, 23 Apr 2015 06:05:57 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:12:02 UTC +00:00, "skill_type"=>"Language", :assigned=>false}, {"id"=>5, "name"=>"Ruby", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:07:43 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:07:43 UTC +00:00, "skill_type"=>"Language", :assigned=>true}, {"id"=>6, "name"=>"VB", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:08:15 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:08:15 UTC +00:00, "skill_type"=>"Language", :assigned=>false}, {"id"=>7, "name"=>"SQL-Server", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:08:32 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:08:32 UTC +00:00, "skill_type"=>"DBMS", :assigned=>false}, {"id"=>8, "name"=>"MySQL", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:08:49 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:08:49 UTC +00:00, "skill_type"=>"DBMS", :assigned=>false}, {"id"=>9, "name"=>"Oracle", "description"=>"", "record_status"=>1, "created_by"=>1, "updated_by"=>1, "created_at"=>Thu, 23 Apr 2015 06:09:01 UTC +00:00, "updated_at"=>Thu, 23 Apr 2015 06:09:01 UTC +00:00, "skill_type"=>"DBMS", :assigned=>false}]
I think this is an array instead of an object collection. So how can I display this as json using rabl. The collection #technical_skills is not working.
My expected output is:
{
"domain_skill": {
"id": 1,
"name": "domain name",
"description": "some description text",
"technical_skills": [
{
"id": 1,
"name": "ASP.NET-WebForms",
"assigned": true
},
{
"id": 2,
"name": "ASP.NET-MVC",
"assigned": true
}
]
}
}
Edit:
I got that this is a json array. My method to merge another field so that I can convert this to json and merge the new fields. Now I thought this is json array. Please check my controller method below:
id = params[:id]
#domain_skill_technical_skill_ids = DomainSkillsTechnicalSkill.select('id, technical_skill_id').where(domain_skill_id: id).collect(&:technical_skill_id)
Rails.logger.info(#domain_skill_technical_skill_ids.to_a)
#technical_skills = []
TechnicalSkill.select('id, name').where(record_status: 1).each do |skill|
#technical_skills << skill.as_json.merge!(
{ assigned: #domain_skill_technical_skill_ids.include?(skill.id)}
)
end
#domain_skill_view_model = DomainSkillViewModel.new(#domain_skill, #technical_skills)
respond_with #domain_skill_view_model
My rabl:
object :#domain_skill_view_model
object :#domain_skill
attributes :id, :name, :description
node :technical_skills do
#technical_skills.each do |skill|
end
end