// swad_date.c: dates
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2019 Antonio Caņas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#include // For string functions
#include // For time functions (mktime...)
#include "swad_box.h"
#include "swad_calendar.h"
#include "swad_config.h"
#include "swad_database.h"
#include "swad_date.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_parameter.h"
#include "swad_setting.h"
#include "swad_table.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Public constants ******************************/
/*****************************************************************************/
/*****************************************************************************/
/**************************** Private constants ******************************/
/*****************************************************************************/
const unsigned Dat_NumDaysMonth[1 + 12] =
{
0,
31, // 1: January
28, // 2: February
31, // 3: Mars
30, // 4: April
31, // 5: May
30, // 6: June
31, // 7: July
31, // 8: Agoust
30, // 9: September
31, // 10: October
30, // 11: November
31, // 12: December
};
const char *Dat_TimeStatusClassVisible[Dat_NUM_TIME_STATUS] =
{
"DATE_RED", // Dat_PAST
"DATE_GREEN", // Dat_PRESENT
"DATE_BLUE", // Dat_FUTURE
};
const char *Dat_TimeStatusClassHidden[Dat_NUM_TIME_STATUS] =
{
"DATE_RED_LIGHT", // Dat_PAST
"DATE_GREEN_LIGHT", // Dat_PRESENT
"DATE_BLUE_LIGHT", // Dat_FUTURE
};
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/**************************** Private prototypes *****************************/
/*****************************************************************************/
static void Dat_PutIconsDateFormat (void);
static unsigned Dat_GetParamDateFormat (void);
/*****************************************************************************/
/************** Put icons to select the first day of the week ****************/
/*****************************************************************************/
void Dat_PutBoxToSelectDateFormat (void)
{
extern const char *Hlp_PROFILE_Settings_dates;
extern const char *Txt_Dates;
Dat_Format_t Format;
/***** Start box *****/
Box_StartBox (NULL,Txt_Dates,Dat_PutIconsDateFormat,
Hlp_PROFILE_Settings_dates,Box_NOT_CLOSABLE);
/***** Form with list of options *****/
Frm_StartForm (ActChgDatFmt);
fprintf (Gbl.F.Out,"
");
for (Format = (Dat_Format_t) 0;
Format <= (Dat_Format_t) (Dat_NUM_OPTIONS_FORMAT - 1);
Format++)
{
fprintf (Gbl.F.Out,"
"
""
"
");
}
/***** End list and form *****/
fprintf (Gbl.F.Out,"
");
/***** End form *****/
Frm_EndForm ();
/***** End box *****/
Box_EndBox ();
}
/*****************************************************************************/
/*************** Put contextual icons in date-format setting *****************/
/*****************************************************************************/
static void Dat_PutIconsDateFormat (void)
{
/***** Put icon to show a figure *****/
Gbl.Figures.FigureType = Fig_DATE_FORMAT;
Fig_PutIconToShowFigure ();
}
/*****************************************************************************/
/******* Put script to write current date-time in a given date format ********/
/*****************************************************************************/
void Dat_PutSpanDateFormat (Dat_Format_t Format)
{
fprintf (Gbl.F.Out,"",
(unsigned) Format);
}
void Dat_PutScriptDateFormat (Dat_Format_t Format)
{
fprintf (Gbl.F.Out,"",
(unsigned) Format,(long) Gbl.StartExecutionTimeUTC,
(unsigned) Format);
}
/*****************************************************************************/
/***************************** Change date format ****************************/
/*****************************************************************************/
void Dat_ChangeDateFormat (void)
{
/***** Get param with date format *****/
Gbl.Prefs.DateFormat = Dat_GetParamDateFormat ();
/***** Store date format in database *****/
if (Gbl.Usrs.Me.Logged)
DB_QueryUPDATE ("can not update your setting about date format",
"UPDATE usr_data SET DateFormat=%u"
" WHERE UsrCod=%ld",
(unsigned) Gbl.Prefs.DateFormat,
Gbl.Usrs.Me.UsrDat.UsrCod);
/***** Set settings from current IP *****/
Set_SetSettingsFromIP ();
}
/*****************************************************************************/
/********************** Get parameter with date format ***********************/
/*****************************************************************************/
static Dat_Format_t Dat_GetParamDateFormat (void)
{
return (Dat_Format_t) Par_GetParToUnsignedLong ("DateFormat",
0L,
(unsigned long) (Dat_NUM_OPTIONS_FORMAT - 1),
(unsigned long) Dat_FORMAT_DEFAULT);
}
/*****************************************************************************/
/*********************** Get date format from string *************************/
/*****************************************************************************/
Dat_Format_t Dat_GetDateFormatFromStr (const char *Str)
{
unsigned UnsignedNum;
if (sscanf (Str,"%u",&UnsignedNum) == 1)
if (UnsignedNum < Dat_NUM_OPTIONS_FORMAT)
return (Dat_Format_t) UnsignedNum;
return Dat_FORMAT_DEFAULT;
}
/*****************************************************************************/
/************************** Get current time UTC *****************************/
/*****************************************************************************/
void Dat_GetStartExecutionTimeUTC (void)
{
Gbl.StartExecutionTimeUTC = time (NULL);
}
/*****************************************************************************/
/********************** Get and convert current time *************************/
/*****************************************************************************/
void Dat_GetAndConvertCurrentDateTime (void)
{
struct tm *tm_ptr;
/***** Convert current local time to a struct tblock *****/
tm_ptr = Dat_GetLocalTimeFromClock (&Gbl.StartExecutionTimeUTC);
Gbl.Now.Date.Year = tm_ptr->tm_year + 1900;
Gbl.Now.Date.Month = tm_ptr->tm_mon + 1;
Gbl.Now.Date.Day = tm_ptr->tm_mday;
Gbl.Now.Time.Hour = tm_ptr->tm_hour;
Gbl.Now.Time.Minute = tm_ptr->tm_min;
Gbl.Now.Time.Second = tm_ptr->tm_sec;
/***** Initialize current date in format YYYYMMDD *****/
snprintf (Gbl.Now.Date.YYYYMMDD,sizeof (Gbl.Now.Date.YYYYMMDD),
"%04u%02u%02u",
Gbl.Now.Date.Year,Gbl.Now.Date.Month,Gbl.Now.Date.Day);
/***** Initialize current time in format YYYYMMDDHHMMSS *****/
snprintf (Gbl.Now.YYYYMMDDHHMMSS,sizeof (Gbl.Now.YYYYMMDDHHMMSS),
"%04u%02u%02u%02u%02u%02u",
Gbl.Now.Date.Year,Gbl.Now.Date.Month,Gbl.Now.Date.Day,
Gbl.Now.Time.Hour,Gbl.Now.Time.Minute,Gbl.Now.Time.Second);
/***** Compute what day was yesterday *****/
Dat_GetDateBefore (&Gbl.Now.Date,&Gbl.Yesterday);
}
/*****************************************************************************/
/************ Get UNIX time (seconds since 1970 UTC) from string *************/
/*****************************************************************************/
time_t Dat_GetUNIXTimeFromStr (const char *Str)
{
time_t Time = (time_t) 0;
if (Str)
if (Str[0])
if (sscanf (Str,"%ld",&Time) != 1)
Time = (time_t) 0;
return Time;
}
/*****************************************************************************/
/*********** Get a struct Date from a string in YYYYMMDD format **************/
/*****************************************************************************/
bool Dat_GetDateFromYYYYMMDD (struct Date *Date,const char *YYYYMMDD)
{
if (YYYYMMDD)
if (YYYYMMDD[0])
if (sscanf (YYYYMMDD,"%04u%02u%02u",&(Date->Year),&(Date->Month),&(Date->Day)) == 3)
{
Str_Copy (Date->YYYYMMDD,YYYYMMDD,
Dat_LENGTH_YYYYMMDD);
return true;
}
Date->Year = Date->Month = Date->Day = 0;
Date->YYYYMMDD[0] = '\0';
return false;
}
/*****************************************************************************/
/******************** Write div for client local time ************************/
/*****************************************************************************/
void Dat_ShowClientLocalTime (void)
{
extern const char *Txt_Show_calendar;
extern const char *Txt_Show_agenda;
/***** Draw the current date and time *****/
/* Start container */
fprintf (Gbl.F.Out,"
");
/* Month with link to calendar */
fprintf (Gbl.F.Out,"
");
Frm_StartForm (ActSeeCal);
Frm_LinkFormSubmit (Txt_Show_calendar,"CURRENT_MONTH",NULL);
fprintf (Gbl.F.Out,"" // JavaScript will write HTML here
""
"");
Frm_EndForm ();
fprintf (Gbl.F.Out,"
");
/* Day with link to agenda (if I am logged) */
fprintf (Gbl.F.Out,"
");
if (Gbl.Usrs.Me.Logged)
{
Frm_StartForm (ActSeeMyAgd);
Frm_LinkFormSubmit (Txt_Show_agenda,"CURRENT_DAY",NULL);
}
fprintf (Gbl.F.Out,"" // JavaScript will write HTML here
"");
if (Gbl.Usrs.Me.Logged)
{
fprintf (Gbl.F.Out,"");
Frm_EndForm ();
}
fprintf (Gbl.F.Out,"
");
/* Time */
fprintf (Gbl.F.Out,"
" // JavaScript will write HTML here
"
");
/* End container */
fprintf (Gbl.F.Out,"
");
/* Write script to draw the month */
fprintf (Gbl.F.Out,"",
(long) Gbl.StartExecutionTimeUTC);
}
/*****************************************************************************/
/***************** Compute local time, adjusting day of week *****************/
/*****************************************************************************/
struct tm *Dat_GetLocalTimeFromClock (const time_t *timep)
{
struct tm *tm_ptr;
if ((tm_ptr = localtime (timep)) == NULL)
Lay_ShowErrorAndExit ("Can not get local time from clock.");
/***** Convert from sunday, monday, tuesday...
to monday, tuesday, wednesday... *****/
if (tm_ptr->tm_wday == 0) // sunday
tm_ptr->tm_wday = 6;
else if (tm_ptr->tm_wday >= 1 &&
tm_ptr->tm_wday <= 6) // monday to saturday
tm_ptr->tm_wday--;
else // error!
tm_ptr->tm_wday = 0;
return tm_ptr;
}
/*****************************************************************************/
/********* Convert a struct with Day, Month and Year to a date string ********/
/*****************************************************************************/
void Dat_ConvDateToDateStr (struct Date *Date,char StrDate[Cns_MAX_BYTES_DATE + 1])
{
extern const char *Txt_MONTHS_SMALL_SHORT[12];
if (Date->Day == 0 ||
Date->Month == 0 ||
Date->Year == 0)
StrDate[0] = '\0';
else
switch (Gbl.Prefs.DateFormat)
{
case Dat_FORMAT_YYYY_MM_DD:
snprintf (StrDate,Cns_MAX_BYTES_DATE + 1,
"%04u-%02u-%02u",
Date->Year,
Date->Month,
Date->Day);
break;
case Dat_FORMAT_DD_MONTH_YYYY:
snprintf (StrDate,Cns_MAX_BYTES_DATE + 1,
"%u %s %04u",
Date->Day,
Txt_MONTHS_SMALL_SHORT[Date->Month - 1],
Date->Year);
break;
case Dat_FORMAT_MONTH_DD_YYYY:
snprintf (StrDate,Cns_MAX_BYTES_DATE + 1,
"%s %u, %04u",
Txt_MONTHS_SMALL_SHORT[Date->Month - 1],
Date->Day,
Date->Year);
break;
}
}
/*****************************************************************************/
/*************** Show forms to enter initial and ending dates ****************/
/*****************************************************************************/
void Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (bool SetHMS000000To235959)
{
extern const char *The_ClassFormInBox[The_NUM_THEMES];
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
extern const char *Txt_Yesterday;
extern const char *Txt_Today;
Tbl_StartRow ();
/***** Start date-time *****/
fprintf (Gbl.F.Out,"
");
Dat_WriteFormClientLocalDateTimeFromTimeUTC ("Start",
"Start",
Gbl.DateRange.TimeUTC[0],
Cfg_LOG_START_YEAR,
Gbl.Now.Date.Year,
Dat_FORM_SECONDS_ON,
SetHMS000000To235959 ? Dat_HMS_TO_000000 : // Set hour, minute and second to 00:00:00
Dat_HMS_DO_NOT_SET, // Don't set hour, minute and second to 00:00:00
false); // Don't submit on change
Tbl_EndCell ();
/***** "Yesterday" and "Today" buttons *****/
fprintf (Gbl.F.Out,"
"
"");
Tbl_EndCell ();
/***** Second *****/
if (FormSeconds == Dat_FORM_SECONDS_ON)
{
fprintf (Gbl.F.Out,"
"
"");
Tbl_EndCell ();
}
/***** End table *****/
Tbl_EndRow ();
Tbl_EndTable ();
/***** Hidden field with UTC time (seconds since 1970) used to send time *****/
fprintf (Gbl.F.Out,"",
Id,ParamName,(long) TimeUTC);
/***** Script to set selectors to local date and time from UTC time *****/
fprintf (Gbl.F.Out,"");
}
/*****************************************************************************/
/***************** Get an hour-minute time from a form ***********************/
/*****************************************************************************/
time_t Dat_GetTimeUTCFromForm (const char *ParamName)
{
/**** Get time ****/
return Par_GetParToLong (ParamName);
}
/*****************************************************************************/
/**************** Put a hidden param with time difference ********************/
/**************** between UTC time and client local time, ********************/
/**************** in minutes ********************/
/*****************************************************************************/
void Dat_PutHiddenParBrowserTZDiff (void)
{
fprintf (Gbl.F.Out,""
""
"");
}
/*****************************************************************************/
/*************************** Get browser time zone ***************************/
/*****************************************************************************/
// MySQL CONVERT_TZ function may fail around changes related to Daylight Saving Time
// when a fixed amount (for example +01:00) is used as destination time zone,
// because this amount may be different before and after the DST change
void Dat_GetBrowserTimeZone (char BrowserTimeZone[Dat_MAX_BYTES_TIME_ZONE + 1])
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
bool TZNameIsUsable = false;
char IntStr[1 + 10 + 1];
int ClientUTCMinusLocal; // Time difference between UTC time and client local time, in minutes
/***** 1. Get client time zone name *****/
// We get client time zone using JavaScript jstz script, available in:
// - http://pellepim.bitbucket.org/jstz/
// - https://bitbucket.org/pellepim/jstimezonedetect/
// The return value is an IANA zone info key (aka the Olson time zone database).
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
// For example, if browser is in Madrid(Spain) timezone, "Europe/Berlin" will be returned.
Par_GetParToText ("BrowserTZName",BrowserTimeZone,Dat_MAX_BYTES_TIME_ZONE);
/* Check if client time zone is usable with CONVERT_TZ */
if (BrowserTimeZone[0])
{
/* Try to convert a date from server time zone to browser time zone */
if (DB_QuerySELECT (&mysql_res,"can not check if time zone name"
" is usable",
"SELECT CONVERT_TZ(NOW(),@@session.time_zone,'%s')",
BrowserTimeZone))
{
row = mysql_fetch_row (mysql_res);
if (row[0] != NULL)
TZNameIsUsable = true;
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
if (!TZNameIsUsable)
{
/***** 2. Get client time zone difference *****/
// We get client TZ difference using JavaScript getTimezoneOffset() method
// getTimezoneOffset() returns UTC-time - browser-local-time, in minutes.
// For example, if browser time zone is GMT+2, -120 will be returned.
Par_GetParToText ("BrowserTZDiff",IntStr,1 + 10);
if (sscanf (IntStr,"%d",&ClientUTCMinusLocal) != 1)
ClientUTCMinusLocal = 0;
/* Convert from minutes to +-hh:mm */
// BrowserTimeZone must have space for strings in +hh:mm format (6 chars + \0)
if (ClientUTCMinusLocal > 0)
snprintf (BrowserTimeZone,Dat_MAX_BYTES_TIME_ZONE + 1,
"-%02u:%02u",
(unsigned) ClientUTCMinusLocal / 60,
(unsigned) ClientUTCMinusLocal % 60);
else // ClientUTCMinusLocal <= 0
snprintf (BrowserTimeZone,Dat_MAX_BYTES_TIME_ZONE + 1,
"+%02u:%02u",
(unsigned) (-ClientUTCMinusLocal) / 60,
(unsigned) (-ClientUTCMinusLocal) % 60);
}
}
/*****************************************************************************/
/************************* Show a form to enter a date ***********************/
/*****************************************************************************/
/*
See the following pages:
http://javascript.internet.com/forms/date-selection-form.html
http://www.java-scripts.net/javascripts/Calendar-Popup.phtml
http://webmonkey.wired.com/webmonkey/98/04/index3a_page10.html?tw=programming
http://www.jsmadeeasy.com/javascripts/Calendars/Select-A-Mo://tech.irt.org/articles/js068/calendar.htm
http://javascript.internet.com/calendars/select-a-month.html
http://javascript.internet.com/forms/country.html
http://javascript.internet.com/forms/category-selection.html
See also http://www.ashleyit.com/rs/jsrs/select/php/select.php
*/
void Dat_WriteFormDate (unsigned FirstYear,unsigned LastYear,
const char *Id,
struct Date *DateSelected,
bool SubmitFormOnChange,bool Disabled)
{
extern const char *Txt_MONTHS_SMALL[12];
unsigned Year;
unsigned Month;
unsigned Day;
unsigned NumDaysSelectedMonth;
/***** Start table *****/
Tbl_StartTable ();
Tbl_StartRow ();
/***** Year *****/
fprintf (Gbl.F.Out,"