Skip to content
Snippets Groups Projects
say.c 187 KiB
Newer Older
				for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
					sndfile[sndoffset] = format[offset];
				sndfile[sndoffset] = '\0';
				res = wait_file(chan,ints,sndfile,lang);
				break;
			case 'A':
			case 'a':
				/* Sunday - Saturday */
				snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'B':
			case 'b':
			case 'h':
				/* January - December */
				snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'm':
				/* Month enumerated */
				res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);	
				break;
			case 'd':
			case 'e':
				/* First - Thirtyfirst */
				res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);	
				break;
			case 'Y':
				/* Year */
				if (tm.tm_year > 99) {
				        res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
				} else if (tm.tm_year < 1) {
					/* I'm not going to handle 1900 and prior */
					/* We'll just be silent on the year, instead of bombing out. */
					res = wait_file(chan, ints, "digits/19", lang);
					if (!res) {
						if (tm.tm_year <= 9) {
							/* 1901 - 1909 */
							res = wait_file(chan,ints, "digits/oh", lang);

						res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
					}
				}
				break;
			case 'I':
			case 'l':
				/* 12-Hour */
				if (tm.tm_hour == 0)
					snprintf(nextmsg,sizeof(nextmsg), "digits/12");
				else if (tm.tm_hour > 12)
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'H':
			case 'k':
				/* 24-Hour */
				if (format[offset] == 'H') {
					/* e.g. oh-eight */
					if (tm.tm_hour < 10) {
						res = wait_file(chan,ints, "digits/oh",lang);
					}
				} else {
					/* e.g. eight */
					if (tm.tm_hour == 0) {
						res = wait_file(chan,ints, "digits/oh",lang);
					}
				}
				if (!res) {
					if (tm.tm_hour != 0) {
						int remainder = tm.tm_hour;
						if (tm.tm_hour > 20) {
							res = wait_file(chan,ints, "digits/20",lang);
							remainder -= 20;
						}
						if (!res) {
							snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
							res = wait_file(chan,ints,nextmsg,lang);
						}
					}
				}
				break;
			case 'M':
				/* Minute */
				if (tm.tm_min == 0) {
					if (format[offset] == 'M') {
						res = wait_file(chan, ints, "digits/oclock", lang);
					} else {
						res = wait_file(chan, ints, "digits/hundred", lang);
					}
				} else if (tm.tm_min < 10) {
					res = wait_file(chan,ints, "digits/oh",lang);
					if (!res) {
						snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
						res = wait_file(chan,ints,nextmsg,lang);
					}
				} else {
					res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
				}
				break;
			case 'P':
			case 'p':
				/* AM/PM */
				if (tm.tm_hour > 11)
					snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'Q':
				/* Shorthand for "Today", "Yesterday", or ABdY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						res = wait_file(chan,ints, "digits/today",lang);
					} else if (beg_today - 86400 < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if (beg_today - 86400 * 6 < time) {
						/* Within the last week */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
					} else if (beg_today - 2628000 < time) {
						/* Less than a month ago - "Sunday, October third" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
					} else if (beg_today - 15768000 < time) {
						/* Less than 6 months ago - "August seventh" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
						/* More than 6 months ago - "April nineteenth two thousand three" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
					}
				}
				break;
			case 'q':
				/* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
					} else if ((beg_today - 86400) < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if (beg_today - 86400 * 6 < time) {
						/* Within the last week */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
					} else if (beg_today - 2628000 < time) {
						/* Less than a month ago - "Sunday, October third" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
					} else if (beg_today - 15768000 < time) {
						/* Less than 6 months ago - "August seventh" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
						/* More than 6 months ago - "April nineteenth two thousand three" */
						res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
				res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
				break;
			case 'S':
				/* Seconds */
				if (tm.tm_sec == 0) {
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
					res = wait_file(chan,ints,nextmsg,lang);
				} else if (tm.tm_sec < 10) {
					res = wait_file(chan,ints, "digits/oh",lang);
					if (!res) {
						snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
						res = wait_file(chan,ints,nextmsg,lang);
					}
				} else {
					res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
				res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
				break;
			case ' ':
			case '	':
				/* Just ignore spaces and tabs */
				break;
			default:
				/* Unknown character */
				ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
		}
		/* Jump out on DTMF */
		if (res) {
			break;
		}
	}
	return res;
}

