C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[903] [VCL] StrToDateTime("2009-07-02 11:23:11"); 이 에러나는 경우..
장성호 [nasilso] 15012 읽음    2009-07-02 14:06
StrToDateTime("2009-07-02 11:23:11"); 이 에러나는 경우

혹시 다음과 같이 StrToDateTime 함수를 쓰면서
에러가 나는 경우를 격어 보신적 있으신가요?

void __fastcall TForm1::Button2Click(TObject *Sender)
{
    TDateTime dt=StrToDateTime("2009-07-02 11:23:11");
}
//---------------------------------------------------------------------------


아무리 봐도 전혀 문제가 전혀 문제가 없는 코드 같지 않나요?

예전에 그런 경험을 한적이 있었는데..
대부분의 pc에서는 아무 문제가 없는데..
특정 pc에서 예외를 발생시키더군요





왜그럴까요?

그것은  윈도우즈의 시스템 설정이 다른기 때문입니다.
"제어판" "국가 및 언어 옵션" 에서 숫자,통화,시간,날짜 등의 형식을 사용자 지정할수가 있는데..
이것을 사람마다 다르게 해 놓고 쓰는경우가 가끔 있더군요

특히나 만약 해외로 프로그램을 만들어 판다면
이런 사항은 반드시 고려해야할 사항이라 할수 있겠죠





제어판의 사용자 지정 날짜 Format을 프로그램에 적용하려면?

그럼 위 그림의 제어판 국가별 옵션 설정을 어떻게 프로그램에 적용할수 있을까요?

간단합니다
String이용하지 않고   DateUtils유닛에 있는 EncodeDateTime 함수를 사용하면  됩니다

#include "DateUtils.hpp"

void __fastcall TForm1::Button3Click(TObject *Sender)
{
   TDateTime dt=EncodeDateTime(2009,07,02,11,23,11,0);
   ShowMessage(dt.DateTimeString());
}
//---------------------------------------------------------------------------



제어판의 사용자 지정 날짜 Format에 관계없이 쓰려면?

그런데 반대로 위 제어판 옵션을 무시하고 항상 특정한 Format으로 나오게 하고 싶은경우는 어떻게 하면 될까요?


SysUtils에 보니 다음과 같은 global 변수들이 있네요

extern PACKAGE AnsiString CurrencyString;
extern PACKAGE Byte CurrencyFormat;
extern PACKAGE Byte NegCurrFormat;
extern PACKAGE char ThousandSeparator;
extern PACKAGE char DecimalSeparator;
extern PACKAGE Byte CurrencyDecimals;
extern PACKAGE char DateSeparator;
extern PACKAGE AnsiString ShortDateFormat;
extern PACKAGE AnsiString LongDateFormat;
extern PACKAGE char TimeSeparator;
extern PACKAGE AnsiString TimeAMString;
extern PACKAGE AnsiString TimePMString;
extern PACKAGE AnsiString ShortTimeFormat;
extern PACKAGE AnsiString LongTimeFormat;
extern PACKAGE AnsiString ShortMonthNames[12];
extern PACKAGE AnsiString LongMonthNames[12];
extern PACKAGE AnsiString ShortDayNames[7];
extern PACKAGE AnsiString LongDayNames[7];




다음과 같이 SysUtils의 내용을 ShowMessage로 뿌려봤습니다.
void __fastcall TForm1::Button4Click(TObject *Sender)
{
  TStringList *lst=new TStringList;
  lst->Add("CurrencyFormat="+    String(CurrencyFormat));  
  lst->Add("NegCurrFormat="+      String(NegCurrFormat));
  lst->Add("ThousandSeparator="+  String(ThousandSeparator));
  lst->Add("DecimalSeparator="+   String(DecimalSeparator));
  lst->Add("CurrencyDecimals="+   String(CurrencyDecimals));
  lst->Add("DateSeparator="+      String(DateSeparator));
  lst->Add("ShortDateFormat="+    ShortDateFormat);
  lst->Add("LongDateFormat="+     LongDateFormat);
  lst->Add("TimeSeparator="+      String(TimeSeparator));
  lst->Add("TimeAMString="+       TimeAMString);
  lst->Add("TimePMString="+       TimePMString);
  lst->Add("ShortTimeFormat="+    ShortTimeFormat);
  lst->Add("LongTimeFormat="+     LongTimeFormat);
  for(int i=0;i<12;i++)
    lst->Add("ShortMonthNames["+IntToStr(i)+"]="+ShortMonthNames[i]);
  for(int i=0;i<12;i++)
    lst->Add("LongMonthNames["+IntToStr(i)+"]="+ LongMonthNames[i]);
  for(int i=0;i<7;i++)
    lst->Add("ShortDayNames["+IntToStr(i)+"]="+   ShortDayNames[i]);
  for(int i=0;i<7;i++)
    lst->Add("LongDayNames["+IntToStr(i)+"]="+   LongDayNames[i]);

  ShowMessage(lst->Text);
  delete lst;
}
//---------------------------------------------------------------------------



다음과 같이 나오네요



정리하면

