|
|
#include "priv.h"
#include "ids.h"
#include <mluisupp.h>
#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; }
|