概要

コマンドラインで動作し、第一引数で指定されたフォルダー内のファイルについて、日時を修正します。
第一引数のみ指定された場合は、現在の日時。
第二引数で年月日を指定された場合は、指定年月日で時刻は0時0分0秒
第二引数で年月日、第三引数で時刻を指定された場合は、指定日時に修正します。
なお、FAT12,FAT16,FAT32の場合は、ファイルシステムが1秒単位で保存できないため、2秒単位となります。
また、作成日時、アクセス日時についてはFAT12,FAT16,FAT32では必須でないためサポートされないことが多いようです。したがって、修正してもそのとおりとならないことが多いです。
Visual C++ 2008 Standard Edition 32/64bit対応

使い方の例

 カレントフォルダのtestフォルダ内のファイルを現在日時に修正する。
  mtouch test
 カレントフォルダのtestフォルダ内のファイルを指定年月日に修正する。
  mtouch test 2000/1/1
 カレントフォルダのtestフォルダ内のファイルを指定年月日に指定時刻に修正する。
  mtouch test 2000/1/1 1:2:3

プログラム説明

全体の流れ

コマンドラインの引数を解析し、修正するローカルタイム日時を構造体SYSTEMTIME stに保存します。
indFirstFile及びFindNextFile APIでフォルダー内のファイルを検索し、ファイルのリストを作成します。ファイル検索後はCloseHandleを実行します。
検索されたファイルは、CreateFileでハンドルを取得し、SetFileTime APIで日時を修正し、CloseHandleを実行します。


ファイル日時の取得

ファイル日時を取得する場合は、ファイルのオープン後にGetFileTime APIまたは、FindFirstFile及びFindNextFileで取得できます。
GetFileTimeで取得する場合は、FILETIME(1601 年 1 月 1 日午前 12 時からの 100 ナノ秒間隔の数を表す 64 ビット値でUTC)であるため、これをFILETIMEのローカルタイムに変換し、さらにSYSTEMTIME構造体で年月日を個別に得ることができます。

	GetFileTime(hFile,&CreationTime,&AccessTime,&WriteTime);
	FileTimeToLocalFileTime(&CreationTime,&localTime);	//	UTC→ローカルタイム
	FileTimeToSystemTime(&localTime,&systemTime);	//	FILETIMEをSYSTEMTIMEに変換
	printf("\tCreationTime %i/%i/%i %i:%i:%i\n",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute,systemTime.wSecond);

ファイル日時の修正