/* Danish syntax */
int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
{
	struct tm tm;
	int res=0, offset, sndoffset;
	char sndfile[256], nextmsg[256];

	ast_localtime(&time,&tm,timezone);

	for (offset=0 ; format[offset] != '\0' ; offset++) {
		ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
		switch (format[offset]) {
			/* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
			case '\'':
				/* Literal name of a sound file */
				sndoffset=0;
				for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
					sndfile[sndoffset] = format[offset];
				sndfile[sndoffset] = '\0';
				res = wait_file(chan,ints,sndfile,lang);
				break;
			case 'A':
			case 'a':
				/* Sunday - Saturday */
				snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'B':
			case 'b':
			case 'h':
				/* January - December */
				snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'm':
				/* Month enumerated */
				res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");	
				break;
			case 'd':
			case 'e':
				/* First - Thirtyfirst */
				res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");	
				break;
			case 'Y':
				/* Year */
				{
					int year = tm.tm_year + 1900;
					if (year > 1999) {	/* year 2000 and later */
						res = ast_say_number(chan, year, ints, lang, (char *) NULL);	
					} else {
						if (year < 1100) {
							/* I'm not going to handle 1100 and prior */
							/* We'll just be silent on the year, instead of bombing out. */
						} else {
						    /* year 1100 to 1999. will anybody need this?!? */
						    /* say 1967 as 'nineteen hundred seven and sixty' */
							snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
							res = wait_file(chan,ints,nextmsg,lang);
							if (!res) {
								res = wait_file(chan,ints, "digits/hundred",lang);
								if (!res && year % 100 != 0) {
									res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);	
								}
							}
						}
					}
				}
				break;
			case 'I':
			case 'l':
				/* 12-Hour */
				res = wait_file(chan,ints,"digits/oclock",lang);
				if (tm.tm_hour == 0)
					snprintf(nextmsg,sizeof(nextmsg), "digits/12");
				else if (tm.tm_hour > 12)
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
				if (!res) {
					res = wait_file(chan,ints,nextmsg,lang);
				}
				break;
			case 'H':
Kevin P. Fleming's avatar
Kevin P. Fleming committed
				/* 24-Hour, single digit hours preceeded by "oh" (0) */
				if (tm.tm_hour < 10 && tm.tm_hour > 0) {
					res = wait_file(chan,ints, "digits/0",lang);
				}
				/* FALLTRHU */
			case 'k':
				/* 24-Hour */
Kevin P. Fleming's avatar
Kevin P. Fleming committed
				res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);	
				break;
			case 'M':
				/* Minute */
				if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
					res = ast_say_number(chan, tm.tm_min, ints, lang, "f");	
				}
				if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
					if (tm.tm_min == 1) {
						res = wait_file(chan,ints,"digits/minute",lang);
					} else {
						res = wait_file(chan,ints,"digits/minutes",lang);
					}
				}
				break;
			case 'P':
			case 'p':
				/* AM/PM */
				if (tm.tm_hour > 11)
					snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'Q':
				/* Shorthand for "Today", "Yesterday", or AdBY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
				{
					struct timeval now;
					struct tm tmnow;

					gettimeofday(&now,NULL);
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						res = wait_file(chan,ints, "digits/today",lang);
					} else if (beg_today - 86400 < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "AdBY", timezone);
					}
				}
				break;
			case 'q':
				/* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
				{
					struct timeval now;
					struct tm tmnow;

					gettimeofday(&now,NULL);
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
					} else if ((beg_today - 86400) < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if (beg_today - 86400 * 6 < time) {
						/* Within the last week */
						res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "AdBY", timezone);
					}
				}
				break;
			case 'R':
				res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
				break;
			case 'S':
				/* Seconds */
				res = wait_file(chan,ints, "digits/and",lang);
				if (!res) {
					res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");	
					if (!res) {
						res = wait_file(chan,ints, "digits/seconds",lang);
					}
				}
				break;
			case 'T':
				res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
				break;
			case ' ':
			case '	':
				/* Just ignore spaces and tabs */
				break;
			default:
				/* Unknown character */
				ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
		}
		/* Jump out on DTMF */
		if (res) {
			break;
		}
	}
	return res;
}

