I am trying to create a Date header for an email.
The format is as follows:
DayOfWeek, Day ShortMonth Year Hours:Minutes:Seconds Offset (TimeZone)
So an example would be:
Fri, 19 Mar 2010 11:44:09 -0700 (PDT)
Its the TimeZone part (PDT in the example) that I'm trying to get. I realize that this will be different across systems, but I'd like a solution for all of them.
Thanks!
You are looking for %Z in strftime().
For example:
#include <stdio.h>
#include <time.h>
int main()
{
char buf[16];
time_t t = time(0);
strftime(buf, sizeof(buf), "%Z", localtime(&t));
printf("%s\n", buf);
return 0;
}
And it's C89, AFAIR (which means it's very portable).
Snippet from the C99 standard:
%Z is replaced by the locale’s time zone name or abbreviation, or by no characters if no time zone is determinable. [tm_isdst]
Sadly, as we noticed not every system gives reasonable values for it...
Related
I have written the following code to convert date to timestamp.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main()
{
struct tm date_time;
char date_time_buf[255];
char date_time_hdr[255]={0};
strncpy(date_time_hdr,"Thu, 02 Mar 2017 05:54:28 GMT",255);
memset(&date_time, 0, sizeof(struct tm));
strptime(date_time_hdr, "%a, %d %b %Y %H:%M:%S %Z", &date_time);
memset(date_time_buf, 0, 255);
strftime(date_time_buf, 255, "%s", &date_time);
int p=atoi(date_time_buf);
printf("time is %d \r\n", p);
return 0;
}
I am able to convert date to timestamp. But facing an issue.
The timestamp is offset by 5 hrs 30 minutes which is the timezone of my linux machine. But I don't want that. Is there a way to ignore system timezone?
Instead of using strftime() to format the broken down time struct tm as an integer, and parsing it using atoi(), you can use the simple but nonstandard timegm() function.
Even though timegm() is not in POSIX, it is provided by most POSIXy systems. If you want your code to be portable across POSIXy systems, you can use a workaround (as described in some versions of the timegm man page):
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
time_t alt_timegm(struct tm *from)
{
char *tz;
time_t result;
int saved_errno;
tz = getenv("TZ");
if (tz)
tz = strdup(tz);
setenv("TZ", "", 1); /* TZ empty refers to UTC */
tzset();
errno = 0;
result = mktime(from);
saved_errno = errno;
setenv("TZ", tz, 1);
free(tz);
errno = saved_errno;
return result;
}
This workaround does have the drawback that it temporarily modifies the current timezone, which affects other threads executing any timezone-related functions. In a single-threaded process this is not an issue (as the timezone-related functions are not async-signal safe, so they aren't to be used in signal handlers).
In multithreaded programs, one should serialize all accesses to the timezone related functions -- alt_timegm() above, mktime(), localtime(), and so on --, to ensure each function is run with the proper timezone set.
The timegm() implementations in Linux and BSDs are thread-safe; that is, they do not modify the timezone.
Using the above, parsing a string using an strptime() format to an Unix timestamp is easy:
const char *parse_utc(const char *s, const char *format, time_t *to)
{
char *result;
struct tm t_parts;
time_t t;
if (!s || !format) {
errno = EINVAL;
return NULL;
}
result = strptime(s, format, &t_parts);
if (!result) {
errno = EINVAL;
return NULL;
}
errno = 0;
t = alt_timegm(&t_parts);
if (to)
*to = result;
return (const char *)result;
}
The above parse_utc() parses a string s using strptime() format format, saving the UTC timestamp to to if not NULL, returning the pointer to the first unparsed character in string s.
Setting errno = 0 in the above functions may look strange, but it is because the mktime() (and timegm()) function may or may not set errno in case of an error; it just returns (time_t)-1. This means that it is impossible to reliably determine whether a (time_t)-1 (corresponding to last second of year 1969 in UTC`) is an actual valid timestamp, or an error return.
In other words, if errno != 0 after a call to parse_utc(), there was an error. (Note that if the string or format was invalid, parse_utc() returns NULL, with errno == EINVAL.) If errno == 0 but *to == (time_t)-1, it depends on the architecture whether the time string actually referred to the last second of year 1969 in UTC, or if it was an error; we simply do not know.
I'm trying to display the time, then wait for a period of time, then display the updated time. My code however prints the same time, without updating it.
this is my code so far:
#include<stdlib.h>
#include<time.h>
#include<sys/time.h>
int main(){
time_t timer;
time(&timer);
struct tm* time;
time = localtime(&timer);
printf("%s", asctime(time));
fflush(stdout);
sleep(4); //my attempt at adjusting the time by 4 seconds
time = localtime(&timer); // "refreshing" the time?
printf("%s", asctime(time));
return(0);
}
and my output is:
ubuntu#ubuntu:~/Desktop$ ./tester
Sat Feb 25 08:09:01 2012
Sat Feb 25 08:09:01 2012
ideally, i'd be using ctime(&timer) instead of localtime(&timer), but I'm just trying to adjust the time by 4 seconds for now. Any help would be appreciated.
localtime just converts a (pointer to) struct timep to a struct tm, it doesn't check what time it is at all.
Just call time(&timer) after the sleep if you want the new current time, and don't give a local variable the same name as a library function you're using in that same block.
(And you're missing two headers - <stdio.h> for printf, and <unistd.h> for sleep - make sure you enable warnings on your compiler.)
#include<stdlib.h>
#include<time.h>
#include<sys/time.h>
#include <stdio.h>
int main(){
time_t timer;
time(&timer);
struct tm* time_real;//time is function you can't use as variable
time_real = localtime(&timer);
printf("%s", asctime(time_real));
sleep(4);
time(&timer);//update to new time
time_real = localtime(&timer); // convert seconds to time structure tm
printf("%s", asctime(time_real));
return(0);
}
The info pages for the GNU date command contains this example:
For example, with the GNU date
command you can answer the question
"What time is it in New York when a
Paris clock shows 6:30am on October
31, 2004?" by using a date beginning
with `TZ="Europe/Paris"' as shown in
the following shell transcript:
$ export TZ="America/New_York"
$ date --date='TZ="Europe/Paris" 2004-10-31 06:30'
Sun Oct 31 01:30:00 EDT 2004
In this example, the '--date'
operand begins with its own 'TZ'
setting, so the rest of that operand
is processed according to
'Europe/Paris' rules, treating the
string 2004-10-31 06:30 as if it
were in Paris. However, since the
output of the date command is
processed according to the overall
time zone rules, it uses New York
time. (Paris was normally six hours
ahead of New York in 2004, but this
example refers to a brief Halloween
period when the gap was five hours.)
I am trying to accomplish essentially the same thing programatically in C without calling the date program millions of times. Basically I am looking for a way to take an arbitrary date and time in one timezone and convert it to the equivalent date and time in another timezone either directly or via conversion to and from UTC. I don't care about the formats of the input and output time as long as I can manipulate them using standard functions (strftime/strptime/mktime/etc).
The date program appears to accomplish this using complex routines internal to the coreutils package, I am looking for a way to do this in C using either standard POSIX/Linux routines or an external library. I looked at zoneinfo quite a bit which seemed promising but I cannot find any libraries to do anything useful with it.
I came up with a solution that seems to work on Linux with glibc, I don't know how portable this behavior is, any comments about portability or a better way to go about this would be welcome.
convert_time.c (No error checking for clarity):
#define _XOPEN_SOURCE
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
struct tm mytm = {0};
time_t mytime;
char buf[100];
mytm.tm_isdst = -1;
putenv(argv[1]);
tzset();
strptime(argv[2], "%Y-%m-%d %H:%M", &mytm);
mytime = mktime(&mytm);
putenv(argv[3]);
tzset();
localtime_r(&mytime, &mytm);
strftime(buf, 100, "%a, %d %b %Y %H:%M:%S %z(%Z)", &mytm);
puts(buf);
return 0;
}
The first argument is the source timezone (actually "TZ=Timezone" to pass to putenv), the second argument is the time in the specified format, the last argument is the destination timezone. I am using zoneinfo timezone names which glibc supports using the zoneinfo database.
The results of several DST test corner cases correspond to the results from the equivalent date command as well as this site which uses the zoneinfo database:
$ ./convert_time "TZ=America/New_York" "2005-05-31 06:30" "TZ=America/Indiana/Indianapolis"
Tue, 31 May 2005 05:30:00 -0500(EST)
$ ./convert_time "TZ=America/New_York" "2006-05-31 06:30" "TZ=America/Indiana/Indianapolis"
Wed, 31 May 2006 06:30:00 -0400(EDT)
$ ./convert_time "TZ=Europe/Paris" "2004-10-30 06:30" "TZ=America/New_York"
Sat, 30 Oct 2004 00:30:00 -0400(EDT)
$ ./convert_time "TZ=Europe/Paris" "2004-10-31 06:30" "TZ=America/New_York"
Sun, 31 Oct 2004 01:30:00 -0400(EDT)
$ ./convert_time "TZ=Europe/Paris" "2004-11-01 06:30" "TZ=America/New_York"
Mon, 01 Nov 2004 00:30:00 -0500(EST)
For time:
Get the GMT time with gmtime
Add/subtract the hours from time_t.tm_hour
Use mktime to renormalize
The date calculation will be similar but a little more complicated.
I was actually looking for the same answer you did, and this is the solution I came up with.
The following sample program takes the local system time and converts it to any timezone you want.
// compile: gcc snippet.c
// usage: ./a.out Europe/Berlin Pacific/Nauru Asia/Hong_Kong Asia/Kabul
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char buf[80];
time_t tt = time(NULL);
struct tm tm;
int i;
if (!localtime_r(&tt, &tm)) {
printf("Could not convert time_t to tm struct. %s\n", strerror(errno));
return 1;
}
printf("time_t: %ld\n", tt);
printf("timezone: %ld, %s/%s, daylight:%d\n", timezone, tzname[0], tzname[1], daylight);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z %Z", &tm);
printf("time: %s\n", buf);
for (i = 1; i < argc; ++i) {
printf("\ninput: %s\n", argv[i]);
snprintf(buf, sizeof(buf), "TZ=%s", argv[i]);
putenv(buf);
tzset();
printf("\ttimezone: %ld, %s/%s, daylight:%d\n", timezone, tzname[0], tzname[1], daylight);
if (!localtime_r(&tt, &tm)) {
printf("Could not convert time_t to tm struct. %s\n", strerror(errno));
return 1;
}
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z %Z", &tm);
printf("\ttime: %s\n", buf);
}
return 0;
}
I represent dates using seconds (and microseconds) since 1970 as well as a time zone and dst flag. I want to print a representation of the date using strftime, but it uses the global value for timezone (extern long int timezone) that is picked up from the environment. How can I get strftime to print the zone of my choice?
The following program sets the UNIX environment variable TZ with your required timezone and then prints a formatted time using strftime.
In the example below the timezone is set to U.S. Pacific Time Zone .
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main (int argc, char *argv[])
{
struct tm *mt;
time_t mtt;
char ftime[10];
setenv("TZ", "PST8PDT", 1);
tzset();
mtt = time(NULL);
mt = localtime(&mtt);
strftime(ftime,sizeof(ftime),"%Z %H%M",mt);
printf("%s\n", ftime);
}
Change timezone via setting timezone global variable and use localtime to get the time you print via strftime.
Jon Skeet spoke of the complexity of programming dates and times at the 2009 DevDays in London.
Can you give me an introduction to the ANSI C date/time functions on UNIX and indicate some of the deeper issues I should also consider when using dates and times?
Terminology
A date/time can be in two formats:
calendar time (a.k.a. simpletime) – time as an absolute value typically since some base time, often referred to as the Coordinated Universal Time
localtime (a.k.a. broken-down time) – a calendar time made up of components of year, month, day etc. which takes into account the local time zone including Daylight Saving Time if applicable.
Data types
The date/time functions and types are declared in the time.h header file.
Time can be stored as a whole number or as an instance of a structure:
as a number using the time_t arithmetic type – to store calendar time as the number of seconds elapsed since the UNIX epoch January 1, 1970 00:00:00
using the structure timeval – to store calendar time as the number of seconds and nanoseconds elapsed since the UNIX epoch January 1, 1970 00:00:00
using the structure tm to store localtime, it contains attributes such as the following:
tm_hour
tm_min
tm_isdst
The tm_isdst attribute above is used to indicate Daylight Saving Time (DST). If the value is positive it is DST, if the value is 0 it is not DST.
Program to print the current Coordinated Universal Time
#include <stdio.h>
#include <time.h>
int main ( int argc, char *argv[] )
{
time_t now;
now = time ( NULL );
printf ( "It’s %ld seconds since January 1, 1970 00:00:00", (long) now );
return 0;
}
In the program above the function time reads the UNIX system time, subtracts that from January 1, 1970 00:00:00 (the UNIX epoch) and returns its result in seconds.
Program to print the current local time
#include <stdio.h>
#include <time.h>
int main ( int argc, char *argv[] )
{
time_t now;
struct tm *lcltime;
now = time ( NULL );
lcltime = localtime ( &now );
printf ( "The time is %d:%d\n", lcltime->tm_hour, lcltime->tm_min );
return 0;
}
In the program above the function localtime converts the elapsed time in seconds from the UNIX epoch into the broken-down time. localtime reads the UNIX environment TZ (through a call to the tzset function) to return the time relative to the timezone and to set the tm_isdst attribute.
A typical setting of the TZ variable in UNIX (using bash) would be as follows:
export TZ=GMT
or
export TZ=US/Eastern
Program to print the current formatted Greenwich Mean Time
#include <stdio.h>
#include <time.h>
int main ( int argc, char *argv[] )
{
time_t now;
struct tm *gmt;
char formatted_gmt [50];
now = time ( NULL );
gmt = gmtime ( &now );
strftime ( formatted_gmt, sizeof(formatted_gmt), "%I:%M %p", gmt );
printf ( "The time is %s\n", formatted_gmt );
return 0;
}
In the program above the function strftime provides specialised formatting of dates.
Other issues to consider
Leap seconds
What should we do to prepare for 2038?