/* -*- Mode: C -*-
======================================================================
FILE: icaltime.c
CREATOR: eric 02 June 2000
$Id$
$Locker$
(C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
This program is free software; you can redistribute it and/or modify
it under the terms of either:
The LGPL as published by the Free Software Foundation, version
2.1, available at: http://www.fsf.org/copyleft/lesser.html
Or:
The Mozilla Public License Version 1.0. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
The Original Code is eric. The Initial Developer of the Original
Code is Eric Busboom
======================================================================*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "icaltime.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef ICAL_NO_LIBICAL
#define icalerror_set_errno(x)
#define icalerror_check_arg_rv(x,y)
#define icalerror_check_arg_re(x,y,z)
#else
#include "icalerror.h"
#include "icalmemory.h"
#endif
struct icaltimetype
icaltime_from_timet(time_t tm, int is_date)
{
struct icaltimetype tt = icaltime_null_time();
struct tm t;
t = *(gmtime(&tm));
if(is_date == 0){
tt.second = t.tm_sec;
tt.minute = t.tm_min;
tt.hour = t.tm_hour;
} else {
tt.second = tt.minute =tt.hour = 0 ;
}
tt.day = t.tm_mday;
tt.month = t.tm_mon + 1;
tt.year = t.tm_year+ 1900;
tt.is_utc = 1;
tt.is_date = is_date;
return tt;
}
/* Structure used by set_tz to hold an old value of TZ, and the new
value, which is in memory we will have to free in unset_tz */
/* This will hold the last "TZ=XXX" string we used with putenv(). After we
call putenv() again to set a new TZ string, we can free the previous one.
As far as I know, no libc implementations actually free the memory used in
the environment variables (how could they know if it is a static string or
a malloc'ed string?), so we have to free it ourselves. */
static char* saved_tz = NULL;
/* If you use set_tz(), you must call unset_tz() some time later to restore the
original TZ. Pass unset_tz() the string that set_tz() returns. */
char* set_tz(const char* tzid)
{
char *old_tz, *old_tz_copy = NULL, *new_tz;
/* Get the old TZ setting and save a copy of it to return. */
old_tz = getenv("TZ");
if(old_tz){
old_tz_copy = (char*)malloc(strlen (old_tz) + 4);
if(old_tz_copy == 0){
icalerror_set_errno(ICAL_NEWFAILED_ERROR);
return 0;
}
strcpy (old_tz_copy, "TZ=");
strcpy (old_tz_copy + 3, old_tz);
}
/* Create the new TZ string. */
new_tz = (char*)malloc(strlen (tzid) + 4);
if(new_tz == 0){
icalerror_set_errno(ICAL_NEWFAILED_ERROR);
return 0;
}
strcpy (new_tz, "TZ=");
strcpy (new_tz + 3, tzid);
/* Add the new TZ to the environment. */
putenv(new_tz);
/* Free any previous TZ environment string we have used. */
if (saved_tz)
free (saved_tz);
/* Save a pointer to the TZ string we just set, so we can free it later. */
saved_tz = new_tz;
return old_tz_copy; /* This will be zero if the TZ env var was not set */
}
void unset_tz(char *tzstr)
{
/* restore the original environment */
if(tzstr!=0){
putenv(tzstr);
} else {
putenv("TZ"); /* Delete from environment */
}
/* Free any previous TZ environment string we have used. */
if (saved_tz)
free (saved_tz);
/* Save a pointer to the TZ string we just set, so we can free it later.
(This can possibly be NULL if there was no TZ to restore.) */
saved_tz = tzstr;
}
time_t icaltime_as_timet(struct icaltimetype tt)
{
struct tm stm;
time_t t;
memset(&stm,0,sizeof( struct tm));
if(icaltime_is_null_time(tt)) {
return 0;
}
stm.tm_sec = tt.second;
stm.tm_min = tt.minute;
stm.tm_hour = tt.hour;
stm.tm_mday = tt.day;
stm.tm_mon = tt.month-1;
stm.tm_year = tt.year-1900;
stm.tm_isdst = -1;
if(tt.is_utc == 1 || tt.is_date == 1){
char *old_tz = set_tz("UTC");
t = mktime(&stm);
unset_tz(old_tz);
} else {
t = mktime(&stm);
}
return t;
}
char* icaltime_as_ical_string(struct icaltimetype tt)
{
size_t size = 17;
char* buf = icalmemory_new_buffer(size);
if(tt.is_date){
snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day);
} else {
char* fmt;
if(tt.is_utc){
fmt = "%04d%02d%02dT%02d%02d%02dZ";
} else {
fmt = "%04d%02d%02dT%02d%02d%02d";
}
snprintf(buf, size,fmt,tt.year,tt.month,tt.day,
tt.hour,tt.minute,tt.second);
}
icalmemory_add_tmp_buffer(buf);
return buf;
}
/* convert tt, of timezone tzid, into a utc time */
struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
{
int tzid_offset;
if(tt.is_utc == 1 || tt.is_date == 1){
return tt;
}
tzid_offset = icaltime_utc_offset(tt,tzid);
tt.second -= tzid_offset;
tt.is_utc = 1;
return icaltime_normalize(tt);
}
/* convert tt, a time in UTC, into a time in timezone tzid */
struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
{
int tzid_offset;
tzid_offset = icaltime_utc_offset(tt,tzid);
tt.second += tzid_offset;
tt.is_utc = 0;
return icaltime_normalize(tt);
}
/* Return the offset of the named zone as seconds. tt is a time
indicating the date for which you want the offset */
int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
{
time_t tt = icaltime_as_timet(ictt);
time_t offset_tt;
struct tm gtm;
char *tz_str = 0;
if(tzid != 0){
tz_str = set_tz(tzid);
}
/* Mis-interpret a UTC broken out time as local time */
gtm = *(gmtime(&tt));
gtm.tm_isdst = localtime(&tt)->tm_isdst;
offset_tt = mktime(>m);
if(tzid != 0){
unset_tz(tz_str);
}
return tt-offset_tt;
}
/* Normalize by converting from localtime to utc and back to local
time. This uses localtime because localtime and mktime are inverses
of each other */
struct icaltimetype icaltime_normalize(struct icaltimetype tt)
{
struct tm stm;
time_t tut;
memset(&stm,0,sizeof( struct tm));
stm.tm_sec = tt.second;
stm.tm_min = tt.minute;
stm.tm_hour = tt.hour;
stm.tm_mday = tt.day;
stm.tm_mon = tt.month-1;
stm.tm_year = tt.year-1900;
stm.tm_isdst = -1; /* prevents mktime from changing hour based on
daylight savings */
tut = mktime(&stm);
stm = *(localtime(&tut));
tt.second = stm.tm_sec;
tt.minute = stm.tm_min;
tt.hour = stm.tm_hour;
tt.day = stm.tm_mday;
tt.month = stm.tm_mon +1;
tt.year = stm.tm_year+1900;
return tt;
}
#ifndef ICAL_NO_LIBICAL
#include "icalvalue.h"
struct icaltimetype icaltime_from_string(const char* str)
{
struct icaltimetype tt = icaltime_null_time();
int size;
icalerror_check_arg_re(str!=0,"str",icaltime_null_time());
size = strlen(str);
if(size == 15) { /* floating time */
tt.is_utc = 0;
tt.is_date = 0;
} else if (size == 16) { /* UTC time, ends in 'Z'*/
tt.is_utc = 1;
tt.is_date = 0;
if(str[15] != 'Z'){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return icaltime_null_time();
}
} else if (size == 8) { /* A DATE */
tt.is_utc = 1;
tt.is_date = 1;
} else { /* error */
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return icaltime_null_time();
}
if(tt.is_date == 1){
sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day);
} else {
char tsep;
sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day,
&tsep,&tt.hour,&tt.minute,&tt.second);
if(tsep != 'T'){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return icaltime_null_time();
}
}
return tt;
}
#endif
char ctime_str[20];
char* icaltime_as_ctime(struct icaltimetype t)
{
time_t tt;
tt = icaltime_as_timet(t);
sprintf(ctime_str,"%s",ctime(&tt));
ctime_str[strlen(ctime_str)-1] = 0;
return ctime_str;
}
short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
short icaltime_days_in_month(short month,short year)
{
int is_leap =0;
int days = days_in_month[month];
assert(month > 0);
assert(month <= 12);
if( (year % 4 == 0 && year % 100 != 0) ||
year % 400 == 0){
is_leap =1;
}
if( month == 2){
days += is_leap;
}
return days;
}
/* 1-> Sunday, 7->Saturday */
short icaltime_day_of_week(struct icaltimetype t){
time_t tt = icaltime_as_timet(t);
struct tm *tm;
if(t.is_utc == 1){
tm = gmtime(&tt);
} else {
tm = localtime(&tt);
}
return tm->tm_wday+1;
}
/* Day of the year that the first day of the week (Sunday) is on */
short icaltime_start_doy_of_week(struct icaltimetype t){
time_t tt = icaltime_as_timet(t);
time_t start_tt;
struct tm *stm;
int syear;
stm = gmtime(&tt);
syear = stm->tm_year;
start_tt = tt - stm->tm_wday*(60*60*24);
stm = gmtime(&start_tt);
if(syear == stm->tm_year){
return stm->tm_yday+1;
} else {
/* return negative to indicate that start of week is in
previous year. */
int is_leap = 0;
int year = stm->tm_year;
if( (year % 4 == 0 && year % 100 != 0) ||
year % 400 == 0){
is_leap =1;
}
return (stm->tm_yday+1)-(365+is_leap);
}
}
short icaltime_week_number(struct icaltimetype ictt)
{
char str[5];
time_t t = icaltime_as_timet(ictt);
int week_no;
strftime(str,5,"%V", gmtime(&t));
week_no = atoi(str);
return week_no;
}
short icaltime_day_of_year(struct icaltimetype t){
time_t tt = icaltime_as_timet(t);
struct tm *stm;
if(t.is_utc==1){
stm = gmtime(&tt);
} else {
stm = localtime(&tt);
}
return stm->tm_yday+1;
}
/* Jan 1 is day #1, not 0 */
struct icaltimetype icaltime_from_day_of_year(short doy, short year)
{
struct tm stm;
time_t tt;
char *old_tz = set_tz("UTC");
/* Get the time of january 1 of this year*/
memset(&stm,0,sizeof(struct tm));
stm.tm_year = year-1900;
stm.tm_mday = 1;
tt = mktime(&stm);
unset_tz(old_tz);
/* Now add in the days */
doy--;
tt += doy *60*60*24;
return icaltime_from_timet(tt, 1);
}
struct icaltimetype icaltime_null_time()
{
struct icaltimetype t;
memset(&t,0,sizeof(struct icaltimetype));
return t;
}
int icaltime_is_valid_time(struct icaltimetype t){
if(t.is_utc > 1 || t.is_utc < 0 ||
t.year < 0 || t.year > 3000 ||
t.is_date > 1 || t.is_date < 0){
return 0;
} else {
return 1;
}
}
int icaltime_is_null_time(struct icaltimetype t)
{
if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
return 1;
}
return 0;
}
int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
{
time_t t1 = icaltime_as_timet(a);
time_t t2 = icaltime_as_timet(b);
if (t1 > t2) {
return 1;
} else if (t1 < t2) {
return -1;
} else {
return 0;
}
}
int
icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b)
{
time_t t1;
time_t t2;
if (a.year == b.year && a.month == b.month && a.day == b.day)
return 0;
t1 = icaltime_as_timet (a);
t2 = icaltime_as_timet (b);
if (t1 > t2)
return 1;
else if (t1 < t2)
return -1;
else {
/* not reached */
assert (0);
return 0;
}
}
/* These are defined in icalduration.c:
struct icaltimetype icaltime_add(struct icaltimetype t,
struct icaldurationtype d)
struct icaldurationtype icaltime_subtract(struct icaltimetype t1,
struct icaltimetype t2)
*/