/* German syntax */
int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
{
	struct tm tm;
	int res=0, offset, sndoffset;
	char sndfile[256], nextmsg[256];

	ast_localtime(&time,&tm,timezone);

	for (offset=0 ; format[offset] != '\0' ; offset++) {
		ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
		switch (format[offset]) {
			/* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
			case '\'':
				/* Literal name of a sound file */
				sndoffset=0;
				for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
					sndfile[sndoffset] = format[offset];
				sndfile[sndoffset] = '\0';
				res = wait_file(chan,ints,sndfile,lang);
				break;
			case 'A':
			case 'a':
				/* Sunday - Saturday */
				snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'B':
			case 'b':
			case 'h':
				/* January - December */
				snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'm':
				/* Month enumerated */
				res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");	
				break;
			case 'd':
			case 'e':
				/* First - Thirtyfirst */
				res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");	
				break;
			case 'Y':
				/* Year */
				{
					int year = tm.tm_year + 1900;
					if (year > 1999) {	/* year 2000 and later */
						res = ast_say_number(chan, year, ints, lang, (char *) NULL);	
					} else {
						if (year < 1100) {
							/* I'm not going to handle 1100 and prior */
							/* We'll just be silent on the year, instead of bombing out. */
						} else {
						    /* year 1100 to 1999. will anybody need this?!? */
						    /* say 1967 as 'neunzehn hundert sieben und sechzig' */
							snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
							res = wait_file(chan,ints,nextmsg,lang);
							if (!res) {
								res = wait_file(chan,ints, "digits/hundred",lang);
								if (!res && year % 100 != 0) {
									res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);	
								}
							}
						}
					}
				}
				break;
			case 'I':
			case 'l':
				/* 12-Hour */
				if (tm.tm_hour == 0)
					snprintf(nextmsg,sizeof(nextmsg), "digits/12");
				else if (tm.tm_hour > 12)
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
				res = wait_file(chan,ints,nextmsg,lang);
				if (!res) {
					res = wait_file(chan,ints,"digits/oclock",lang);
				}
				break;
			case 'H':
			case 'k':
				/* 24-Hour */
				res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);	
				if (!res) {
					res = wait_file(chan,ints,"digits/oclock",lang);
				}
				break;
			case 'M':
				/* Minute */
				if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
					res = ast_say_number(chan, tm.tm_min, ints, lang, "f");	
				}
				if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
					if (tm.tm_min == 1) {
						res = wait_file(chan,ints,"digits/minute",lang);
					} else {
						res = wait_file(chan,ints,"digits/minutes",lang);
					}
				}
				break;
			case 'P':
			case 'p':
				/* AM/PM */
				if (tm.tm_hour > 11)
					snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'Q':
				/* Shorthand for "Today", "Yesterday", or AdBY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						res = wait_file(chan,ints, "digits/today",lang);
					} else if (beg_today - 86400 < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "AdBY", timezone);
					}
				}
				break;
			case 'q':
				/* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
					} else if ((beg_today - 86400) < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if (beg_today - 86400 * 6 < time) {
						/* Within the last week */
						res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "AdBY", timezone);
					}
				}
				break;
			case 'R':
				res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
				break;
			case 'S':
				/* Seconds */
				res = wait_file(chan,ints, "digits/and",lang);
				if (!res) {
					res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");	
					if (!res) {
						res = wait_file(chan,ints, "digits/seconds",lang);
					}
				}
				break;
			case 'T':
				res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
				break;
			case ' ':
			case '	':
				/* Just ignore spaces and tabs */
				break;
			default:
				/* Unknown character */
				ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
		}
		/* Jump out on DTMF */
		if (res) {
			break;
		}
	}
	return res;
}

/* TODO: this probably is not the correct format for doxygen remarks */