extern PACKAGE AnsiString CurrencyString;
extern PACKAGE Byte CurrencyFormat;
extern PACKAGE Byte NegCurrFormat;
extern PACKAGE char ThousandSeparator;         //천단위 구분문자
extern PACKAGE char DecimalSeparator;          //소숫점 구분문자
extern PACKAGE Byte CurrencyDecimals;   
extern PACKAGE char DateSeparator;             //날짜 구분문자
extern PACKAGE AnsiString ShortDateFormat;     //짧은 날짜 형식
extern PACKAGE AnsiString LongDateFormat;      //긴 날짜 형식
extern PACKAGE char TimeSeparator;             //시간 구분문자
extern PACKAGE AnsiString TimeAMString;        //오전을 나타내는 String
extern PACKAGE AnsiString TimePMString;        //오후를 나타내는 String 
extern PACKAGE AnsiString ShortTimeFormat;     //짧은 시간 형식
extern PACKAGE AnsiString LongTimeFormat;      //긴 시간 형식
extern PACKAGE AnsiString ShortMonthNames[12]; //1월~12월까지 짧은 표현형식
extern PACKAGE AnsiString LongMonthNames[12];  //1월~12월까지 긴 표현형식
extern PACKAGE AnsiString ShortDayNames[7];    //일요일~토요일까지 짧은 표현형식
extern PACKAGE AnsiString LongDayNames[7];     //일요일~토요일까지 긴 표현형식




위에 global변수는 프로그램이 시작할때
그리나까 VCLxx.bpl이 Load될때 System에서 정보를 가져와서 셋팅합니다.
그런후에 StrToDateTime 같은 함수에서 위 변수에 내용을 참조해서 적용하는것 입니다.

결론적으로
제어판의 사용자 지정 날짜 Format에 관계없이 쓰려면
위 SysUtils의 내용을 임의대로 바꾸어 쓰면 됩니다.

다음과 같이..
void __fastcall TForm1::Button2Click(TObject *Sender)
{
   char OrgDateSeparator,OrgTimeSeparator;
   OrgDateSeparator=DateSeparator;
   OrgTimeSeparator=TimeSeparator;
   DateSeparator='*';
   TimeSeparator='^';
   TDateTime dt=StrToDateTime("2009*07*02 11^23^11");
   DateSeparator=OrgDateSeparator;
   TimeSeparator=OrgTimeSeparator;

   ShowMessage(dt.DateTimeString());
}
//---------------------------------------------------------------------------


날짜 구분자뿐 아니라 순서도 바뀌는 경우에도
ShortDateFormat 또는 LongDateFormat을 수정해서 적용할수 있습니다.
void __fastcall TForm1::Button5Click(TObject *Sender)
{
   char OrgDateSeparator,OrgTimeSeparator;
   OrgDateSeparator=DateSeparator;
   OrgTimeSeparator=TimeSeparator;
   String sOrg=ShortDateFormat;
   ShortDateFormat="dd-mm-hhhh hh:nn:ss";
   DateSeparator='*';
   TimeSeparator='^';
   TDateTime dt=StrToDateTime("02*07*2009 11^23^11");
   DateSeparator=OrgDateSeparator;
   TimeSeparator=OrgTimeSeparator;
   ShortDateFormat=sOrg;

   ShowMessage(dt.DateTimeString());
}
//---------------------------------------------------------------------------


이것도 뭐 모른다고 해서
큰 문제될것 없는데...

좀더 완성도 높은 프로그램을 위해..
GLOBAL한 프로그램을 위해...

그럼..
김동욱 [kimehddnr]   2009-07-02 18:16 X
설정에 따라서는 'yyyy-mm-dd hh:nn' 이런 방식이 아니고

'dd-mm-yyyy hh:nn' 이런 식으로 완전히 틀리게 사용하는 경우도 있는거 같네요. ^^;;

이런 경우는 어떻게 하나요?
장성호 [nasilso]   2009-07-02 18:20 X
ShortDateFormat 이나 LongDateFormat 을 가지고 놀면 될듯 한데요...
범천 [elephans]   2009-07-03 09:40 X
VCL SysUtils에 복제된 윈도우 국가별 옵션사항들을 특정하게 수정, 사용할 때 주의할 점은,
이 값들이 다른 프로그램, 사용자가 제어판에서 값을 바꾸면 역시 연동해서 변경되어 집니다
프로그램에서 SysUtils.ShortDateFormat등을 수정해서 쓰더라도 이걸 염두에 두지 않으면 또 변환에러등이 발생합니다
TApplication에 제어판등에서 값이 변경되면 감지하는 Event가 있습니다, 이 때 다시 원하는 포맷으로 재지정해야 합니다
SysUtils에 Format값 등은 윈도우 복제값이므로 역으로 윈도우 제어판값이 변경되지는 않습니다
김태선 [cppbuilder]   2009-07-03 10:14 X
고전적인 방법으로는

sscanf("2009-07-02 11:23:11", "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);

이렇게 하면 시스템 설정과 관계없이 날짜 시간에 대한 스트링 값을 정수 형태로 가져올 수 있습니다.

아제나 [azena]   2009-07-03 13:28 X
구관이 명관이죠 ^^ 태선님 방법으로 ymd hms를 얻어서 EncodeDateTime을 이용하는게 가장 심플하죠.
그러는 본인은 TDateTime은 8바이트이고 DBMS 호환성이 떨어져서 unix timestamp를 주로 이용합니다. ㅋㅋ
#include <time.h>
time_t currentTime = time(NULL);
대부분의 DBMS들을 보면 UNIX TIMESTAMP를 DB 내부의 DATETIME 형식으로 변경해주는 함수들을 가지고 있습니다.
장성호 [nasilso]   2010-04-01 17:18 X
범천님게서 댓글 단것 이제야 보네요..

http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_qna&no=60657

Application->OnSettingChange 이벤트를 이용하면 되는군요
아루스 [tinydew4]   2010-07-19 15:15 X
이 문제를 다른 방식으로 피하자면
문자열을 직접 파싱해서 년,월,일,시,분,초 등으로 구분하여
Encode/DecodeDateTime 을 사용하시면 됩니다.
이렇게 하면 범천님이 지적하신 상황이 문제가 되지 않게 되죠.

+ -

관련 글 리스트
903 [VCL] StrToDateTime("2009-07-02 11:23:11"); 이 에러나는 경우.. 장성호 15012 2009/07/02
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.