#include "priv.h" #include "ids.h" #include #ifndef DATE_LTRREADING #define DATE_LTRREADING 0x00000010 //FEATURE: figure out why we have to do this, and fix it. #define DATE_RTLREADING 0x00000020 #endif /*------------------------------------------------------------------------- Purpose: Calls GetDateFormat and tries to replace the day with a relative reference like "Today" or "Yesterday". Returns the count of characters written to pszBuf. */ int GetRelativeDateFormat( DWORD dwDateFlags, DWORD * pdwFlags, SYSTEMTIME * pstDate, LPWSTR pszBuf, int cchBuf) { int cch; ASSERT(pdwFlags); ASSERT(pstDate); ASSERT(pszBuf); // Assume that no relative date is applied, so clear the bit // for now. *pdwFlags &= ~FDTF_RELATIVE; // Get the Win32 date format. (GetDateFormat's return value includes // the null terminator.) cch = GetDateFormat(LOCALE_USER_DEFAULT, dwDateFlags, pstDate, NULL, pszBuf, cchBuf); if (0 < cch) { SYSTEMTIME stCurrentTime; int iDay = 0; // 1 = today, -1 = yesterday, 0 = neither today nor yesterday. // Now see if the date merits a replacement to "Yesterday" or "Today". GetLocalTime(&stCurrentTime); // get the current date // Does it match the current day? if (pstDate->wYear == stCurrentTime.wYear && pstDate->wMonth == stCurrentTime.wMonth && pstDate->wDay == stCurrentTime.wDay) { // Yes iDay = 1; } else { // No; maybe it matches yesterday FILETIME ftYesterday; SYSTEMTIME stYesterday; // Compute yesterday's date by converting to FILETIME, // subtracting one day, then converting back. SystemTimeToFileTime(&stCurrentTime, &ftYesterday); DecrementFILETIME(&ftYesterday, FT_ONEDAY); FileTimeToSystemTime(&ftYesterday, &stYesterday); // Does it match yesterday? if (pstDate->wYear == stYesterday.wYear && pstDate->wMonth == stYesterday.wMonth && pstDate->wDay == stYesterday.wDay) { // Yes iDay = -1; } } // Should we try replacing the day? if (0 != iDay) { // Yes TCHAR szDayOfWeek[32]; LPTSTR pszModifier; int cchDayOfWeek; cchDayOfWeek = MLLoadString((IDS_DAYSOFTHEWEEK + pstDate->wDayOfWeek), szDayOfWeek, SIZECHARS(szDayOfWeek)); // Search for the day of week text in the string we got back. // Depending on the user's regional settings, there might not // be a day in the long-date format... pszModifier = StrStr(pszBuf, szDayOfWeek); if (pszModifier) { // We found the day in the string, so replace it with // "Today" or "Yesterday" TCHAR szTemp[64]; TCHAR szRelativeDay[32]; int cchRelativeDay; int cchModifier; cchModifier = cchBuf - ((int)(pszModifier - pszBuf)); // Save the tail end (the part after the "Monday" string) StringCchCopy(szTemp, SIZECHARS(szTemp), &pszModifier[cchDayOfWeek]); // Load the appropriate string ("Yesterday" or "Today"). // If the string is empty (localizers might need to do this // if this logic isn't locale-friendly), don't bother doing // anything. cchRelativeDay = MLLoadString((1 == iDay) ? IDS_TODAY : IDS_YESTERDAY, szRelativeDay, SIZECHARS(szRelativeDay)); if (0 < cchRelativeDay) { // Make sure that we have enough room for the replacement // (cch already accounts for the null terminator) if (cch - cchDayOfWeek + cchRelativeDay <= cchBuf) { // copy the friendly name over the day of the week StringCchCopy(pszModifier, cchModifier , szRelativeDay); // put back the tail end StringCchCat(pszModifier, cchModifier, szTemp); cch = cch - cchDayOfWeek + cchRelativeDay; *pdwFlags |= FDTF_RELATIVE; } } } } } return cch; } #define LRM 0x200E // UNICODE Left-to-right mark control character #define RLM 0x200F // UNICODE Left-to-right mark control character /*------------------------------------------------------------------------- Purpose: Constructs a displayname form of the file time. *pdwFlags may be NULL, in which case FDTF_DEFAULT is assumed. Other valid flags are: FDTF_DEFAULT "3/29/98 7:48 PM" FDTF_SHORTTIME "7:48 PM" FDTF_SHORTDATE "3/29/98" FDTF_LONGDATE "Monday, March 29, 1998" FDTF_LONGTIME "7:48:33 PM" FDTF_RELATIVE only works with FDTF_LONGDATE. If possible, replace the day with "Yesterday" or "Today": "Yesterday, March 29, 1998" This function updates *pdwFlags to indicate which sections of the string were actually set. For example, if FDTF_RELATIVE is passed in, but no relative date conversion was performed, then FDTF_RELATIVE is cleared before returning. If the date is the magic "Sorry, I don't know what date it is" value that FAT uses, then we return an empty string. */ STDAPI_(int) SHFormatDateTimeW(const FILETIME UNALIGNED *puft, DWORD *pdwFlags, LPWSTR pszBuf, UINT ucchBuf) { int cchBuf = ucchBuf; int cchBufSav = cchBuf; FILETIME ftLocal, ftInput = *puft; // allign the data ASSERT(IS_VALID_READ_PTR(puft, FILETIME)); ASSERT(IS_VALID_WRITE_BUFFER(pszBuf, WCHAR, cchBuf)); ASSERT(NULL == pdwFlags || IS_VALID_WRITE_PTR(pdwFlags, DWORD)); DWORD dwFlags = 0; FileTimeToLocalFileTime(&ftInput, &ftLocal); if (FILETIMEtoInt64(ftInput) == FT_NTFS_UNKNOWNGMT || FILETIMEtoInt64(ftLocal) == FT_FAT_UNKNOWNLOCAL) { // This date is uninitialized. Don't show a bogus "10/10/72" string. if (0 < cchBuf) *pszBuf = 0; } else if (0 < cchBuf) { int cch; SYSTEMTIME st; DWORD dwDateFlags = DATE_SHORTDATE; // default DWORD dwTimeFlags = TIME_NOSECONDS; // default dwFlags = pdwFlags ? *pdwFlags : FDTF_DEFAULT; // Initialize the flags we're going to use if (dwFlags & FDTF_LONGDATE) dwDateFlags = DATE_LONGDATE; else dwFlags &= ~FDTF_RELATIVE; // can't show relative dates w/o long dates if (dwFlags & FDTF_LTRDATE) dwDateFlags |= DATE_LTRREADING; else if(dwFlags & FDTF_RTLDATE) dwDateFlags |= DATE_RTLREADING; if (dwFlags & FDTF_LONGTIME) dwTimeFlags &= ~TIME_NOSECONDS; FileTimeToSystemTime(&ftLocal, &st); cchBuf--; // Account for null terminator first if (dwFlags & (FDTF_LONGDATE | FDTF_SHORTDATE)) { // Get the date if (dwFlags & FDTF_RELATIVE) cch = GetRelativeDateFormat(dwDateFlags, &dwFlags, &st, pszBuf, cchBuf); else cch = GetDateFormat(LOCALE_USER_DEFAULT, dwDateFlags, &st, NULL, pszBuf, cchBuf); ASSERT(0 <= cch && cch <= cchBuf); if (0 < cch) { cch--; // (null terminator was counted above, so don't count it again) ASSERT('\0'==pszBuf[cch]); } else dwFlags &= ~(FDTF_LONGDATE | FDTF_SHORTDATE); // no date, so clear these bits cchBuf -= cch; pszBuf += cch; // Are we tacking on the time too? if (dwFlags & (FDTF_SHORTTIME | FDTF_LONGTIME)) { // Yes; for long dates, separate with a comma, otherwise // separate with a space. if (dwFlags & FDTF_LONGDATE) { WCHAR szT[8]; cch = MLLoadString(IDS_LONGDATE_SEP, szT, SIZECHARS(szT)); StrCpyNW(pszBuf, szT, cchBuf); int cchCopied = min(cchBuf, cch); cchBuf -= cchCopied; pszBuf += cchCopied; } else { if (cchBuf>0) { *pszBuf++ = TEXT(' '); *pszBuf = 0; // (in case GetTimeFormat doesn't add anything) cchBuf--; } } // [msadek]; need to insert strong a Unicode control character to simulate // a strong run in the opposite base direction to enforce // correct display of concatinated string in all cases if (dwFlags & FDTF_RTLDATE) { if (cchBuf>=2) { *pszBuf++ = LRM; // simulate an opposite run *pszBuf++ = RLM; // force RTL display of the time part. *pszBuf = 0; cchBuf -= 2; } } else if (dwFlags & FDTF_LTRDATE) { if (cchBuf>=2) { *pszBuf++ = RLM; // simulate an opposite run *pszBuf++ = LRM; // force LTR display of the time part. *pszBuf = 0; cchBuf -= 2; } } } } if (dwFlags & (FDTF_SHORTTIME | FDTF_LONGTIME)) { // Get the time cch = GetTimeFormat(LOCALE_USER_DEFAULT, dwTimeFlags, &st, NULL, pszBuf, cchBuf); if (0 < cch) cch--; // (null terminator was counted above, so don't count it again) else dwFlags &= ~(FDTF_LONGTIME | FDTF_SHORTTIME); // no time, so clear these bits cchBuf -= cch; } } if (pdwFlags) *pdwFlags = dwFlags; return cchBufSav - cchBuf; } STDAPI_(int) SHFormatDateTimeA(const FILETIME UNALIGNED *pft, DWORD *pdwFlags, LPSTR pszBuf, UINT cchBuf) { WCHAR wsz[256]; int cchRet = SHFormatDateTimeW(pft, pdwFlags, wsz, SIZECHARS(wsz)); if (0 < cchRet) { cchRet = SHUnicodeToAnsi(wsz, pszBuf, cchBuf); } else if (0 < cchBuf) { *pszBuf = 0; } return cchRet; }