/** ast_say_date_with_format_he Say formmated date in Hebrew
 *
 * \ref ast_say_date_with_format_en for the details of the options 
 *
 * Changes from the English version: 
 *
 * * don't replicate in here the logic of ast_say_number_full_he
 *
 * * year is always 4-digit (because it's simpler)
 *
 * * added c, x, and X. Mainly for my tests
 *
 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
 *
 * TODO: 
 * * A "ha" is missing in the standard date format, before the 'd'.
 * * The numbers of 3000--19000 are not handled well
 **/
#define IL_DATE_STR "AdBY"
#define IL_TIME_STR "IMp"
#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, 
    const char *ints, const char *lang, const char *format, 
    const char *timezone)
{
	/* TODO: This whole function is cut&paste from 
	 * ast_say_date_with_format_en . Is that considered acceptable?
	 **/
	struct tm tm;
	int res=0, offset, sndoffset;
	char sndfile[256], nextmsg[256];

	ast_localtime(&time,&tm,timezone);

	for (offset=0 ; format[offset] != '\0' ; offset++) {
		ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
		switch (format[offset]) {
			/* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
			case '\'':
				/* Literal name of a sound file */
				sndoffset=0;
				for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
					sndfile[sndoffset] = format[offset];
				sndfile[sndoffset] = '\0';
				res = wait_file(chan,ints,sndfile,lang);
				break;
			case 'A':
			case 'a':
				/* Sunday - Saturday */
				snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'B':
			case 'b':
			case 'h':
				/* January - December */
				snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'd':
			case 'e': /* Day of the month */
                                /* I'm not sure exactly what the parameters 
                                 * audiofd and ctrlfd to 
                                 * ast_say_number_full_he mean, but it seems
                                 * safe to pass -1 there. 
                                 *
                                 * At least in one of the pathes :-( 
                                 */
				res = ast_say_number_full_he(chan, tm.tm_mday,
					ints, lang, "m", -1, -1
				);
				break;
			case 'Y': /* Year */
				res = ast_say_number_full_he(chan, tm.tm_year+1900,
					ints, lang, "f", -1, -1
				);
				break;
			case 'I':
			case 'l': /* 12-Hour */
				{
					int hour = tm.tm_hour;
					hour = hour%12;
					if (hour == 0) hour=12;
				
					res = ast_say_number_full_he(chan, hour,
						ints, lang, "f", -1, -1
					);
				}
				break;
			case 'H':
			case 'k': /* 24-Hour */
				/* With 'H' there is an 'oh' after a single-
				 * digit hour */
				if ((format[offset] == 'H') && 
				    (tm.tm_hour <10)&&(tm.tm_hour>0)
				) { /* e.g. oh-eight */
					res = wait_file(chan,ints, "digits/oh",lang);
				}
				
				res = ast_say_number_full_he(chan, tm.tm_hour,
					ints, lang, "f", -1, -1
				);
				break;
			case 'M': /* Minute */
				res = ast_say_number_full_he(chan, tm.tm_min, 
					ints, lang,"f", -1, -1
				);
				break;
			case 'P':
			case 'p':
				/* AM/PM */
				if (tm.tm_hour > 11)
					snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'Q':
				/* Shorthand for "Today", "Yesterday", or "date" */
			case 'q':
				/* Shorthand for "" (today), "Yesterday", A 
                                 * (weekday), or "date" */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
				{
					struct timeval now;
					struct tm tmnow;
					char todo = format[offset]; /* The letter to format*/
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						if (todo == 'Q') {
							res = wait_file(chan,
						}
					} else if (beg_today - 86400 < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if ((todo != 'Q') &&
						(beg_today - 86400 * 6 < time))
					{
						/* Within the last week */
						res = ast_say_date_with_format_he(chan,
										  time, ints, lang, 
										  "A", timezone);
						res = ast_say_date_with_format_he(chan,
										  time, ints, lang, 
										  IL_DATE_STR, timezone);
					}
				}
				break;
			case 'R':
				res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
				break;
			case 'S': /* Seconds */
				res = ast_say_number_full_he(chan, tm.tm_sec,
					ints, lang, "f", -1, -1
				);
				break;
			case 'T':
				res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
				break;
			/* c, x, and X seem useful for testing. Not sure
                         * if thiey're good for the general public */
                        case 'c':
				res = ast_say_date_with_format_he(chan, time, 
                                    ints, lang, IL_DATE_STR_FULL, timezone);
				break;
                        case 'x':
				res = ast_say_date_with_format_he(chan, time, 
                                    ints, lang, IL_DATE_STR, timezone);
				break;
                        case 'X': /* Currently not locale-dependent...*/
				res = ast_say_date_with_format_he(chan, time, 
                                    ints, lang, IL_TIME_STR, timezone);
				break;
			case ' ':
			case '	':
				/* Just ignore spaces and tabs */
				break;
			default:
				/* Unknown character */
				ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
		}
		/* Jump out on DTMF */
		if (res) {
			break;
		}
	}
	return res;
}


/* Spanish syntax */
int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
{
	struct tm tm;
	int res=0, offset, sndoffset;
	char sndfile[256], nextmsg[256];

	ast_localtime(&time,&tm,timezone);

	for (offset=0 ; format[offset] != '\0' ; offset++) {
		ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
		switch (format[offset]) {
			/* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
			case '\'':
				/* Literal name of a sound file */
				sndoffset=0;
				for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
					sndfile[sndoffset] = format[offset];
				sndfile[sndoffset] = '\0';
				snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'A':
			case 'a':
				/* Sunday - Saturday */
				snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'B':
			case 'b':
			case 'h':
				/* January - December */
				snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'm':
				/* First - Twelfth */
				snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'd':
			case 'e':
				/* First - Thirtyfirst */
				res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
				break;
			case 'Y':
				/* Year */
				res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
				break;
			case 'I':
			case 'l':
				/* 12-Hour */
				if (tm.tm_hour == 0)
					snprintf(nextmsg,sizeof(nextmsg), "digits/12");
				else if (tm.tm_hour > 12)
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
				else
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
				res = wait_file(chan,ints,nextmsg,lang);
				break;
			case 'H':
			case 'k':
				/* 24-Hour */
				res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
				break;
			case 'M':
				/* Minute */
				res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);	
				break;
			case 'P':
			case 'p':
				/* AM/PM */
				if (tm.tm_hour > 12)
					res = wait_file(chan, ints, "digits/p-m", lang);
				else if (tm.tm_hour  && tm.tm_hour < 12)
					res = wait_file(chan, ints, "digits/a-m", lang);
				break;
			case 'Q':
				/* Shorthand for "Today", "Yesterday", or ABdY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
				{
					struct timeval now;
					struct tm tmnow;

					gettimeofday(&now,NULL);
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						res = wait_file(chan,ints, "digits/today",lang);
					} else if (beg_today - 86400 < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
					}
				}
				break;
			case 'q':
				/* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
				/* XXX As emphasized elsewhere, this should the native way in your
				 * language to say the date, with changes in what you say, depending
				 * upon how recent the date is. XXX */
				{
					struct timeval now;
					struct tm tmnow;

					gettimeofday(&now,NULL);
					/* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
					/* In any case, it saves not having to do ast_mktime() */
					beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
					if (beg_today < time) {
						/* Today */
						res = wait_file(chan,ints, "digits/today",lang);
					} else if ((beg_today - 86400) < time) {
						/* Yesterday */
						res = wait_file(chan,ints, "digits/yesterday",lang);
					} else if (beg_today - 86400 * 6 < time) {
						/* Within the last week */
						res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
					} else {
						res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
				res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/y' M", timezone);
				break;
			case 'S':
				/* Seconds */
				if (tm.tm_sec == 0) {
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
					res = wait_file(chan,ints,nextmsg,lang);
				} else if (tm.tm_sec < 10) {
					res = wait_file(chan,ints, "digits/oh",lang);
					if (!res) {
						snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
						res = wait_file(chan,ints,nextmsg,lang);
					}
				} else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
					res = wait_file(chan,ints,nextmsg,lang);
				} else {
					int ten, one;
					ten = (tm.tm_sec / 10) * 10;
					one = (tm.tm_sec % 10);
					snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
					res = wait_file(chan,ints,nextmsg,lang);
					if (!res) {
						/* Fifty, not fifty-zero */
						if (one != 0) {
							snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
							res = wait_file(chan,ints,nextmsg,lang);
						}
					}
				}
				break;
			case 'T':
				res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
				break;