WindowsXP-SP1/shell/shlwapi/time.cpp

314 lines
11 KiB
C++

#include "priv.h"
#ifdef _X86_
#include <w95wraps.h>
#endif
#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;
// Save the tail end (the part after the "Monday" string)
lstrcpyn(szTemp, &pszModifier[cchDayOfWeek], SIZECHARS(szTemp));
// 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
lstrcpy(pszModifier, szRelativeDay);
// put back the tail end
lstrcat(pszModifier, 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;
}