// 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-2023 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 ***********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For vasprintf #include // For vasprintf #include // For free #include // For string functions #include // For time functions (mktime...) #include "swad_action_list.h" #include "swad_box.h" #include "swad_calendar.h" #include "swad_config.h" #include "swad_database.h" #include "swad_date.h" #include "swad_error.h" #include "swad_figure.h" #include "swad_form.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_parameter.h" #include "swad_setting.h" #include "swad_setting_database.h" #include "swad_user_database.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /***************************** Public constants ******************************/ /*****************************************************************************/ const char *Dat_TimeStatusClassVisible[Dat_NUM_TIME_STATUS] = { [Dat_PAST ] = "DATE_RED", [Dat_PRESENT] = "DATE_GREEN", [Dat_FUTURE ] = "DATE_BLUE", }; const char *Dat_TimeStatusClassHidden[Dat_NUM_TIME_STATUS] = { [Dat_PAST ] = "DATE_RED_LIGHT", [Dat_PRESENT] = "DATE_GREEN_LIGHT", [Dat_FUTURE ] = "DATE_BLUE_LIGHT", }; /*****************************************************************************/ /**************************** Private constants ******************************/ /*****************************************************************************/ static const unsigned Dat_NumDaysMonth[1 + 12] = { [ 0] = 0, [ 1] = 31, // January [ 2] = 28, // February [ 3] = 31, // March [ 4] = 30, // April [ 5] = 31, // May [ 6] = 30, // June [ 7] = 31, // July [ 8] = 31, // Agoust [ 9] = 30, // September [10] = 31, // October [11] = 30, // November [12] = 31, // December }; static struct { struct timeval tvStart; struct timeval tvPageCreated; time_t StartExecutionTimeUTC; long TimeGenerationInMicroseconds; long TimeSendInMicroseconds; struct Dat_DateTime Now; struct { struct Dat_DateTime DateTime[Dat_NUM_START_END_TIME]; // TODO: Remove in future versions? time_t TimeUTC[Dat_NUM_START_END_TIME]; } Range; } Dat_Time = { .TimeGenerationInMicroseconds = 0L, .TimeSendInMicroseconds = 0L, }; static const char *Dat_ParName[Dat_NUM_START_END_TIME] = { [Dat_STR_TIME] = "Start", [Dat_END_TIME] = "End", }; static const char *Dat_ParTimeUTCName[Dat_NUM_START_END_TIME] = { [Dat_STR_TIME] = "StartTimeUTC", [Dat_END_TIME] = "EndTimeUTC", }; /*****************************************************************************/ /**************************** Private prototypes *****************************/ /*****************************************************************************/ static void Dat_PutIconsDateFormat (__attribute__((unused)) void *Args); static unsigned Dat_GetParDateFormat (void); /*****************************************************************************/ /*********************** Set/get start execution time ************************/ /*****************************************************************************/ void Dat_SetStartExecutionTimeval (void) { struct timezone tz; gettimeofday (&Dat_Time.tvStart, &tz); } void Dat_SetStartExecutionTimeUTC (void) { Dat_Time.StartExecutionTimeUTC = time (NULL); } time_t Dat_GetStartExecutionTimeUTC (void) { return Dat_Time.StartExecutionTimeUTC; } struct Dat_Date *Dat_GetCurrentDate (void) { return &Dat_Time.Now.Date; } unsigned Dat_GetCurrentDay (void) { return Dat_Time.Now.Date.Day; } unsigned Dat_GetCurrentMonth (void) { return Dat_Time.Now.Date.Month; } unsigned Dat_GetCurrentYear (void) { return Dat_Time.Now.Date.Year; } /*****************************************************************************/ /************** Get time to generate/send page in microseconds ***************/ /*****************************************************************************/ long Dat_GetTimeGenerationInMicroseconds (void) { return Dat_Time.TimeGenerationInMicroseconds; } long Dat_GetTimeSendInMicroseconds (void) { return Dat_Time.TimeSendInMicroseconds; } /*****************************************************************************/ /**************** Compute the time used to generate the page *****************/ /*****************************************************************************/ void Dat_ComputeTimeToGeneratePage (void) { struct timezone tz; if (gettimeofday (&Dat_Time.tvPageCreated, &tz)) // Error in gettimeofday Dat_Time.TimeGenerationInMicroseconds = 0L; else Dat_Time.TimeGenerationInMicroseconds = (long) ((Dat_Time.tvPageCreated.tv_sec - Dat_Time.tvStart.tv_sec) * 1000000L + Dat_Time.tvPageCreated.tv_usec - Dat_Time.tvStart.tv_usec); } /*****************************************************************************/ /****************** Compute the time used to send the page *******************/ /*****************************************************************************/ void Dat_ComputeTimeToSendPage (void) { struct timeval tvPageSent; struct timezone tz; if (gettimeofday (&tvPageSent, &tz)) // Error in gettimeofday Dat_Time.TimeSendInMicroseconds = 0; else { if (tvPageSent.tv_usec < Dat_Time.tvPageCreated.tv_usec) { tvPageSent.tv_sec--; tvPageSent.tv_usec += 1000000; } Dat_Time.TimeSendInMicroseconds = (tvPageSent.tv_sec - Dat_Time.tvPageCreated.tv_sec) * 1000000L + tvPageSent.tv_usec - Dat_Time.tvPageCreated.tv_usec; } } /*****************************************************************************/ /************** Write the time to generate and send the page *****************/ /*****************************************************************************/ void Dat_WriteTimeToGenerateAndSendPage (void) { extern const char *Txt_PAGE1_Page_generated_in; extern const char *Txt_PAGE2_and_sent_in; char StrTimeGenerationInMicroseconds[Dat_MAX_BYTES_TIME + 1]; char StrTimeSendInMicroseconds[Dat_MAX_BYTES_TIME + 1]; Dat_WriteTime (StrTimeGenerationInMicroseconds,Dat_GetTimeGenerationInMicroseconds ()); Dat_WriteTime (StrTimeSendInMicroseconds ,Dat_GetTimeSendInMicroseconds ()); HTM_TxtF ("%s %s %s %s", Txt_PAGE1_Page_generated_in,StrTimeGenerationInMicroseconds, Txt_PAGE2_and_sent_in,StrTimeSendInMicroseconds); } /*****************************************************************************/ /********* Write time (given in microseconds) depending on amount ************/ /*****************************************************************************/ void Dat_WriteTime (char Str[Dat_MAX_BYTES_TIME],long TimeInMicroseconds) { if (TimeInMicroseconds < 1000L) snprintf (Str,Dat_MAX_BYTES_TIME + 1,"%ld µs",TimeInMicroseconds); else if (TimeInMicroseconds < 1000000L) snprintf (Str,Dat_MAX_BYTES_TIME + 1,"%ld ms",TimeInMicroseconds / 1000); else if (TimeInMicroseconds < (60 * 1000000L)) snprintf (Str,Dat_MAX_BYTES_TIME + 1,"%.1f s", (double) TimeInMicroseconds / 1E6); else snprintf (Str,Dat_MAX_BYTES_TIME + 1,"%ld min, %ld s", TimeInMicroseconds / (60 * 1000000L), (TimeInMicroseconds / 1000000L) % 60); } /*****************************************************************************/ /******************************** Reset date *********************************/ /*****************************************************************************/ void Dat_ResetDate (struct Dat_Date *Date) { Date->Day = 0; Date->Month = 0; Date->Year = 0; Date->Week = 0; Date->YYYYMMDD[0] = '\0'; } void Dat_ResetHour (struct Dat_Hour *Hour) { Hour->Hour = 0; Hour->Minute = 0; } /*****************************************************************************/ /************** 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; /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Dates, Dat_PutIconsDateFormat,NULL, Hlp_PROFILE_Settings_dates,Box_NOT_CLOSABLE); /***** Form with list of options *****/ Frm_BeginForm (ActChgDatFmt); /***** Begin list *****/ HTM_UL_Begin ("class=\"LIST_LEFT\""); for (Format = (Dat_Format_t) 0; Format <= (Dat_Format_t) (Dat_NUM_OPTIONS_FORMAT - 1); Format++) { if (Format == Gbl.Prefs.DateFormat) HTM_LI_Begin ("class=\"DAT_STRONG_%s BG_HIGHLIGHT\"", The_GetSuffix ()); else HTM_LI_Begin ("class=\"DAT_%s\"", The_GetSuffix ()); HTM_LABEL_Begin (NULL); HTM_INPUT_RADIO ("DateFormat",HTM_SUBMIT_ON_CLICK, " value=\"%u\"%s", (unsigned) Format, Format == Gbl.Prefs.DateFormat ? " checked=\"checked\"" : ""); Dat_PutSpanDateFormat (Format); Dat_PutScriptDateFormat (Format); HTM_LABEL_End (); HTM_LI_End (); } /***** End list *****/ HTM_UL_End (); /***** End form *****/ Frm_EndForm (); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /*************** Put contextual icons in date-format setting *****************/ /*****************************************************************************/ static void Dat_PutIconsDateFormat (__attribute__((unused)) void *Args) { /***** Put icon to show a figure *****/ Fig_PutIconToShowFigure (Fig_DATE_FORMAT); } /*****************************************************************************/ /******* Put script to write current date-time in a given date format ********/ /*****************************************************************************/ void Dat_PutSpanDateFormat (Dat_Format_t Format) { HTM_SPAN_Begin ("id=\"date_format_%u\"",(unsigned) Format); HTM_SPAN_End (); } void Dat_PutScriptDateFormat (Dat_Format_t Format) { char *Id; if (asprintf (&Id,"date_format_%u",(unsigned) Format) < 0) Err_NotEnoughMemoryExit (); Dat_WriteLocalDateHMSFromUTC (Id,Dat_GetStartExecutionTimeUTC (), Format,Dat_SEPARATOR_NONE, false,true,false,0x0); free (Id); } /*****************************************************************************/ /***************************** Change date format ****************************/ /*****************************************************************************/ void Dat_ChangeDateFormat (void) { /***** Get param with date format *****/ Gbl.Prefs.DateFormat = Dat_GetParDateFormat (); /***** Store date format in database *****/ if (Gbl.Usrs.Me.Logged) Set_DB_UpdateMySettingsAboutDateFormat (Gbl.Prefs.DateFormat); /***** Set settings from current IP *****/ Set_SetSettingsFromIP (); } /*****************************************************************************/ /********************** Get parameter with date format ***********************/ /*****************************************************************************/ static Dat_Format_t Dat_GetParDateFormat (void) { return (Dat_Format_t) Par_GetParUnsignedLong ("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 and convert current time *************************/ /*****************************************************************************/ void Dat_GetAndConvertCurrentDateTime (void) { struct tm *tm_ptr; time_t t = Dat_GetStartExecutionTimeUTC (); /***** Convert current local time to a struct tblock *****/ tm_ptr = Dat_GetLocalTimeFromClock (&t); Dat_Time.Now.Date.Year = tm_ptr->tm_year + 1900; Dat_Time.Now.Date.Month = tm_ptr->tm_mon + 1; Dat_Time.Now.Date.Day = tm_ptr->tm_mday; Dat_Time.Now.Time.Hour = tm_ptr->tm_hour; Dat_Time.Now.Time.Minute = tm_ptr->tm_min; Dat_Time.Now.Time.Second = tm_ptr->tm_sec; /***** Initialize current date in format YYYYMMDD *****/ snprintf (Dat_Time.Now.Date.YYYYMMDD,sizeof (Dat_Time.Now.Date.YYYYMMDD), "%04u%02u%02u", Dat_Time.Now.Date.Year,Dat_Time.Now.Date.Month,Dat_Time.Now.Date.Day); /***** Initialize current time in format YYYYMMDDHHMMSS *****/ snprintf (Dat_Time.Now.YYYYMMDDHHMMSS,sizeof (Dat_Time.Now.YYYYMMDDHHMMSS), "%04u%02u%02u%02u%02u%02u", Dat_Time.Now.Date.Year,Dat_Time.Now.Date.Month ,Dat_Time.Now.Date.Day, Dat_Time.Now.Time.Hour,Dat_Time.Now.Time.Minute,Dat_Time.Now.Time.Second); } /*****************************************************************************/ /************ 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 Dat_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,sizeof (Date->YYYYMMDD) - 1); 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 *****/ /* Begin container */ HTM_DIV_Begin ("id=\"current_date\""); /* Month with link to calendar */ HTM_DIV_Begin ("id=\"current_month\""); Frm_BeginForm (ActSeeCal); HTM_BUTTON_Submit_Begin (Txt_Show_calendar, "class=\"BT_LINK CURRENT_MONTH\""); HTM_SPAN_Begin ("id=\"current_month_txt\""); // JavaScript will write HTML here HTM_SPAN_End (); HTM_BUTTON_End (); Frm_EndForm (); HTM_DIV_End (); /* Day with link to agenda (if I am logged) */ HTM_DIV_Begin ("id=\"current_day\""); if (Gbl.Usrs.Me.Logged) { Frm_BeginForm (ActSeeMyAgd); HTM_BUTTON_Submit_Begin (Txt_Show_agenda, "class=\"BT_LINK CURRENT_DAY\""); } HTM_SPAN_Begin ("id=\"current_day_txt\""); // JavaScript will write HTML here HTM_SPAN_End (); if (Gbl.Usrs.Me.Logged) { HTM_BUTTON_End (); Frm_EndForm (); } HTM_DIV_End (); /* Time */ HTM_DIV_Begin ("id=\"current_time\""); // JavaScript will write HTML here HTM_DIV_End (); /* End container */ HTM_DIV_End (); /* Write script to draw the month */ HTM_SCRIPT_Begin (NULL,NULL); HTM_TxtF ("secondsSince1970UTC = %ld;\n" "writeLocalClock();\n", (long) Dat_GetStartExecutionTimeUTC ()); HTM_SCRIPT_End (); } /*****************************************************************************/ /***************** 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) Err_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 (const struct Dat_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; } } /*****************************************************************************/ /**************************** Get start/end date *****************************/ /*****************************************************************************/ time_t Dat_GetRangeTimeUTC (Dat_StartEndTime_t StartEndTime) { return Dat_Time.Range.TimeUTC[StartEndTime]; } struct Dat_Date *Dat_GetRangeDate (Dat_StartEndTime_t StartEndTime) { return &Dat_Time.Range.DateTime[StartEndTime].Date; } /*****************************************************************************/ /*************** Show forms to enter initial and ending dates ****************/ /*****************************************************************************/ void Dat_PutFormStartEndClientLocalDateTimesWithYesterdayToday (const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME]) { extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; extern const char *Txt_Yesterday; extern const char *Txt_Today; unsigned CurrentYear = Dat_GetCurrentYear (); /***** Start date-time *****/ HTM_TR_Begin (NULL); /* Label */ Frm_LabelColumn ("RM","",Txt_START_END_TIME[Dat_STR_TIME]); /* Data (date-time) */ HTM_TD_Begin ("class=\"LM\""); Dat_WriteFormClientLocalDateTimeFromTimeUTC ("Start", Dat_STR_TIME, Dat_Time.Range.TimeUTC[Dat_STR_TIME], Cfg_LOG_START_YEAR, CurrentYear, Dat_FORM_SECONDS_ON, SetHMS[Dat_STR_TIME], HTM_DONT_SUBMIT_ON_CHANGE); /* "Yesterday" and "Today" buttons */ HTM_NBSP (); HTM_INPUT_BUTTON ("Yesterday",Txt_Yesterday, "onclick=\"setDateToYesterday('Start','End');\""); HTM_INPUT_BUTTON ("Today",Txt_Today, "onclick=\"setDateToToday('Start','End');\""); HTM_TD_End (); HTM_TR_End (); /***** End date-time *****/ HTM_TR_Begin (NULL); /* Label */ Frm_LabelColumn ("RM","",Txt_START_END_TIME[Dat_END_TIME]); /* Data (date-time) */ HTM_TD_Begin ("class=\"LM\""); Dat_WriteFormClientLocalDateTimeFromTimeUTC ("End", Dat_END_TIME, Dat_Time.Range.TimeUTC[Dat_END_TIME], Cfg_LOG_START_YEAR, CurrentYear, Dat_FORM_SECONDS_ON, SetHMS[Dat_END_TIME], HTM_DONT_SUBMIT_ON_CHANGE); HTM_TD_End (); HTM_TR_End (); } /*****************************************************************************/ /************* Show forms to enter initial and ending date-times *************/ /*****************************************************************************/ void Dat_PutFormStartEndClientLocalDateTimes (const time_t TimeUTC[Dat_NUM_START_END_TIME], Dat_FormSeconds FormSeconds, const Dat_SetHMS SetHMS[Dat_NUM_START_END_TIME]) { extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME]; Dat_StartEndTime_t StartEndTime; const char *Id[Dat_NUM_START_END_TIME] = { [Dat_STR_TIME] = "Start", [Dat_END_TIME] = "End", }; for (StartEndTime = Dat_STR_TIME; StartEndTime <= Dat_END_TIME; StartEndTime++) { /***** Date-time *****/ HTM_TR_Begin (NULL); /* Label */ Frm_LabelColumn ("RM","",Txt_START_END_TIME[StartEndTime]); /* Data */ HTM_TD_Begin ("class=\"LM\""); Dat_WriteFormClientLocalDateTimeFromTimeUTC (Id[StartEndTime], StartEndTime, TimeUTC[StartEndTime], Cfg_LOG_START_YEAR, Dat_GetCurrentYear () + 1, FormSeconds, SetHMS[StartEndTime], // Set hour, minute and second? HTM_DONT_SUBMIT_ON_CHANGE); HTM_TD_End (); HTM_TR_End (); } } /*****************************************************************************/ /************************* Show a form to enter a date ***********************/ /*****************************************************************************/ void Dat_WriteFormClientLocalDateTimeFromTimeUTC (const char *Id, Dat_StartEndTime_t StartEndTime, time_t TimeUTC, unsigned FirstYear, unsigned LastYear, Dat_FormSeconds FormSeconds, Dat_SetHMS SetHMS, HTM_SubmitOnChange_t SubmitOnChange) { extern const char *Txt_MONTHS_SMALL[12]; unsigned Day; unsigned Month; unsigned Year; unsigned Hour; unsigned Minute; unsigned Second; static unsigned MinutesIInterval[Dat_NUM_FORM_SECONDS] = { [Dat_FORM_SECONDS_OFF] = 5, [Dat_FORM_SECONDS_ON ] = 1, }; static const char *Format[Dat_NUM_SET_HMS] = { [Dat_HMS_DO_NOT_SET] = NULL, [Dat_HMS_TO_000000 ] = "setHMSTo000000('%s');", // Set HH:MM:SS form selectors to 00:00:00 [Dat_HMS_TO_235959 ] = "setHMSTo235959('%s');", // Set HH:MM:SS form selectors to 23:59:59 }; char *FuncsYearMonth; char *FuncDayHMS; char *IdTimeUTC; /***** Build strings for onchange functions *****/ if (asprintf (&FuncsYearMonth,"adjustDateForm('%s');" "setUTCFromLocalDateTimeForm('%s');", Id,Id) < 0) Err_NotEnoughMemoryExit (); if (asprintf (&FuncDayHMS,"setUTCFromLocalDateTimeForm('%s');", Id) < 0) Err_NotEnoughMemoryExit (); /***** Begin table *****/ HTM_TABLE_Begin ("DATE_RANGE"); HTM_TR_Begin (NULL); /***** Year *****/ HTM_TD_Begin ("class=\"RM\""); HTM_SELECT_Begin (SubmitOnChange,FuncsYearMonth, "id=\"%sYear\" name=\"%sYear\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Year = FirstYear; Year <= LastYear; Year++) HTM_OPTION (HTM_Type_UNSIGNED,&Year,false,false, "%u",Year); HTM_SELECT_End (); HTM_TD_End (); /***** Month *****/ HTM_TD_Begin ("class=\"CM\""); HTM_SELECT_Begin (SubmitOnChange,FuncsYearMonth, "id=\"%sMonth\" name=\"%sMonth\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Month = 1; Month <= 12; Month++) HTM_OPTION (HTM_Type_UNSIGNED,&Month,false,false, "%s",Txt_MONTHS_SMALL[Month - 1]); HTM_SELECT_End (); HTM_TD_End (); /***** Day *****/ HTM_TD_Begin ("class=\"LM\""); HTM_SELECT_Begin (SubmitOnChange,FuncDayHMS, "id=\"%sDay\" name=\"%sDay\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Day = 1; Day <= 31; Day++) HTM_OPTION (HTM_Type_UNSIGNED,&Day,false,false, "%u",Day); HTM_SELECT_End (); HTM_TD_End (); /***** Hour *****/ HTM_TD_Begin ("class=\"RM\""); HTM_SELECT_Begin (SubmitOnChange,FuncDayHMS, "id=\"%sHour\" name=\"%sHour\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Hour = 0; Hour <= 23; Hour++) HTM_OPTION (HTM_Type_UNSIGNED,&Hour,false,false, "%02u h",Hour); HTM_SELECT_End (); HTM_TD_End (); /***** Minute *****/ HTM_TD_Begin ("class=\"CM\""); HTM_SELECT_Begin (SubmitOnChange,FuncDayHMS, "id=\"%sMinute\" name=\"%sMinute\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Minute = 0; Minute < 60; Minute += MinutesIInterval[FormSeconds]) HTM_OPTION (HTM_Type_UNSIGNED,&Minute,false,false, "%02u ′",Minute); HTM_SELECT_End (); HTM_TD_End (); /***** Second *****/ if (FormSeconds == Dat_FORM_SECONDS_ON) { HTM_TD_Begin ("class=\"LM\""); HTM_SELECT_Begin (SubmitOnChange,FuncDayHMS, "id=\"%sSecond\" name=\"%sSecond\"" " class=\"INPUT_%s\"", Id,Dat_ParName[StartEndTime], The_GetSuffix ()); for (Second = 0; Second <= 59; Second++) HTM_OPTION (HTM_Type_UNSIGNED,&Second,false,false, "%02u ″",Second); HTM_SELECT_End (); HTM_TD_End (); } /***** End table *****/ HTM_TR_End (); HTM_TABLE_End (); /***** Free strings for onchange functions *****/ free (FuncsYearMonth); free (FuncDayHMS); /***** Hidden field with UTC time (seconds since 1970) used to send time *****/ if (asprintf (&IdTimeUTC,"%sTimeUTC",Id) < 0) Err_NotEnoughMemoryExit (); Par_PutParLong (IdTimeUTC,Dat_ParTimeUTCName[StartEndTime],(long) TimeUTC); free (IdTimeUTC); /***** Script to set selectors to local date and time from UTC time *****/ HTM_SCRIPT_Begin (NULL,NULL); HTM_TxtF ("setLocalDateTimeFormFromUTC('%s',%ld);\n" // Set date-time form from UTC time "adjustDateForm('%s');\n" // Adjust date-time form "setUTCFromLocalDateTimeForm('%s');\n", // Adjust UTC time from date-time form Id,(long) TimeUTC, Id, Id); if (Format[SetHMS]) HTM_TxtF (Format[SetHMS],Id); // Hidden TimeUTC is also adjusted HTM_SCRIPT_End (); } /*****************************************************************************/ /***************** Get an hour-minute time from a form ***********************/ /*****************************************************************************/ time_t Dat_GetTimeUTCFromForm (Dat_StartEndTime_t StartEndTime) { return Par_GetParLong (Dat_ParTimeUTCName[StartEndTime]); } /*****************************************************************************/ /**************** Put a hidden param with time difference ********************/ /**************** between UTC time and client local time, ********************/ /**************** in minutes ********************/ /*****************************************************************************/ void Dat_PutParBrowserTZDiff (void) { Par_PutParString ("BrowserTZName","BrowserTZName",""); Par_PutParLong ("BrowserTZDiff","BrowserTZDiff",0); HTM_SCRIPT_Begin (NULL,NULL); HTM_TxtF ("setTZname('BrowserTZName');" "setTZ('BrowserTZDiff');"); HTM_SCRIPT_End (); } /*****************************************************************************/ /*************************** 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[Cns_MAX_DECIMAL_DIGITS_INT + 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_GetParText ("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')", // row[0] 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_GetParText ("BrowserTZDiff",IntStr,Cns_MAX_DECIMAL_DIGITS_INT); 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 Dat_Date *DateSelected, HTM_SubmitOnChange_t SubmitOnChange,bool Disabled) { extern const char *Txt_MONTHS_SMALL[12]; char *FuncOnChange; unsigned Year; unsigned Month; unsigned Day; unsigned NumDaysSelectedMonth; /***** Begin table *****/ HTM_TABLE_Begin (NULL); HTM_TR_Begin (NULL); if (asprintf (&FuncOnChange,"adjustDateForm('%s');",Id) < 0) Err_NotEnoughMemoryExit (); /***** Year *****/ HTM_TD_Begin ("class=\"CM\""); HTM_SELECT_Begin (SubmitOnChange,FuncOnChange, "id=\"%sYear\" name=\"%sYear\"" " class=\"INPUT_%s\"%s", Id,Id, The_GetSuffix (), Disabled ? " disabled=\"disabled\"" : ""); HTM_OPTION (HTM_Type_STRING,"0",false,false, "-"); for (Year = FirstYear; Year <= LastYear; Year++) HTM_OPTION (HTM_Type_UNSIGNED,&Year, Year == DateSelected->Year,false, "%u",Year); HTM_SELECT_End (); HTM_TD_End (); /***** Month *****/ HTM_TD_Begin ("class=\"CM\""); HTM_SELECT_Begin (SubmitOnChange,FuncOnChange, "id=\"%sMonth\" name=\"%sMonth\"" " class=\"INPUT_%s\"%s", Id,Id, The_GetSuffix (), Disabled ? " disabled=\"disabled\"" : ""); HTM_OPTION (HTM_Type_STRING,"0",false,false, "-"); for (Month = 1; Month <= 12; Month++) HTM_OPTION (HTM_Type_UNSIGNED,&Month, Month == DateSelected->Month,false, "%s",Txt_MONTHS_SMALL[Month - 1]); HTM_SELECT_End (); HTM_TD_End (); free (FuncOnChange); /***** Day *****/ HTM_TD_Begin ("class=\"CM\""); HTM_SELECT_Begin (SubmitOnChange,NULL, "id=\"%sDay\" name=\"%sDay\"" " class=\"INPUT_%s\"%s", Id,Id, The_GetSuffix (), Disabled ? " disabled=\"disabled\"" : ""); HTM_OPTION (HTM_Type_STRING,"0",false,false, "-"); NumDaysSelectedMonth = (DateSelected->Month == 0) ? 31 : ((DateSelected->Month == 2) ? Dat_GetNumDaysFebruary (DateSelected->Year) : Dat_NumDaysMonth[DateSelected->Month]); for (Day = 1; Day <= NumDaysSelectedMonth; Day++) HTM_OPTION (HTM_Type_UNSIGNED,&Day, Day == DateSelected->Day,false, "%u",Day); HTM_SELECT_End (); HTM_TD_End (); /***** End table *****/ HTM_TR_End (); HTM_TABLE_End (); } /*****************************************************************************/ /*************************** Get a date from a form **************************/ /*****************************************************************************/ void Dat_GetDateFromForm (const char *ParNameDay,const char *ParNameMonth,const char *ParNameYear, unsigned *Day,unsigned *Month,unsigned *Year) { /**** Get day ****/ *Day = (unsigned) Par_GetParUnsignedLong (ParNameDay ,1,31,0); /**** Get month ****/ *Month = (unsigned) Par_GetParUnsignedLong (ParNameMonth,1,12,0); /**** Get year ****/ *Year = (unsigned) Par_GetParUnsignedLong (ParNameYear ,0,UINT_MAX,0); } /*****************************************************************************/ /******* Set initial date to distant past and end date to current date *******/ /*****************************************************************************/ void Dat_SetIniEndDatesToPastAndNow (void) { Dat_Time.Range.TimeUTC[Dat_STR_TIME] = (time_t) 0; Dat_Time.Range.TimeUTC[Dat_END_TIME] = Dat_GetStartExecutionTimeUTC (); } /*****************************************************************************/ /************ Set end date to current date ************/ /************ and set initial date to end date minus several days ************/ /*****************************************************************************/ void Dat_SetIniEndDatesToRecentWeeks (void) { Dat_Time.Range.TimeUTC[Dat_END_TIME] = Dat_GetStartExecutionTimeUTC (); Dat_Time.Range.TimeUTC[Dat_STR_TIME] = Dat_Time.Range.TimeUTC[Dat_END_TIME] - ((Cfg_DAYS_IN_RECENT_LOG - 1) * 24 * 60 * 60); } /*****************************************************************************/ /******** Write parameters of initial and final dates *****/ /*****************************************************************************/ void Dat_WriteParsIniEndDates (void) { Par_PutParUnsigned (NULL,Dat_ParTimeUTCName[Dat_STR_TIME],Dat_Time.Range.TimeUTC[Dat_STR_TIME]); Par_PutParUnsigned (NULL,Dat_ParTimeUTCName[Dat_END_TIME],Dat_Time.Range.TimeUTC[Dat_END_TIME]); } /*****************************************************************************/ /************************** Get initial and end dates ************************/ /*****************************************************************************/ void Dat_GetIniEndDatesFromForm (void) { struct tm tm; struct tm *tm_ptr; /***** Get initial date *****/ Dat_Time.Range.TimeUTC[Dat_STR_TIME] = Dat_GetTimeUTCFromForm (Dat_STR_TIME); if (Dat_Time.Range.TimeUTC[Dat_STR_TIME]) /* Convert time UTC to a local date */ tm_ptr = Dat_GetLocalTimeFromClock (&Dat_Time.Range.TimeUTC[Dat_STR_TIME]); else // Dat_Time.Range.TimeUTC[Dat_STR_TIME] == 0 ==> initial date not specified { tm.tm_year = Cfg_LOG_START_YEAR - 1900; tm.tm_mon = 0; // January tm.tm_mday = 1; tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; tm.tm_isdst = -1; // a negative value means that mktime() should // (use timezone information and system databases to) // attempt to determine whether DST // is in effect at the specified time. if ((Dat_Time.Range.TimeUTC[Dat_STR_TIME] = mktime (&tm)) < 0) Dat_Time.Range.TimeUTC[Dat_STR_TIME] = (time_t) 0; tm_ptr = &tm; } Dat_Time.Range.DateTime[Dat_STR_TIME].Date.Year = tm_ptr->tm_year + 1900; Dat_Time.Range.DateTime[Dat_STR_TIME].Date.Month = tm_ptr->tm_mon + 1; Dat_Time.Range.DateTime[Dat_STR_TIME].Date.Day = tm_ptr->tm_mday; Dat_Time.Range.DateTime[Dat_STR_TIME].Time.Hour = tm_ptr->tm_hour; Dat_Time.Range.DateTime[Dat_STR_TIME].Time.Minute = tm_ptr->tm_min; Dat_Time.Range.DateTime[Dat_STR_TIME].Time.Second = tm_ptr->tm_sec; /***** Get end date *****/ Dat_Time.Range.TimeUTC[Dat_END_TIME] = Dat_GetTimeUTCFromForm (Dat_END_TIME); if (Dat_Time.Range.TimeUTC[Dat_END_TIME] == 0) // Dat_Time.Range.TimeUTC[Dat_END_TIME] == 0 ==> end date not specified Dat_Time.Range.TimeUTC[Dat_END_TIME] = Dat_GetStartExecutionTimeUTC (); /* Convert current time UTC to a local date */ tm_ptr = Dat_GetLocalTimeFromClock (&Dat_Time.Range.TimeUTC[Dat_END_TIME]); Dat_Time.Range.DateTime[Dat_END_TIME].Date.Year = tm_ptr->tm_year + 1900; Dat_Time.Range.DateTime[Dat_END_TIME].Date.Month = tm_ptr->tm_mon + 1; Dat_Time.Range.DateTime[Dat_END_TIME].Date.Day = tm_ptr->tm_mday; Dat_Time.Range.DateTime[Dat_END_TIME].Time.Hour = tm_ptr->tm_hour; Dat_Time.Range.DateTime[Dat_END_TIME].Time.Minute = tm_ptr->tm_min; Dat_Time.Range.DateTime[Dat_END_TIME].Time.Second = tm_ptr->tm_sec; } /*****************************************************************************/ /****************** Write a UTC date into RFC 822 format *********************/ /*****************************************************************************/ // tm must hold a UTC date void Dat_WriteRFC822DateFromTM (FILE *File,struct tm *tm_ptr) { static const char *StrDayOfWeek[7] = { [0] = "Sun", [1] = "Mon", [2] = "Tue", [3] = "Wed", [4] = "Thu", [5] = "Fri", [6] = "Sat", }; static const char *StrMonth[12] = { [ 0] = "Jan", [ 1] = "Feb", [ 2] = "Mar", [ 3] = "Apr", [ 4] = "May", [ 5] = "Jun", [ 6] = "Jul", [ 7] = "Aug", [ 8] = "Sep", [ 9] = "Oct", [10] = "Nov", [11] = "Dec", }; fprintf (File,"%s, %d %s %d %02d:%02d:%02d UT", StrDayOfWeek[tm_ptr->tm_wday], tm_ptr->tm_mday, StrMonth[tm_ptr->tm_mon], tm_ptr->tm_year + 1900, tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec); } /*****************************************************************************/ /************** Compute the subsequent date of a given date ******************/ /*****************************************************************************/ void Dat_GetDateAfter (struct Dat_Date *Date,struct Dat_Date *SubsequentDate) { unsigned NumDaysInMonth = (Date->Month == 2) ? Dat_GetNumDaysFebruary (Date->Year) : Dat_NumDaysMonth[Date->Month]; if (Date->Day == NumDaysInMonth) { if (Date->Month == 12) { SubsequentDate->Year = Date->Year + 1; SubsequentDate->Month = 1; SubsequentDate->Day = 1; } else { SubsequentDate->Year = Date->Year; SubsequentDate->Month = Date->Month + 1; SubsequentDate->Day = 1; } } else { SubsequentDate->Year = Date->Year; SubsequentDate->Month = Date->Month; SubsequentDate->Day = Date->Day + 1; } } /*****************************************************************************/ /************** Compute the preceding date of a given date *******************/ /*****************************************************************************/ void Dat_GetDateBefore (struct Dat_Date *Date,struct Dat_Date *PrecedingDate) { if (Date->Day == 1) { if (Date->Month == 1) { PrecedingDate->Year = Date->Year - 1; PrecedingDate->Month = 12; PrecedingDate->Day = 31; } else { PrecedingDate->Year = Date->Year; PrecedingDate->Month = Date->Month - 1; PrecedingDate->Day = (PrecedingDate->Month == 2) ? Dat_GetNumDaysFebruary (PrecedingDate->Year) : Dat_NumDaysMonth[PrecedingDate->Month]; } } else { PrecedingDate->Year = Date->Year; PrecedingDate->Month = Date->Month; PrecedingDate->Day = Date->Day - 1; } } /*****************************************************************************/ /**************** Compute the week before to a given week ********************/ /*****************************************************************************/ void Dat_GetWeekBefore (struct Dat_Date *Date,struct Dat_Date *PrecedingDate) { if (Date->Week == 1) { PrecedingDate->Year = Date->Year - 1; PrecedingDate->Week = Dat_GetNumWeeksInYear (PrecedingDate->Year); } else { PrecedingDate->Year = Date->Year; PrecedingDate->Week = Date->Week - 1; } } /*****************************************************************************/ /*************** Compute the month before to a given month *******************/ /*****************************************************************************/ void Dat_GetMonthBefore (struct Dat_Date *Date,struct Dat_Date *PrecedingDate) { if (Date->Month == 1) { PrecedingDate->Year = Date->Year - 1; PrecedingDate->Month = 12; } else { PrecedingDate->Year = Date->Year; PrecedingDate->Month = Date->Month - 1; } } /*****************************************************************************/ /**************** Compute the year before to a given year ********************/ /*****************************************************************************/ void Dat_GetYearBefore (struct Dat_Date *Date,struct Dat_Date *PrecedingDate) { PrecedingDate->Year = Date->Year - 1; } /*****************************************************************************/ /************** Compute the number of days beteen two dates ******************/ /*****************************************************************************/ // If the dates are the same, return 1 // If the old date is the day before the new data, return 2 // ... unsigned Dat_GetNumDaysBetweenDates (struct Dat_Date *DateStr, struct Dat_Date *DateEnd) { int DiffDays; unsigned Year; /***** If initial year is less than end year, return 0 (actually the difference in days should be negative) *****/ if (DateStr->Year > DateEnd->Year) return 0; /***** Initial year is less or equal than end year ==> compute difference in days *****/ DiffDays = (int) Dat_GetDayOfYear (DateEnd) - (int) Dat_GetDayOfYear (DateStr) + 1; for (Year = DateStr->Year; Year < DateEnd->Year; Year++) DiffDays += (int) Dat_GetNumDaysInYear (Year); return (DiffDays > 0) ? (unsigned) DiffDays : 0; } /*****************************************************************************/ /*************** Compute the number of weeks between two dates ***************/ /*****************************************************************************/ // If the two dates are in the same week, return 1 unsigned Dat_GetNumWeeksBetweenDates (struct Dat_Date *DateStr, struct Dat_Date *DateEnd) { int DiffWeeks; unsigned Year; /***** If initial year is lower than the ending year, return 0 (actually the difference should be negative) *****/ if (DateStr->Year > DateEnd->Year) return 0; /***** Initial year is lower or equal to ending year ==> compute difference in weeks *****/ DiffWeeks = (int) DateEnd->Week - (int) DateStr->Week + 1; for (Year = DateStr->Year; Year < DateEnd->Year; Year++) DiffWeeks += (int) Dat_GetNumWeeksInYear (Year); return (DiffWeeks > 0) ? (unsigned) DiffWeeks : 0; } /*****************************************************************************/ /************* Compute the number of months between two dates ****************/ /*****************************************************************************/ // If the two dates are in the same month, return 1 unsigned Dat_GetNumMonthsBetweenDates (struct Dat_Date *DateStr, struct Dat_Date *DateEnd) { int DiffMonths; /***** Compute the difference in months *****/ DiffMonths = ((int) DateEnd->Year - (int) DateStr->Year) * 12 + (int) DateEnd->Month - (int) DateStr->Month + 1; return (DiffMonths > 0) ? (unsigned) DiffMonths : 0; } /*****************************************************************************/ /************** Compute the number of years between two dates ****************/ /*****************************************************************************/ // If the two dates are in the same year, return 1 unsigned Dat_GetNumYearsBetweenDates (struct Dat_Date *DateStr, struct Dat_Date *DateEnd) { int DiffYears; /***** Compute the difference in years *****/ DiffYears = (int) DateEnd->Year - (int) DateStr->Year + 1; return (DiffYears > 0) ? (unsigned) DiffYears : 0; } /*****************************************************************************/ /*************** Compute the number of days in a given year ******************/ /*****************************************************************************/ #define NUM_DAYS_YEAR_EXCEPT_FEBRUARY (365 - 28) unsigned Dat_GetNumDaysInYear (unsigned Year) { return NUM_DAYS_YEAR_EXCEPT_FEBRUARY + Dat_GetNumDaysFebruary (Year); } /*****************************************************************************/ /****************** Return the number of days of february ********************/ /*****************************************************************************/ unsigned Dat_GetNumDaysFebruary (unsigned Year) { return (Dat_GetIfLeapYear (Year) ? 29 : 28); } /*****************************************************************************/ /************************* Return true if year is leap ***********************/ /*****************************************************************************/ bool Dat_GetIfLeapYear (unsigned Year) { return (Year % 4 == 0) && ((Year % 100 != 0) || (Year % 400 == 0)); } /*****************************************************************************/ /******* Compute the number of weeks starting in monday of a given year ******/ /*****************************************************************************/ // A week starting on monday is considered belonging to a year only if it has most of its days (4 to 7) belonging to that year // A year may have 52 (usually) or 53 of those weeks unsigned Dat_GetNumWeeksInYear (unsigned Year) { unsigned December31DayOfWeek = Dat_GetDayOfWeek (Year,12,31); // From 0 to 6 if (December31DayOfWeek == 3) return 53; if (December31DayOfWeek == 4) if (Dat_GetIfLeapYear (Year)) return 53; return 52; } /*****************************************************************************/ /***************** Compute day of the week from a given date *****************/ /*****************************************************************************/ // Return 0 for monday, 1 for tuesday,... 6 for sunday unsigned Dat_GetDayOfWeek (unsigned Year,unsigned Month,unsigned Day) { struct tm tm; struct tm *tm_ptr; time_t t; /***** Create clock in UNIX time from date *****/ tm.tm_year = Year - 1900; tm.tm_mon = Month - 1; tm.tm_mday = Day; tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; tm.tm_isdst = -1; // a negative value means that mktime() should // (use timezone information and system databases to) // attempt to determine whether DST // is in effect at the specified time. if ((t = mktime (&tm)) < 0) return 0; /***** Compute local time, adjusting day of week *****/ tm_ptr = Dat_GetLocalTimeFromClock (&t); return (unsigned) tm_ptr->tm_wday; } /* Alternative: if (Month <= 2) { Month += 12; Year--; } return ( ( ( Day + Month*2 + (Month*3 + 3) / 5 + Year + Year/4 - Year/100 + Year/400 + 2 ) % 7 ) + 5 ) % 7; Code generated by gcc for the alternative: 0000000000001810 : 1810: 83 fe 02 cmp $0x2,%esi 1813: 77 06 ja 181b 1815: 83 c6 0c add $0xc,%esi 1818: 83 ef 01 sub $0x1,%edi 181b: 8d 44 17 02 lea 0x2(%rdi,%rdx,1),%eax 181f: ba 1f 85 eb 51 mov $0x51eb851f,%edx 1824: 8d 0c 70 lea (%rax,%rsi,2),%ecx 1827: 89 f8 mov %edi,%eax 1829: c1 e8 02 shr $0x2,%eax 182c: 01 c1 add %eax,%ecx 182e: 89 f8 mov %edi,%eax 1830: bf 25 49 92 24 mov $0x24924925,%edi 1835: f7 e2 mul %edx 1837: 89 d0 mov %edx,%eax 1839: c1 ea 05 shr $0x5,%edx 183c: c1 e8 07 shr $0x7,%eax 183f: 01 c1 add %eax,%ecx 1841: 8d 44 76 03 lea 0x3(%rsi,%rsi,2),%eax 1845: 29 d1 sub %edx,%ecx 1847: ba cd cc cc cc mov $0xcccccccd,%edx 184c: f7 e2 mul %edx 184e: c1 ea 02 shr $0x2,%edx 1851: 01 d1 add %edx,%ecx 1853: 89 c8 mov %ecx,%eax 1855: 89 ce mov %ecx,%esi 1857: f7 e7 mul %edi 1859: 29 d6 sub %edx,%esi 185b: d1 ee shr %esi 185d: 01 f2 add %esi,%edx 185f: c1 ea 02 shr $0x2,%edx 1862: 8d 04 d5 00 00 00 00 lea 0x0(,%rdx,8),%eax 1869: 29 d0 sub %edx,%eax 186b: 29 c1 sub %eax,%ecx 186d: 83 c1 05 add $0x5,%ecx 1870: 89 c8 mov %ecx,%eax 1872: f7 e7 mul %edi 1874: 89 c8 mov %ecx,%eax 1876: 29 d0 sub %edx,%eax 1878: d1 e8 shr %eax 187a: 01 d0 add %edx,%eax 187c: c1 e8 02 shr $0x2,%eax 187f: 8d 14 c5 00 00 00 00 lea 0x0(,%rax,8),%edx 1886: 29 c2 sub %eax,%edx 1888: 29 d1 sub %edx,%ecx 188a: 89 c8 mov %ecx,%eax 188c: c3 retq 188d: 0f 1f 00 nopl (%rax) Understanding the code generated by gcc: 0000000000001810 : 1810: 83 fe 02 cmp $2,Month 1813: 77 06 ja continue 1815: 83 c6 0c add $12,Month 1818: 83 ef 01 sub $1,Year continue: 181b: 8d 44 17 02 lea 2(Year,Day,1),Suma1 // SumAux = Day + Year + 2 181f: ba 1f 85 eb 51 mov $0x51eb851f,%edx // 1374389535 1824: 8d 0c 70 lea (SumAux,Month,2),n // n = Day + Year + 2 + Month*2 1827: 89 f8 mov Year,%eax 1829: c1 e8 02 shr $2,%eax // Year / 4 182c: 01 c1 add %eax,n // n = Day + Year + 2 + Month*2 + Year/4 182e: 89 f8 mov Year,%eax 1830: bf 25 49 92 24 mov $0x24924925,%edi // 613566757 1835: f7 e2 mul %edx // Year * 1374389535 1837: 89 d0 mov %edx,%eax // (Year * 1374389535) / 2^32 = (Year * 2^32 * 2^5 / 100) / 2^32 = (Year * 2^5) / 100 1839: c1 ea 05 shr $5,%edx // (Year * 1374389535) / 2^32 / 2^5 = Year / 100 183c: c1 e8 07 shr $7,%eax // (Year * 1374389535) / 2^32 / 2^7 = Year / 400 183f: 01 c1 add %eax,n // n = Day + Year + 2 + Month*2 + Year/4 + Year/400 1841: 8d 44 76 03 lea 3(Month,Month,2),%eax // Month*3 + 3 1845: 29 d1 sub %edx,n // n = Day + Year + 2 + Month*2 + Year/4 + Year/400 - Year/100 1847: ba cd cc cc cc mov $0xcccccccd,%edx 184c: f7 e2 mul %edx // (Month*3 + 3) * 3435973837 184e: c1 ea 02 shr $2,%edx // (Month*3 + 3) * 3435973837 / 2^32 / 2^2 = (Month*3 + 3) / 5 1851: 01 d1 add %edx,n // n = Day + Year + 2 + Month*2 + Year/4 + Year/400 - Year/100 + (Month*3 + 3) / 5 1853: 89 c8 mov n,%eax 1855: 89 ce mov n,%esi // Algorithm for remainder: https://doc.lagout.org/security/Hackers%20Delight.pdf, page 209 1857: f7 e7 mul %edi // edx = q = M*n/2**32 1859: 29 d6 sub %edx,%esi // esi = t = n - q 185b: d1 ee shr %esi // esi = t = (n - q)/2 185d: 01 f2 add %esi,%edx // edx = t = (n - q)/2 + q = (n + q)/2 185f: c1 ea 02 shr $2,%edx // edx = q = (n + q)/8 = (n+Mn/2**32)/8 = floor(n/7) 1862: 8d 04 d5 00 00 00 00 lea 0(,%rdx,8),%eax // eax = q*8 1869: 29 d0 sub %edx,%eax // eax = q*8-q = q*7 186b: 29 c1 sub %eax,%ecx // ecx = r = n - q*7 186d: 83 c1 05 add $5,Mod // Mod += 5 1870: 89 c8 mov Mod,%eax 1872: f7 e7 mul %edi 1874: 89 c8 mov Mod,%eax 1876: 29 d0 sub %edx,%eax 1878: d1 e8 shr %eax 187a: 01 d0 add %edx,%eax 187c: c1 e8 02 shr $2,%eax 187f: 8d 14 c5 00 00 00 00 lea 0(,%rax,8),%edx 1886: 29 c2 sub %eax,%edx 1888: 29 d1 sub %edx,Mod // Mod % 7 188a: 89 c8 mov Mod,%eax 188c: c3 retq 188d: 0f 1f 00 nopl (%rax) */ /*****************************************************************************/ /***************** Compute the day of year (from 1 to 366) *******************/ /*****************************************************************************/ unsigned Dat_GetDayOfYear (struct Dat_Date *Date) { unsigned Month; unsigned DayYear; /* Compute day of year */ for (Month = 1, DayYear = 0; Month < Date->Month; Month++) DayYear += (Month == 2) ? Dat_GetNumDaysFebruary (Date->Year) : Dat_NumDaysMonth[Month]; DayYear += Date->Day; return DayYear; } /*****************************************************************************/ /******** Compute Week (and possibly updated Year) from a given date *********/ /*****************************************************************************/ /* The weeks always are counted from monday to sunday. 01/01/2006 was sunday => it is counted in the week 52 of 2005, which goes from the 26/12/2005 (monday) until the 01/01/2006 (sunday). Week 1 of 2006 goes from the 02/01/2006 (monday) until the 08/01/2006 (sunday) */ void Dat_CalculateWeekOfYear (struct Dat_Date *Date) { /* If January 1 of Year is... | ...then first week of Year starts the day | -----------------------------+--------------------------------------------+ (0) Monday | January, 1 of Year | (1) Tuesday | December, 31 of Year-1 | (2) Wednesday | December, 30 of Year-1 | (3) Thursday | December, 29 of Year-1 | (4) Friday | January, 4 of Year | (5) Saturday | January, 5 of Year | (6) Sunday | January, 6 of Year | If December 31 of Year is... | ...then last week of Year ends the day | -----------------------------+--------------------------------------------+ (0) Monday | December, 30 of Year | (1) Tuesday | December, 29 of Year | (2) Wednesday | December, 28 of Year | (3) Thursday | January, 3 of Year + 1 | (4) Friday | January, 4 of Year + 1 | (5) Saturday | January, 5 of Year + 1 | (6) Sunday | December, 31 of Year | */ int DayThatFirstWeekStarts[7] = {1,0,-1,-2,4,3,2}; unsigned DayThatLastWeekEnds[7] = {30,29,28,34,33,32,31}; unsigned January1DayOfWeek = Dat_GetDayOfWeek (Date->Year, 1, 1); // From 0 to 6 unsigned December31DayOfWeek = Dat_GetDayOfWeek (Date->Year,12,31); // From 0 to 6 if (Date->Month == 1 && (int) Date->Day < DayThatFirstWeekStarts[January1DayOfWeek]) // Week is the last week of the year before this { Date->Year--; Date->Week = Dat_GetNumWeeksInYear (Date->Year); } else if (Date->Month == 12 && Date->Day > DayThatLastWeekEnds[December31DayOfWeek]) // Week is the first week of the year after this { Date->Year++; Date->Week = 1; } else // Week corresponds to this year Date->Week = ((int) Dat_GetDayOfYear (Date) - DayThatFirstWeekStarts[January1DayOfWeek]) / 7 + 1; } /*****************************************************************************/ /******************* Assign the values of a date to another ******************/ /*****************************************************************************/ void Dat_AssignDate (struct Dat_Date *DateDst,struct Dat_Date *DateSrc) { DateDst->Year = DateSrc->Year; DateDst->Month = DateSrc->Month; DateDst->Day = DateSrc->Day; DateDst->Week = DateSrc->Week; Str_Copy (DateDst->YYYYMMDD,DateSrc->YYYYMMDD,sizeof (DateDst->YYYYMMDD) - 1); } /*****************************************************************************/ /****** Write script to automatically update clocks of connected users *******/ /*****************************************************************************/ void Dat_WriteScriptMonths (void) { extern const char *Txt_MONTHS_SMALL[12]; extern const char *Txt_MONTHS_SMALL_SHORT[12]; unsigned NumMonth; /***** Month names (full) *****/ HTM_Txt ("\tvar Months = ["); for (NumMonth = 0; NumMonth < 12; NumMonth++) { if (NumMonth) HTM_Comma (); HTM_TxtF ("'%s'",Txt_MONTHS_SMALL[NumMonth]); } HTM_Txt ("];\n"); /***** Month names (short) *****/ HTM_Txt ("\tvar MonthsShort = ["); for (NumMonth = 0; NumMonth < 12; NumMonth++) { if (NumMonth) HTM_Comma (); HTM_TxtF ("'%s'",Txt_MONTHS_SMALL_SHORT[NumMonth]); } HTM_Txt ("];\n"); } /*****************************************************************************/ /********* Write time difference in seconds as hours:minutes:seconds *********/ /*****************************************************************************/ void Dat_WriteHoursMinutesSecondsFromSeconds (time_t Seconds) { time_t Hours = Seconds / (60 * 60); time_t Minutes = (Seconds / 60) % 60; Seconds %= 60; if (Hours) HTM_TxtF ("%ld:%02ld′%02ld″",(long) Hours,(long) Minutes,(long) Seconds); else if (Minutes) HTM_TxtF ("%ld′%02ld″",(long) Minutes,(long) Seconds); else HTM_TxtF ("%ld″",(long) Seconds); } /*****************************************************************************/ /******************* Write time as hours:minutes:seconds *********************/ /*****************************************************************************/ void Dat_WriteHoursMinutesSeconds (struct Dat_Time *Time) { if (Time->Hour) HTM_TxtF ("%u:%02u′%02u″",Time->Hour,Time->Minute,Time->Second); else if (Time->Minute) HTM_TxtF ("%u′%02u″",Time->Minute,Time->Second); else HTM_TxtF ("%u″",Time->Second); } /*****************************************************************************/ /**** Write call to JavaScript function to write local date from UTC time ****/ /*****************************************************************************/ void Dat_WriteLocalDateHMSFromUTC (const char *Id,time_t TimeUTC, Dat_Format_t DateFormat,Dat_Separator_t Separator, bool WriteToday,bool WriteDateOnSameDay, bool WriteWeekDay,unsigned WriteHMS) { static const char *SeparatorStr[Dat_NUM_SEPARATORS] = { [Dat_SEPARATOR_NONE ] = "", [Dat_SEPARATOR_COMMA] = ", ", [Dat_SEPARATOR_BREAK] = "
", }; HTM_SCRIPT_Begin (NULL,NULL); HTM_TxtF ("writeLocalDateHMSFromUTC('%s',%ld,%u,'%s',%u,%s,%s,%s,0x%x);", Id,(long) TimeUTC,(unsigned) DateFormat,SeparatorStr[Separator], (unsigned) Gbl.Prefs.Language, WriteToday ? "true" : "false", WriteDateOnSameDay ? "true" : "false", WriteWeekDay ? "true" : "false", WriteHMS); HTM_SCRIPT_End (); } /*****************************************************************************/ /******** Get and show number of users who have chosen a date format *********/ /*****************************************************************************/ void Dat_GetAndShowNumUsrsPerDateFormat (void) { extern const char *Hlp_ANALYTICS_Figures_dates; extern const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES]; extern const char *Txt_Format; extern const char *Txt_Number_of_users; extern const char *Txt_PERCENT_of_users; unsigned Format; char *SubQuery; unsigned NumUsrs[Dat_NUM_OPTIONS_FORMAT]; unsigned NumUsrsTotal = 0; /***** Begin box and table *****/ Box_BoxTableBegin (NULL,Txt_FIGURE_TYPES[Fig_DATE_FORMAT], NULL,NULL, Hlp_ANALYTICS_Figures_dates,Box_NOT_CLOSABLE,2); /***** Heading row *****/ HTM_TR_Begin (NULL); HTM_TH (Txt_Format ,HTM_HEAD_LEFT); HTM_TH (Txt_Number_of_users ,HTM_HEAD_RIGHT); HTM_TH (Txt_PERCENT_of_users,HTM_HEAD_RIGHT); HTM_TR_End (); /***** For each format... *****/ for (Format = (Dat_Format_t) 0; Format <= (Dat_Format_t) (Dat_NUM_OPTIONS_FORMAT - 1); Format++) { /* Get number of users who have chosen this date format from database */ if (asprintf (&SubQuery,"usr_data.DateFormat=%u", (unsigned) Format) < 0) Err_NotEnoughMemoryExit (); NumUsrs[Format] = Usr_DB_GetNumUsrsWhoChoseAnOption (SubQuery); free (SubQuery); /* Update total number of users */ NumUsrsTotal += NumUsrs[Format]; } /***** Write number of users who have chosen each date format *****/ for (Format = (Dat_Format_t) 0; Format <= (Dat_Format_t) (Dat_NUM_OPTIONS_FORMAT - 1); Format++) { HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"LM DAT_STRONG_%s\"", The_GetSuffix ()); Dat_PutSpanDateFormat (Format); Dat_PutScriptDateFormat (Format); HTM_TD_End (); HTM_TD_Begin ("class=\"RM DAT_%s\"", The_GetSuffix ()); HTM_Unsigned (NumUsrs[Format]); HTM_TD_End (); HTM_TD_Begin ("class=\"RM DAT_%s\"", The_GetSuffix ()); HTM_Percentage (NumUsrsTotal ? (double) NumUsrs[Format] * 100.0 / (double) NumUsrsTotal : 0.0); HTM_TD_End (); HTM_TR_End (); } /***** End table and box *****/ Box_BoxTableEnd (); }