ファイル日時を修正するためには、SetFileTime APIを使いますが、引数がFILETIME(1601 年 1 月 1 日午前 12 時からの 100 ナノ秒間隔の数を表す 64 ビット値でUTC)であるため、まずローカルタイムからFILETIMEを作成し、これをUTCに変換します。

	SYSTEMTIME st;
	printf("変更後 Create,Write,Access %i/%i/%i %i:%i:%i\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
	if(SystemTimeToFileTime(&st,&localTime)==0){	//	SYSTEMTIMEをFILETIMEに変換
		puts("\tSystemTimeToFileTime API error");
	}
	if(LocalFileTimeToFileTime(&localTime,&AccessTime2)==0){	//	ローカルタイム→UTC
		puts("\tLocalFileTimeToFileTim API error");
	}
	SetFileTime(hFile,&AccessTime2,&AccessTime2,&AccessTime2);

mtouch.cpp

// 指定されたフォルダーに存在するファイルの日付を変更する。
// Visual C++ 2008 コマンドライン用


#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include "list.hpp"

struct FILE_LINK{
	TCHAR* name;			//	ファイル名
	FILE_LINK* next;	//	次のファイル名へのポインタ
	FILE_LINK(){
		name=0;
		next=0;
	}
};

struct FILE_LIST : public LIST{
	void append(FILE_LINK* p){
		LIST::append((void*)p);
	}
	FILE_LINK* next(void){
		return (FILE_LINK*)LIST::next();
	}
	FILE_LINK* first(void){
		return (FILE_LINK*)LIST::first();
	}
};

time_t get_now_time(tm& now_tm );	//	現在の時刻を取得(ローカルタイム)
void tm_to_systemtime(struct tm& stm,SYSTEMTIME& st);	//	構造体tmを構造体SYSTEMTIMEに変換する
int get_dir_list(FILE_LIST& list,TCHAR* pass,TCHAR* card);	//	サブディレクトリ内の指定ファイルを検索する
bool atodate(TCHAR* s,SYSTEMTIME& t);	// 文字列を日付に変換する。	文字列例 2004/12/12
bool atotime(TCHAR* s,SYSTEMTIME& t);	// 文字列を時刻に変換する。	文字列例 12:12:12
int file_time_touch(TCHAR* file_name,FILETIME& AccessTime2);	// 指定されたファイルの日付を変更する

int _tmain(int argc, TCHAR** argv){
	TCHAR* pass;
	char sjis[MAX_PATH*4];
	TCHAR FullPath[MAX_PATH*2];
	TCHAR* FilePart;

	tm now_tm;
	SYSTEMTIME st;
	FILETIME localTime;
	FILETIME AccessTime2;

	switch(argc){
	case 1:
		puts("使い方\nmtouch フォルダー名 [日付] [時刻]\n[]内は省略可能\n使用例\nmtouch d:\\ 2011/1/1 12:00:00\n");
		exit(1);
		break;
	case 2:	//	フォルダー名のみ指定されている場合 現在の日付で時刻は0:0:0とする。
		pass=argv[1];
		get_now_time(now_tm); // 現在時刻をローカルタイムで取得
		tm_to_systemtime(now_tm,st);
		st.wHour=0;
		st.wMinute=0;
		st.wSecond=0;
		st.wMilliseconds=0;
		break;
	case 3:	//	フォルダー名および日付が指定されている場合 指定された日付で時刻は0:0:0とする。
		pass=argv[1];
		if(atodate(argv[2],st)==false){
			puts("日付の指定が正しくありません。");
			exit(2);
		}
		st.wHour=0;
		st.wMinute=0;
		st.wSecond=0;
		st.wMilliseconds=0;
		break;
	case 4:
		pass=argv[1];
		if(atodate(argv[2],st)==false){
			puts("日付の指定が正しくありません。");
			exit(2);
		}
		if(atotime(argv[3],st)==false){
			puts("時刻の指定が正しくありません。");
			exit(2);
		}
		break;
	}

	printf("変更後 Create,Write,Access %i/%i/%i %i:%i:%i\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
	if(SystemTimeToFileTime(&st,&localTime)==0){	//	SYSTEMTIMEをFILETIMEに変換
		puts("\tSystemTimeToFileTime API error");
	}
	if(LocalFileTimeToFileTime(&localTime,&AccessTime2)==0){	//	ローカルタイム→UTC
		puts("\tLocalFileTimeToFileTim API error");
	}
	GetFullPathName(pass,sizeof(FullPath)/sizeof(TCHAR),FullPath,&FilePart);	//絶対パスに変更
	if(FilePart==NULL){
		FullPath[_tcslen(FullPath)-1]=_T('\0');	//	ルートディレクトリが指定された場合は\を削除する
	}

	int n=0;	//	ファイル番号

	FILE_LIST file_list;
	get_dir_list(file_list,FullPath,TEXT("*.*"));
	int num=file_list.get_max();	//	ファイル数
	FILE_LINK* p;

	n=0;
	p=file_list.first();
	while(p){
		WideCharToMultiByte(932,0,p->name,-1,sjis,sizeof(sjis),NULL,NULL);
		puts(sjis);
		file_time_touch(p->name,AccessTime2);
		p=file_list.next();
	}

	printf("%i個のファイルを変更しました。\n",num);
	return 0;
}

//	現在の時刻を取得(ローカルタイム)

time_t get_now_time(tm& now_tm ){
	time_t now_time_t;
	tm lim_tm;
	time(&now_time_t);
	errno_t err;
	err=localtime_s(&now_tm,&now_time_t);
	gmtime_s(&lim_tm,(time_t*)&now_time_t);
	return now_time_t;
}

//	構造体tmを構造体SYSTEMTIMEに変換する

void tm_to_systemtime(struct tm& stm,SYSTEMTIME& st){
	st.wYear=stm.tm_year+1900;
	st.wMonth=stm.tm_mon+1;
	st.wDay=stm.tm_mday;
	st.wDayOfWeek=stm.tm_wday;
	st.wHour=stm.tm_hour;
	st.wMinute=stm.tm_min;
	st.wSecond=stm.tm_sec;
	st.wMilliseconds=0;

}

//	サブディレクトリ内の指定ファイルを検索する

int get_dir_list(FILE_LIST& list,TCHAR* pass,TCHAR* card){
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	TCHAR find_pass[MAX_PATH];
	TCHAR make_pass[MAX_PATH];
	int num=0;	//	見つかったファイル数

	_stprintf_s(find_pass,sizeof(find_pass)/sizeof(TCHAR),TEXT("%s\\%s"),pass,card);	//	ファイルの検索
	hFind = FindFirstFile(find_pass, &FindFileData);
	if(hFind != INVALID_HANDLE_VALUE){
		do{ 
			if( !(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
				++num;
				_stprintf_s(make_pass,sizeof(make_pass)/sizeof(TCHAR),TEXT("%s\\%s"),pass,FindFileData.cFileName);
				size_t len=_tcslen(make_pass);
				TCHAR* n=new TCHAR[len+1];
				_tcscpy_s(n,len+1,make_pass);
				FILE_LINK* p=new FILE_LINK;
				p->name=n;
				p->next=0;
				list.append(p);
			}
			
		}while(FindNextFile(hFind,&FindFileData));
		CloseHandle(hFind);
		return num;
	}else
		return num;
}		

// 文字列を日付に変換する。	文字列例 2004/12/12

bool atodate(TCHAR* s,SYSTEMTIME& t){
	TCHAR* p =s;
	TCHAR* tp=s;
	int n=0;
	TCHAR* wp[4];
	wp[0]=s;
	while(*s){
		if(*s==_T('/')){
			*s=_T('\0');
			wp[n++]=tp;
			tp=s+1;
		}
		if(n==4)
			return false;
		++s;
	}
	if(n==2){
		t.wYear=_ttoi(wp[0]);
		t.wMonth=_ttoi(wp[1]);
		t.wDay=_ttoi(tp);
			return true;
	}else
		return false;
}

// 文字列を時刻に変換する。	文字列例 12:12:12

bool atotime(TCHAR* s,SYSTEMTIME& t){
	TCHAR* p =s;
	TCHAR* tp=s;
	int n=0;
	TCHAR* wp[4];
	wp[0]=s;
	while(*s){
		if(*s==_T(':')){
			*s= _T('\0');
			wp[n++]=tp;
			tp=s+1;
		}
		if(n==4)
			return false;
		++s;
	}
	switch(n){
	case 2:
		t.wHour=_ttoi(wp[0]);
		t.wMinute=_ttoi(wp[1]);
		t.wSecond=_ttoi(tp);
		t.wMilliseconds=0;
		return true;
	case 1:
		t.wHour=_ttoi(wp[0]);
		t.wMinute=_ttoi(tp);
		t.wSecond=0;
		t.wMilliseconds=0;
		return true;
	case 0:
		t.wHour=_ttoi(tp);
		t.wMinute=0;
		t.wSecond=0;
		t.wMilliseconds=0;
		return true;
	default:
		return false;
	}
}

// 指定されたファイルの日付を変更する

int file_time_touch(TCHAR* file_name,FILETIME& AccessTime2){
	HANDLE hFile;
	hFile=CreateFile(file_name,GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile==INVALID_HANDLE_VALUE){
		puts("\tFile Open Error");
		return -1;
	}
	FILETIME CreationTime;
	FILETIME AccessTime;
	FILETIME WriteTime;
	FILETIME localTime;
	SYSTEMTIME systemTime;

//	現在のファイルの日時を取得表示

	GetFileTime(hFile,&CreationTime,&AccessTime,&WriteTime);
	FileTimeToLocalFileTime(&CreationTime,&localTime);	//	UTC→ローカルタイム
	FileTimeToSystemTime(&localTime,&systemTime);	//	FILETIMEをSYSTEMTIMEに変換

	printf("\t変更前 Create %i/%i/%i %i:%i:%i\n",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute,systemTime.wSecond);

	SystemTimeToFileTime(&systemTime,&localTime);	//	SYSTEMTIMEをFILETIMEに変換
	LocalFileTimeToFileTime(&localTime,&CreationTime);	//	ローカルタイム→UTC

	FileTimeToLocalFileTime(&WriteTime,&localTime);	//	UTC→ローカルタイム
	FileTimeToSystemTime(&localTime,&systemTime);	//	FILETIMEをSYSTEMTIMEに変換

	printf("\t変更前 Write  %i/%i/%i %i:%i:%i\n",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute,systemTime.wSecond);

	SystemTimeToFileTime(&systemTime,&localTime);	//	SYSTEMTIMEをFILETIMEに変換
	LocalFileTimeToFileTime(&localTime,&WriteTime);	//	ローカルタイム→UTC

	FileTimeToLocalFileTime(&AccessTime,&localTime);	//	UTC→ローカルタイム
	FileTimeToSystemTime(&localTime,&systemTime);	//	FILETIMEをSYSTEMTIMEに変換

	printf("\t変更前 Access %i/%i/%i %i:%i:%i\n",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute,systemTime.wSecond);

	if(SetFileTime(hFile,&AccessTime2,&AccessTime2,&AccessTime2)==0){
		puts("\tSetFileTime API error");
		CloseHandle(hFile);
		return -2;
	}
	CloseHandle(hFile);
	return 0;
}

list.hpp

#ifndef LIST_HPP
#define LIST_HPP 1

class LINK{
	LINK* next;
	LINK* back;
	void* data;
protected:
	LINK(){
		next=0;
		back=0;
		data=0;
	}
	void set(void* d){
		data=d;
	}
	friend class LIST;
	friend int cmp(const void* d,const void* s){
		if((char*)d>(char*)s)
			return 1;
		if((char*)d<(char*)s)
			return -1;
		return 0;
		
	}
};

typedef void* LPVOID;

class LIST{
	LINK* top;	//	リストの先頭
	LINK* end;	//	リストの最後尾
	LINK* pos;	//	リストのカレントポジション
	int max;	//	リストの要素数
protected:
	LIST(){
		pos=top=end=0;
		max=0;
	}
	void append(void* d){	//	リストの最後尾にデータを追加
		LINK* p=new LINK;
		++max;
		p->data=d;
		if(top==0){
			pos=top=end=p;
		}else{
			end->next=p;
			p->back=end;
			end=p;
		}
	}
	void* next(void){	//	カレントポジジョンからデータを得た後、カレントポジションを1個進める。
		LINK* t=pos;
		if(pos==0){
			return 0;
		}else{
			pos=pos->next;
			return t->data;
		}
	}
	void* first(void){	//	カレントポジションを先頭に移動させ先頭データを得た後、カレントポジションを1個進める。
		pos=top;
		return next();
	}
	void all_del(void){	//	リスト全部を削除する
		while(top){
			LINK* t=top->next;
			delete top;
			top=t;
		}
		top=end=pos=0;
		max=0;
	}
	void repeat_del(int(*cmp )(const void* d,const void* s)){	//	ソートされたリストから重複する要素を削除
		LINK* lp=top;
		if(lp){
			LINK* bp=0;
			while(lp){
				if(bp){
					if( (*cmp)((void**)&lp->data,(void**)&bp->data) ){	//	前の要素と異なる場合
						bp=lp;
					}else{	//	前の要素と同じ場合
						LINK* t=lp->next;
						del(lp);
						--max;
						lp=t;
						continue;
					}
				}else
					bp=lp;
				lp=lp->next;
			}
		}
	}	
	void qsort(int(*cmp )(const void* d,const void* s)){
		if(max){
			int n=0;
			LPVOID* vec=new LPVOID[max];	//	リストの要素を配列にコピーする
			if(vec==0)
				return;
			void* p;
			p=first();
			while(p){
				vec[n++]=p;
				p=next();
			}
			::qsort((void*)vec,max,sizeof(LPVOID),cmp);
			LINK* lp=top;						//	配列をリストにコピー
			n=0;
			while(lp){
				lp->set(vec[n++]);
				lp=lp->next;
			}
			delete vec;
		}
	}
	void del(LINK*	p){	// リストからpを削除する
		if(p){
			if(p==top){
				LINK* t=top;
				top=top->next;
				top->back=0;
				if(pos==p){
					pos=p->next;
				}
				delete p;
				return;
			}
			if(p==end){
				LINK* t=end;
				end=end->back;
				end->next=0;
				delete p;
				return;
			}
			LINK* t=p;
			p->back->next=p->next;
			p->next->back=p->back;
			if(pos==p){
				pos=p->next;
			}
			delete t;
		}

	}
public:
	int get_max(void){
		return max;
	}
};

#endif

 

mtouch.zip