private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi) {
Calendar cal = dtfi.Calendar;
StringBuilder result = new StringBuilder();
// This is a flag to indicate if we are format the dates using Hebrew calendar.
bool isHebrewCalendar = (cal.ID == Calendar.CAL_HEBREW);
// This is a flag to indicate if we are formating hour/minute/second only.
bool bTimeOnly = true;
int i = 0;
int tokenLen, hour12;
while (i < format.Length) {
char ch = format[i];
int nextChar;
switch (ch) {
case 'g':
tokenLen = ParseRepeatPattern(format, i, ch);
result.Append(dtfi.GetEraName(cal.GetEra(dateTime)));
break;
case 'h':
tokenLen = ParseRepeatPattern(format, i, ch);
hour12 = dateTime.Hour % 12;
if (hour12 == 0)
{
hour12 = 12;
}
FormatDigits(result, hour12, tokenLen);
break;
case 'H':
tokenLen = ParseRepeatPattern(format, i, ch);
FormatDigits(result, dateTime.Hour, tokenLen);
break;
case 'm':
tokenLen = ParseRepeatPattern(format, i, ch);
FormatDigits(result, dateTime.Minute, tokenLen);
break;
case 's':
tokenLen = ParseRepeatPattern(format, i, ch);
FormatDigits(result, dateTime.Second, tokenLen);
break;
case 'f':
case 'F':
tokenLen = ParseRepeatPattern(format, i, ch);
if (tokenLen <= MaxSecondsFractionDigits) {
long fraction = (dateTime.Ticks % Calendar.TicksPerSecond);
fraction = fraction / (long)Math.Pow(10, 7 - tokenLen);
if (ch == 'f') {
result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
}
else {
int effectiveDigits = tokenLen;
while (effectiveDigits > 0) {
if (fraction % 10 == 0) {
fraction = fraction / 10;
effectiveDigits--;
}
else {
break;
}
}
if (effectiveDigits > 0) {
result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
}
else {
// No fraction to emit, so see if we should remove decimal also.
if (result.Length > 0 && result[result.Length - 1] == '.') {
result.Remove(result.Length - 1, 1);
}
}
}
} else {
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
break;
case 't':
tokenLen = ParseRepeatPattern(format, i, ch);
if (tokenLen == 1)
{
if (dateTime.Hour < 12)
{
if (dtfi.AMDesignator.Length >= 1)
{
result.Append(dtfi.AMDesignator[0]);
}
}
else
{
if (dtfi.PMDesignator.Length >= 1)
{
result.Append(dtfi.PMDesignator[0]);
}
}
}
else
{
result.Append((dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator));
}
break;
case 'd':
//
// tokenLen == 1 : Day of month as digits with no leading zero.
// tokenLen == 2 : Day of month as digits with leading zero for single-digit months.
// tokenLen == 3 : Day of week as a three-leter abbreviation.
// tokenLen >= 4 : Day of week as its full name.
//
tokenLen = ParseRepeatPattern(format, i, ch);
if (tokenLen <= 2)
{
int day = cal.GetDayOfMonth(dateTime);
if (isHebrewCalendar) {
// For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values.
HebrewFormatDigits(result, day);
} else {
FormatDigits(result, day, tokenLen);
}
}
else
{
int dayOfWeek = (int)cal.GetDayOfWeek(dateTime);
result.Append(FormatDayOfWeek(dayOfWeek, tokenLen, dtfi));
}
bTimeOnly = false;
break;
case 'M':
//
// tokenLen == 1 : Month as digits with no leading zero.
// tokenLen == 2 : Month as digits with leading zero for single-digit months.
// tokenLen == 3 : Month as a three-letter abbreviation.
// tokenLen >= 4 : Month as its full name.
//
tokenLen = ParseRepeatPattern(format, i, ch);
int month = cal.GetMonth(dateTime);
if (tokenLen <= 2)
{
if (isHebrewCalendar) {
// For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values.
HebrewFormatDigits(result, month);
} else {
FormatDigits(result, month, tokenLen);
}
}
else {
if (isHebrewCalendar) {
result.Append(FormatHebrewMonthName(dateTime, month, tokenLen, dtfi));
} else {
if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && tokenLen >= 4) {
result.Append(
dtfi.internalGetMonthName(
month,
IsUseGenitiveForm(format, i, tokenLen, 'd')? MonthNameStyles.Genitive : MonthNameStyles.Regular,
false));
} else {
result.Append(FormatMonth(month, tokenLen, dtfi));
}
}
}
bTimeOnly = false;
break;
case 'y':
// Notes about OS behavior:
// y: Always print (year % 100). No leading zero.
// yy: Always print (year % 100) with leading zero.
// yyy/yyyy/yyyyy/... : Print year value. No leading zero.
int year = cal.GetYear(dateTime);
tokenLen = ParseRepeatPattern(format, i, ch);
if (dtfi.HasForceTwoDigitYears) {
FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2);
}
else if (cal.ID == Calendar.CAL_HEBREW) {
HebrewFormatDigits(result, year);
}
else {
if (tokenLen <= 2) {
FormatDigits(result, year % 100, tokenLen);
}
else {
String fmtPattern = "D" + tokenLen;
result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture));
}
}
bTimeOnly = false;
break;
case 'z':
//
// Output the offset of the timezone according to the system timezone setting.
//
tokenLen = ParseRepeatPattern(format, i, ch);
TimeSpan offset;
if (bTimeOnly && dateTime.Ticks < Calendar.TicksPerDay) {
offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
} else {
if (dateTime.Kind == DateTimeKind.Utc) {
InvalidFormatForUtc(format, dateTime);
offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.SpecifyKind(dateTime, DateTimeKind.Local));
}
else {
offset = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
}
}
switch (tokenLen)
{
case 1:
result.Append((offset.Hours).ToString("+0;-0", CultureInfo.InvariantCulture));
break;
case 2:
result.Append((offset.Hours).ToString("+00;-00", CultureInfo.InvariantCulture));
break;
default:
if (offset.Ticks >= 0) {
result.Append(String.Format(CultureInfo.InvariantCulture, "+{0:00}:{1:00}", offset.Hours, offset.Minutes));
} else {
// When the offset is negative, note that the offset.Minute is also negative.
// So use should use -offset.Minute to get the postive value.
result.Append(String.Format(CultureInfo.InvariantCulture, "-{0:00}:{1:00}", -offset.Hours, -offset.Minutes));
}
break;
}
break;
case 'K':
tokenLen = 1;
// The objective of this format is to round trip the Kind value and preserve the time zone
switch (dateTime.Kind) {
case DateTimeKind.Local:
// This should output the local offset, e.g. "-07:00"
TimeSpan localOffset = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
if (localOffset.Ticks >= 0) {
result.Append(String.Format(CultureInfo.InvariantCulture, "+{0:00}:{1:00}", localOffset.Hours, localOffset.Minutes));
} else {
// When the offset is negative, note that the localOffset.Minute is also negative.
// So use should use -localOffset.Minute to get the postive value.
result.Append(String.Format(CultureInfo.InvariantCulture, "-{0:00}:{1:00}", -localOffset.Hours, -localOffset.Minutes));
}
break;
case DateTimeKind.Utc:
// The 'Z' constant is a marker for a UTC date
result.Append("Z");
break;
default:
// If the kind is unspecified, we output nothing here
break;
}
break;
case ':':
result.Append(dtfi.TimeSeparator);
tokenLen = 1;
break;
case '/':
result.Append(dtfi.DateSeparator);
tokenLen = 1;
break;
case '\'':
case '\"':
StringBuilder enquotedString = new StringBuilder();
tokenLen = ParseQuoteString(format, i, enquotedString);
result.Append(enquotedString);
break;
case '%':
// Optional format character.
// For example, format string "%d" will print day of month
// without leading zero. Most of the cases, "%" can be ignored.
nextChar = ParseNextChar(format, i);
// nextChar will be -1 if we already reach the end of the format string.
// Besides, we will not allow "%%" appear in the pattern.
if (nextChar >= 0 && nextChar != (int)'%') {
result.Append(FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi));
tokenLen = 2;
}
else
{
//
// This means that '%' is at the end of the format string or
// "%%" appears in the format string.
//
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
break;
case '\\':
//
nextChar = ParseNextChar(format, i);
if (nextChar >= 0)
{
result.Append(((char)nextChar));
tokenLen = 2;
}
else
{
//
// This means that '\' is at the end of the formatting string.
//
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
break;
default:
result.Append(ch);
tokenLen = 1;
break;
}
i += tokenLen;
}
return (result.ToString());
}