void gnc_date_cell_set_value_secs (DateCell *cell, time64 secs) { PopBox *box = cell->cell.gui_private; char buff[DATE_BUF]; gnc_localtime_r (&secs, &(box->date)); qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH, box->date.tm_mday, box->date.tm_mon + 1, box->date.tm_year + 1900); gnc_basic_cell_set_value_internal (&cell->cell, buff); if (!box->date_picker) return; block_picker_signals (cell); gnc_date_picker_set_date (box->date_picker, box->date.tm_mday, box->date.tm_mon, box->date.tm_year + 1900); unblock_picker_signals (cell); }
size_t gnc_print_date_time_buff (char * buff, size_t len, time64 t) /* at the same time than gnc_parse_time_date is based on a call on GncDateTime and all calls on TZenv = g_strdup(g_getenv("TZ")) are destroyed, replace definition of gnc_print_date_time_buff by * if (!buff) return 0; GncDateTime gncdt(t); std::string str = gncdt.format_time("%Z %H:%M:%S " + qof_date_format_get_string(dateFormat)); strncpy(buff, str.c_str(), len); if (str.length() >= len) buff[len - 1] = '\0'; */ { /* replace this call by GncDateTime gncdt(t) when all TZenv code will disappear*/ struct tm theTime; size_t timelength; time64 bt; bt = t; if (!gnc_localtime_r(&bt, &theTime)) /* theTime is *local* time */ return 0; timelength = g_snprintf (buff, len, "%c%02d%02d %02d:%02d:%02d ", theTime.tm_gmtoff < 0 ? '-' : '+', abs(theTime.tm_gmtoff) / 60 / 60, (abs(theTime.tm_gmtoff) / 60) % 60, theTime.tm_hour, theTime.tm_min, theTime.tm_sec);// the left part of the string is the one hidden whenever the column is too small. timelength += qof_print_date_buff (buff+timelength, len-timelength, t);// TODO mettre timelength au lieu de 9 ! return strlen(buff); }
static void gnc_tm_get_day_end (struct tm *tm, time64 time_val) { /* Get the equivalent time structure */ if (!gnc_localtime_r(&time_val, tm)) return; gnc_tm_set_day_end(tm); }
time64 time64CanonicalDayTime (time64 t) { struct tm tm; gnc_localtime_r(&t, &tm); gnc_tm_set_day_middle(&tm); return gnc_mktime (&tm); }
void gnc_gdate_set_time64 (GDate* gd, time64 time) { struct tm tm; gnc_localtime_r(&time, &tm); g_date_set_dmy (gd, tm.tm_mday, static_cast<GDateMonth>(tm.tm_mon + 1), tm.tm_year + 1900); }
struct tm* gnc_localtime (const time64 *secs) { struct tm *time = g_slice_alloc0 (sizeof (struct tm)); if (gnc_localtime_r (secs, time) == NULL) { gnc_tm_free (time); return NULL; } return time; }
void gnc_timespec2dmy (Timespec t, int *day, int *month, int *year) { struct tm result; time64 t_secs = t.tv_sec + (t.tv_nsec / NANOS_PER_SECOND); gnc_localtime_r(&t_secs, &result); if (day) *day = result.tm_mday; if (month) *month = result.tm_mon + 1; if (year) *year = result.tm_year + 1900; }
struct tm* gnc_localtime (const time64 *secs) { auto time = static_cast<struct tm*>(calloc(1, sizeof(struct tm))); if (gnc_localtime_r (secs, time) == NULL) { gnc_tm_free (time); return NULL; } return time; }
/* Converts any time on a day to midday that day. * given a timepair contains any time on a certain day (local time) * converts it to be midday that day. */ Timespec timespecCanonicalDayTime(Timespec t) { struct tm tm; Timespec retval; time64 t_secs = t.tv_sec + (t.tv_nsec / NANOS_PER_SECOND); gnc_localtime_r(&t_secs, &tm); gnc_tm_set_day_middle(&tm); retval.tv_sec = gnc_mktime(&tm); retval.tv_nsec = 0; return retval; }
/* Convert day, month and year values to a date string Convert a date as day / month / year integers into a localized string representation param buff - pointer to previously allocated character array; its size must be at lease MAX_DATE_LENTH bytes. param day - value to be set with the day of the month as 1 ... 31 param month - value to be set with the month of the year as 1 ... 12 param year - value to be set with the year (4-digit) return length of string created in buff. Globals: global dateFormat value */ size_t qof_print_date_dmy_buff (char * buff, size_t len, int day, int month, int year) { int flen; if (!buff) return 0; /* Note that when printing year, we use %-4d in format string; * this causes a one, two or three-digit year to be left-adjusted * when printed (i.e. padded with blanks on the right). This is * important while the user is editing the year, since erasing a * digit can temporarily cause a three-digit year, and having the * blank on the left is a real pain for the user. So pad on the * right. */ switch (dateFormat) { case QOF_DATE_FORMAT_UK: flen = g_snprintf (buff, len, "%02d/%02d/%-4d", day, month, year); break; case QOF_DATE_FORMAT_CE: flen = g_snprintf (buff, len, "%02d.%02d.%-4d", day, month, year); break; case QOF_DATE_FORMAT_LOCALE: { struct tm tm_str; time64 t; tm_str.tm_mday = day; tm_str.tm_mon = month - 1; /* tm_mon = 0 through 11 */ tm_str.tm_year = year - 1900; /* this is what the standard says, it's not a Y2K thing */ gnc_tm_set_day_start (&tm_str); t = gnc_mktime (&tm_str); gnc_localtime_r (&t, &tm_str); flen = qof_strftime (buff, len, GNC_D_FMT, &tm_str); if (flen != 0) break; } /* FALL THROUGH */ case QOF_DATE_FORMAT_ISO: case QOF_DATE_FORMAT_UTC: flen = g_snprintf (buff, len, "%04d-%02d-%02d", year, month, day); break; case QOF_DATE_FORMAT_US: default: flen = g_snprintf (buff, len, "%02d/%02d/%-4d", month, day, year); break; } return flen; }
size_t qof_print_date_buff (char * buff, size_t len, time64 t) { struct tm theTime; time64 bt = t; size_t actual; if (!buff) return 0 ; if (!gnc_localtime_r(&bt, &theTime)) return 0; actual = qof_print_date_dmy_buff (buff, len, theTime.tm_mday, theTime.tm_mon + 1, theTime.tm_year + 1900); return actual; }
static void fill_time_combo (GtkWidget *widget, GNCDateEdit *gde) { GtkTreeModel *model; GtkTreeIter hour_iter, min_iter; struct tm *tm_returned; struct tm mtm; time64 current_time; int i, j; if (gde->lower_hour > gde->upper_hour) return; model = gtk_combo_box_get_model (GTK_COMBO_BOX(gde->time_combo)); gnc_time (¤t_time); tm_returned = gnc_localtime_r (¤t_time, &mtm); g_return_if_fail(tm_returned != NULL); for (i = gde->lower_hour; i <= gde->upper_hour; i++) { char buffer [40]; mtm.tm_hour = i; mtm.tm_min = 0; if (gde->flags & GNC_DATE_EDIT_24_HR) qof_strftime (buffer, sizeof (buffer), "%H:00", &mtm); else qof_strftime (buffer, sizeof (buffer), "%I:00 %p ", &mtm); gtk_tree_store_append (GTK_TREE_STORE(model), &hour_iter, NULL); gtk_tree_store_set (GTK_TREE_STORE(model), &hour_iter, 0, buffer, -1); for (j = 0; j < 60; j += 15) { mtm.tm_min = j; if (gde->flags & GNC_DATE_EDIT_24_HR) qof_strftime (buffer, sizeof (buffer), "%H:%M", &mtm); else qof_strftime (buffer, sizeof (buffer), "%I:%M %p", &mtm); gtk_tree_store_append(GTK_TREE_STORE(model), &min_iter, &hour_iter ); gtk_tree_store_set (GTK_TREE_STORE(model), &min_iter, 0, buffer, -1); } } }
int main(void) { int y, m, d; time64 bin_time; struct tm br_time; gnc_time (&bin_time); gnc_localtime_r (&bin_time, &br_time); gregorian_to_jalali(&y, &m, &d, 1900 + br_time.tm_year, 1 + br_time.tm_mon, br_time.tm_mday); printf("Current Jalali date: %d %s %d\n", d, j_month_name[m-1], y); return 0; }
/* Return the field separator for the current date format return date character */ char dateSeparator (void) { static char locale_separator = '\0'; switch (dateFormat) { case QOF_DATE_FORMAT_CE: return '.'; case QOF_DATE_FORMAT_ISO: case QOF_DATE_FORMAT_UTC: return '-'; case QOF_DATE_FORMAT_US: case QOF_DATE_FORMAT_UK: default: return '/'; case QOF_DATE_FORMAT_LOCALE: if (locale_separator != '\0') return locale_separator; else { /* Make a guess */ gchar string[256]; struct tm tm; time64 secs; gchar *s; secs = gnc_time (NULL); gnc_localtime_r(&secs, &tm); auto normalized_fmt = normalize_format(qof_date_format_get_string(dateFormat)); qof_strftime(string, sizeof(string), normalized_fmt.c_str(), &tm); for (s = string; *s != '\0'; s++) if (!isdigit(*s)) return (locale_separator = *s); } break; } return '\0'; }
static void gnc_date_cell_init (DateCell *cell) { PopBox *box; time64 secs; char buff[DATE_BUF]; gnc_basic_cell_init (&(cell->cell)); cell->cell.is_popup = TRUE; cell->cell.destroy = gnc_date_cell_destroy; cell->cell.gui_realize = gnc_date_cell_realize; cell->cell.gui_destroy = gnc_date_cell_gui_destroy; cell->cell.modify_verify = gnc_date_cell_modify_verify; cell->cell.direct_update = gnc_date_cell_direct_update; cell->cell.set_value = gnc_date_cell_set_value_internal; box = g_new0 (PopBox, 1); box->sheet = NULL; box->item_edit = NULL; box->date_picker = NULL; box->signals_connected = FALSE; box->calendar_popped = FALSE; box->in_date_select = FALSE; cell->cell.gui_private = box; /* default value is today's date */ gnc_time (&secs); gnc_localtime_r (&secs, &(box->date)); gnc_date_cell_print_date (cell, buff); gnc_basic_cell_set_value_internal (&cell->cell, buff); }
static char * get_date_help (VirtualLocation virt_loc, gpointer user_data) { GncEntryLedger *ledger = user_data; BasicCell *cell; char string[1024]; struct tm tm; Timespec ts; time64 tt; cell = gnc_table_get_cell (ledger->table, virt_loc); if (!cell) return NULL; if (!cell->value || *cell->value == '\0') return NULL; gnc_date_cell_get_date ((DateCell *) cell, &ts); tt = ts.tv_sec; gnc_localtime_r (&tt, &tm); qof_strftime (string, sizeof(string), "%A %d %B %Y", &tm); return g_strdup (string); }
/* Based on the post date and a proximo type, compute the month and * year this is due. The actual day is filled in below. * * A proximo billing term has multiple parameters: * * due day: day of the month the invoice/bill will be due * * cutoff: day of the month used to decide if the due date will be * in the next month or in the month thereafter. This can be * a negative number in which case the cutoff date is relative * to the end of the month and counting backwards. * Eg: cutoff = -3 would mean 25 in February or 28 in June * * How does it work: * Assume cutoff = 19 and due day = 20 * * * Example 1 post date = 14-06-2010 (European date format) * 14 is less than the cutoff of 19, so the due date will be in the next * month. Since the due day is set to 20, the due date will be * 20-07-2010 * * * Example 2 post date = 22-06-2010 (European date format) * 22 is more than the cutoff of 19, so the due date will be in the month * after next month. Since the due day is set to 20, the due date will be * 20-02-2010 * */ static void compute_monthyear (const GncBillTerm *term, time64 post_date, int *month, int *year) { int iday, imonth, iyear; struct tm tm; int cutoff = term->cutoff; g_return_if_fail (term->type == GNC_TERM_TYPE_PROXIMO); gnc_localtime_r (&post_date, &tm); iday = tm.tm_mday; imonth = tm.tm_mon + 1; iyear = tm.tm_year + 1900; if (cutoff <= 0) cutoff += gnc_date_get_last_mday (imonth - 1, iyear); if (iday <= cutoff) { /* We apply this to next month */ imonth++; } else { /* We apply to the following month */ imonth += 2; } if (imonth > 12) { iyear++; imonth -= 12; } if (month) *month = imonth; if (year) *year = iyear; }
void gnc_date_format_refresh (GNCDateFormat *gdf) { GNCDateFormatPriv *priv; int sel_option; gboolean enable_year, enable_month, enable_custom, check_modifiers; static gchar *format, *c; gchar date_string[MAX_DATE_LEN]; time64 secs_now; struct tm today; g_return_if_fail(gdf); g_return_if_fail(GNC_IS_DATE_FORMAT(gdf)); priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf); sel_option = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->format_combobox)); switch (sel_option) { case QOF_DATE_FORMAT_CUSTOM: format = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->custom_entry))); enable_year = enable_month = check_modifiers = FALSE; enable_custom = TRUE; break; case QOF_DATE_FORMAT_LOCALE: case QOF_DATE_FORMAT_UTC: format = g_strdup(qof_date_format_get_string(sel_option)); enable_year = enable_month = check_modifiers = enable_custom = FALSE; break; case QOF_DATE_FORMAT_ISO: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->months_number), TRUE); enable_year = check_modifiers = TRUE; enable_month = enable_custom = FALSE; break; default: enable_year = enable_month = check_modifiers = TRUE; enable_custom = FALSE; break; } /* Tweak widget sensitivities, as appropriate. */ gnc_date_format_enable_year(gdf, enable_year); gnc_date_format_enable_month(gdf, enable_month); gnc_date_format_enable_format(gdf, enable_custom); /* Update the format string based upon the user's preferences */ if (check_modifiers) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_number))) { format = g_strdup(qof_date_format_get_string(sel_option)); } else { format = g_strdup(qof_date_text_format_get_string(sel_option)); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_name))) { c = strchr(format, 'b'); if (c) *c = 'B'; } } if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->years_button))) { c = strchr(format, 'y'); if (c) *c = 'Y'; } } /* * Give feedback on the format string so users can see how it works * without having to read the strftime man page. Prevent recursive * signals. */ g_signal_handlers_block_matched(priv->custom_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gdf); gtk_entry_set_text(GTK_ENTRY(priv->custom_entry), format); g_signal_handlers_unblock_matched(priv->custom_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gdf); /* Visual feedback on what the date will look like. */ secs_now = gnc_time (NULL); gnc_localtime_r (&secs_now, &today); qof_strftime(date_string, MAX_DATE_LEN, format, &today); gtk_label_set_text(GTK_LABEL(priv->sample_label), date_string); g_free(format); }
/** Parses a string into a date, given a format. The format must * include the year. This function should only be called by * parse_date. * @param date_str The string containing a date being parsed * @param format An index specifying a format in date_format_user * @return The parsed value of date_str on success or -1 on failure */ static time64 parse_date_with_year (const char* date_str, int format) { time64 rawtime; /* The integer time */ struct tm retvalue, test_retvalue; /* The time in a broken-down structure */ int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1; /* Buffer for containing individual parts (e.g. year, month, day) of a date */ char date_segment[5]; /* The compiled regular expression */ regex_t preg = {0}; /* An array containing indices specifying the matched substrings in date_str */ regmatch_t pmatch[4] = { {0}, {0}, {0}, {0} }; /* The regular expression for parsing dates */ const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$|^ *([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]).*$"; /* We get our matches using the regular expression. */ regcomp (&preg, regex, REG_EXTENDED); regexec (&preg, date_str, 4, pmatch, 0); regfree (&preg); /* If there wasn't a match, there was an error. */ if (pmatch[0].rm_eo == 0) return -1; /* If this is a string without separators ... */ if (pmatch[1].rm_so == -1) { /* ... we will fill in the indices based on the user's selection. */ int k = 0; /* k traverses date_str by keeping track of where separators "should" be. */ j = 1; /* j traverses pmatch. */ for (i = 0; date_format_user[format][i]; i++) { char segment_type = date_format_user[format][i]; /* Only do something if this is a meaningful character */ if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd') { pmatch[j].rm_so = k; switch (segment_type) { case 'm': case 'd': k += 2; break; case 'y': k += 4; break; } pmatch[j].rm_eo = k; j++; } } } /* Put some sane values in retvalue by using the current time for * the non-year-month-day parts of the date. */ gnc_time (&rawtime); gnc_localtime_r (&rawtime, &retvalue); /* j traverses pmatch (index 0 contains the entire string, so we * start at index 1 for the first meaningful match). */ j = 1; /* Go through the date format and interpret the matches in order of * the sections in the date format. */ for (i = 0; date_format_user[format][i]; i++) { char segment_type = date_format_user[format][i]; /* Only do something if this is a meaningful character */ if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd') { /* Copy the matching substring into date_segment so that we can * convert it into an integer. */ mem_length = pmatch[j].rm_eo - pmatch[j].rm_so; memcpy (date_segment, date_str + pmatch[j].rm_so, mem_length); date_segment[mem_length] = '\0'; /* Set the appropriate member of retvalue. Save the original * values so that we can check if the change when we use gnc_mktime * below. */ switch (segment_type) { case 'y': retvalue.tm_year = atoi (date_segment); /* Handle two-digit years. */ if (retvalue.tm_year < 100) { /* We allow two-digit years in the range 1969 - 2068. */ if (retvalue.tm_year < 69) retvalue.tm_year += 100; } else retvalue.tm_year -= 1900; orig_year = retvalue.tm_year; break; case 'm': orig_month = retvalue.tm_mon = atoi (date_segment) - 1; break; case 'd': orig_day = retvalue.tm_mday = atoi (date_segment); break; } j++; } } /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged, * everything is okay; otherwise, an error has occurred. */ /* We have to use a "test" date value to account for changes in * daylight savings time, which can cause a date change with gnc_mktime * near midnight, causing the code to incorrectly think a date is * incorrect. */ test_retvalue = retvalue; gnc_mktime (&test_retvalue); retvalue.tm_isdst = test_retvalue.tm_isdst; rawtime = gnc_mktime (&retvalue); if (retvalue.tm_mday == orig_day && retvalue.tm_mon == orig_month && retvalue.tm_year == orig_year) { return rawtime; } else { return -1; } }
/* gnc_parse_time_date parsing tm (+0000) (from %+05d %d:%d:%d then the date) in datestr I hope that github will show differences between my new function gnc_parse_time_date and function qof_scan_date_internal found below, existing in HEAD (as of 25/1/2016) of the branch master of gnucash. Thanks to spaces added to qof_scan_date_internal. ********************************************************** KNOWN BUG: gnucash crashes if a date string starts by ":" and longer thant ":". ********************************************************** */ void gnc_parse_time_date (struct tm *parsed, const char * buff) /* gnc_parse_time_date should call GncDateTime gncdt(std::string) when gnc_print_date_time_buff will be using GncDateTime and TZenv will disappear from everywhere*/ { char *dupe, *tmp, *TZhourtxt, *TZtxt, *hourtxt, *minutetxt, *secondtxt; /* tmp may be the unparsed part of the string. , */ int iday, imonth, iyear; time64 secs; int tz; /* signed timezone in seconds */ hourtxt = NULL;/* will stay NULL if TZ is not in buff */ if (!parsed) return; parsed->tm_year=-1; /* means failure. */ if (!buff) return; dupe = g_strdup (buff); tmp = dupe; TZhourtxt = strtok (tmp, ":"); minutetxt = strtok (NULL, ":"); secs = time(NULL); /* gnc_time(NULL) or time(NULL) ?? */ gnc_localtime_r(&secs, parsed); tz = parsed->tm_gmtoff; if (TZhourtxt || minutetxt) { if (dupe[0] != ':' && minutetxt) {/* "hh:mm date" and "hh:mm:ss date" are parsed. "hh date" is not recognized. "hh:mm" and "hh:mm:ss" imply today. Default value for the timezone is TZenv */ secondtxt = strtok (NULL, " "); tmp = strtok (NULL, "\a");/* hack: \a is the audible bell, hope this one is never used in a date format ; tmp = minutetxt + strlen (strtok (minutetxt, " "))+1 might be better */ TZtxt = strtok (TZhourtxt, " "); hourtxt = strtok (NULL, " "); if (hourtxt) { tz = atoi(TZtxt); parsed->tm_hour = atoi(hourtxt) ; tz = 60 * ( (tz % 100) + 60 * (tz / 100) ) ; if (atoi(TZtxt) < 0) tz = - tz; } else { if (TZtxt) { parsed->tm_hour = atoi(TZtxt) ; } else { parsed->tm_hour = 0 ; /* the case "::" falls here ? */ } } if (tmp) { /* +0200 02:01:03 2016-06-03 * <+0200 02> TZhourtxt * <01> minutetxt * <03> secondtxt * <2016-06-03> tmp * <+0200> TZtxt * <02> hourtxt */ parsed->tm_min = atoi(minutetxt) ; parsed->tm_sec = atoi(secondtxt) ; } else { /* +0200 02:01 2016-06-03 * <+0200 02> TZhourtxt * <01 2016-06-03> minutetxt */ parsed->tm_min = atoi ( strtok (minutetxt, " ") ); parsed->tm_sec = 0 ; tmp = secondtxt ; /* +0200 02:01 2016-06-03 * <+0200 02> TZhourtxt * <2016-06-03> minutetxt * <2016-06-03> tmp */ } parsed->tm_sec = (parsed->tm_sec) - tz + (parsed->tm_gmtoff) ; } } else { parsed->tm_hour = k_reference_time_TZ_hour; /* parsed->tm_gmtoff = 0; does not work */ parsed->tm_min = k_reference_time_TZ_min; parsed->tm_sec = k_reference_time_TZ_sec_of_min + parsed->tm_gmtoff; tmp = TZhourtxt; } if (tmp && qof_scan_date (tmp, &iday, &imonth, &iyear)) { parsed->tm_mday = iday; parsed->tm_mon = imonth - 1; parsed->tm_year = iyear - 1900; } /* else { Couldn't parse date, use today ; } */ g_free (dupe); }