2013-02-22 67 views
12

मैं डेल्फी के लिए नया हूं (इसमें लगभग 6 महीने के लिए प्रोग्रामिंग कर रहा हूं)। अब तक, यह बेहद निराशाजनक अनुभव रहा है, इनमें से अधिकतर डेल्फी तारीखों और समयों को संभालने में कितना बुरा है। शायद मुझे लगता है कि यह बुरा है क्योंकि मुझे नहीं पता कि टीडीएटी और टीटाइम का सही तरीके से उपयोग कैसे करें, मुझे नहीं पता। यहाँ अभी मुझ पर हो रहा है:डेटाटाइम मैनिपुलेशन को सटीक रूप से संभालने के लिए डेल्फी की अक्षमता के आसपास मैं कैसे काम करूं?

// This shows 570, as expected 
ShowMessage(IntToStr(MinutesBetween(StrToTime('8:00'), StrToTime('17:30')))); 

// Here I would expect 630, but instead 629 is displayed. WTF!? 
ShowMessage(IntToStr(MinutesBetween(StrToTime('7:00'), StrToTime('17:30')))); 

सटीक कोड मैं का उपयोग नहीं है यही कारण है, सब कुछ चर में है और एक अन्य संदर्भ में प्रयोग किया है, लेकिन मुझे लगता है कि आप इस समस्या को देख सकते हैं। वह गणना गलत क्यों है? मुझे इस समस्या के आसपास काम करने का अनुमान है?

ShowMessage(IntToStr(trunc(MinuteSpan(a, b)))); // Gives 629 

हालांकि, यह दौर के लिए बेहतर हो सकता है:

+0

डेल्फी के 'टीडीएटीटाइम' के साथ बड़ी समस्या यह है कि वे निश्चित बिंदुओं के बजाय दिन का प्रतिनिधित्व करने वाले युगल हैं। इसका मतलब है कि वे मिनटों का बिल्कुल प्रतिनिधित्व नहीं कर सकते हैं। (यकीन नहीं है कि क्या समस्या का कारण बनता है, लेकिन मुझे आश्चर्य नहीं होगा) – CodesInChaos

+1

@CodesInChaos: मुझे यह बताया गया है। यह मेरे लिए उचित बहाना नहीं है। वे सिर्फ [लीकी एब्स्ट्रक्शन] (http://www.joelonsoftware.com/articles/LeakyAbstractions.html) रखने के बजाय इसे सही तरीके से काम कर सकते थे। –

+0

डेल्फी के नए संस्करणों (2006 या उससे अधिक) में आप अपने स्वयं के रिकॉर्ड दिनांक/समय का प्रतिनिधित्व कर सकते हैं और ऑपरेटरों को अधिभारित कर सकते हैं। फिर आप स्वाभाविक रूप से त्रुटिपूर्ण डबल-डेटटाइम्स को छोड़ सकते हैं। – CodesInChaos

उत्तर

19

a := StrToTime('7:00'); 
b := StrToTime('17:30'); 

ShowMessage(FloatToStr(a)); 
ShowMessage(FloatToStr(b)); 

अपने कोड को देखते हुए, MinutesBetween का उपयोग कर, प्रभावी ढंग से करता है

ShowMessage(IntToStr(round(MinuteSpan(a, b)))); // Gives 630 

क्या वास्तव में है चल -बिंदु मान?

ShowMessage(FloatToStr(MinuteSpan(a, b))); // Gives 630 

ताकि आप यहां पारंपरिक फ़्लोटिंग-पॉइंट समस्याओं से स्पष्ट रूप से पीड़ित हैं।

अद्यतन:

Round के प्रमुख लाभ है कि अगर मिनट अवधि बहुत एक पूर्णांक के करीब है, तो गोल मूल्य कि पूर्णांक होना गारंटी है, जबकि छोटा कर दिया मूल्य बहुत अच्छी तरह से ठीक पहले हो सकता है पूर्णांक।

Trunc का प्रमुख लाभ यह है कि आप वास्तव में इस तरह के तर्क चाहते हैं: वास्तव में, यदि आप पांच दिनों में 18 वर्ष का हो जाते हैं, तो कानूनी तौर पर आपको स्वीडिश ड्राइविंग लाइसेंस के लिए आवेदन करने की अनुमति नहीं है।

तो तुम आप Round बजाय Trunc उपयोग करना चाहते हैं, तो आप सिर्फ अपने इकाई के लिए

function MinutesBetween(const ANow, AThen: TDateTime): Int64; 
begin 
    Result := Round(MinuteSpan(ANow, AThen)); 
end; 

जोड़ सकते हैं। फिर पहचानकर्ता MinutesBetween इस संदर्भ में, एक ही इकाई में, DateUtils में से एक के बजाय संदर्भित करेगा। सामान्य नियम यह है कि संकलक उस नवीनतम फ़ंक्शन का उपयोग करेगा जो इसे नवीनतम मिला। तो, उदाहरण के लिए, तो आप इस समारोह के ऊपर अपनी खुद की इकाई DateUtilsFix में डाल करना चाहते हैं, तो

implementation 

uses DateUtils, DateUtilsFix 

DateUtils के अधिकार के लिए DateUtilsFix occurss के बाद से, नए MinutesBetween का उपयोग करेगा।

अद्यतन 2:

एक और प्रशंसनीय दृष्टिकोण हो सकता है

function MinutesBetween(const ANow, AThen: TDateTime): Int64; 
var 
    spn: double; 
begin 
    spn := MinuteSpan(ANow, AThen); 
    if SameValue(spn, round(spn)) then 
    result := round(spn) 
    else 
    result := trunc(spn); 
end; 

यह round(spn) वापस आ जाएगी अवधि एक पूर्णांक के फ़ज़्ज़ सीमा के भीतर है, और trunc(spn) अन्यथा।

उदाहरण के लिए

, इस दृष्टिकोण

07:00:00 and 07:00:58 

का उपयोग कर 0 मिनट निकलेगा, बस मूल trunc आधारित संस्करण की तरह, और सिर्फ स्वीडिश Trafikverket की तरह चाहते हैं। लेकिन यह ओपी के सवाल को ट्रिगर करने वाली समस्या से पीड़ित नहीं होगा।

+1

यह दृष्टिकोण बुलेट प्रूफ –

+0

जाने का तरीका लगता है। धन्यवाद! –

+0

मुझे यकीन नहीं है कि मैं आपका दूसरा अपडेट समझता हूं। यह क्या मूल्य जोड़ता है? –

8

यह एक मुद्दा है जिसे डेल्फी के नवीनतम संस्करणों में हल किया गया है। तो आप या तो उन्नयन कर सकता है, या बस डेल्फी 2010 में नए कोड उदाहरण के लिए इस कार्यक्रम के उत्पादन जैसा कि आप उम्मीद पैदा करता है का उपयोग करें:

{$APPTYPE CONSOLE} 
uses 
    SysUtils, DateUtils; 

function DateTimeToMilliseconds(const ADateTime: TDateTime): Int64; 
var 
    LTimeStamp: TTimeStamp; 
begin 
    LTimeStamp := DateTimeToTimeStamp(ADateTime); 
    Result := LTimeStamp.Date; 
    Result := (Result * MSecsPerDay) + LTimeStamp.Time; 
end; 

function MinutesBetween(const ANow, AThen: TDateTime): Int64; 
begin 
    Result := Abs(DateTimeToMilliseconds(ANow) - DateTimeToMilliseconds(AThen)) 
    div (MSecsPerSec * SecsPerMin); 
end; 

begin 
    Writeln(IntToStr(MinutesBetween(StrToTime('7:00'), StrToTime('17:30')))); 
    Readln; 
end. 

MinutesBetween के लिए डेल्फी 2010 कोड इस तरह दिखता है:

function SpanOfNowAndThen(const ANow, AThen: TDateTime): TDateTime; 
begin 
    if ANow < AThen then 
    Result := AThen - ANow 
    else 
    Result := ANow - AThen; 
end; 

function MinuteSpan(const ANow, AThen: TDateTime): Double; 
begin 
    Result := MinsPerDay * SpanOfNowAndThen(ANow, AThen); 
end; 

function MinutesBetween(const ANow, AThen: TDateTime): Int64; 
begin 
    Result := Trunc(MinuteSpan(ANow, AThen)); 
end; 

तो, MinutesBetween प्रभावी रूप से दो दिनांक/समय मानों के एक फ़्लोटिंग पॉइंट घटाव को उबालता है। फ्लोटिंग पॉइंट अंकगणित की अंतर्निहित सटीकता के कारण, यह घटाव एक मान उत्पन्न कर सकता है जो वास्तविक मूल्य से ऊपर या नीचे है। जब यह वास्तविक मान से नीचे होता है, तो Trunc का उपयोग आपको पिछले मिनट तक नीचे ले जाएगा। को Round के साथ बस समस्या का समाधान करेगा।


जैसा कि यह नवीनतम डेल्फी संस्करण होता है, तिथि/समय गणना पूरी तरह से ओवरहाल करता है। DateUtils में बड़े बदलाव हैं। विश्लेषण करना थोड़ा कठिन है, लेकिन नया संस्करण DateTimeToTimeStamp पर निर्भर करता है। यह मध्यरात्रि के बाद मूल्य के समय हिस्से को मिलीसेकंड की संख्या में परिवर्तित करता है। और यह ऐसा करता है:

function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp; 
var 
    LTemp, LTemp2: Int64; 
begin 
    LTemp := Round(DateTime * FMSecsPerDay); 
    LTemp2 := (LTemp div IMSecsPerDay); 
    Result.Date := DateDelta + LTemp2; 
    Result.Time := Abs(LTemp) mod IMSecsPerDay; 
end; 

Round का उपयोग नोट करें। के बजाय Round का उपयोग एक कारण है कि नवीनतम डेल्फी कोड एक मजबूत फैशन में MinutesBetween को संभालता है।


हैं यह मानते हुए कि आप इसे अभी अपग्रेड नहीं कर सकते, मैं इस तरह समस्या से निपटने:

  1. अपने कोड में कोई बदलाव नहीं छोड़ दें। MinutesBetween आदि पर कॉल करना जारी रखें
  2. जब आप अपग्रेड करते हैं, तो MinutesBetween आदि पर कॉल करने वाला आपका कोड अब काम करेगा।
  3. इस बीच MinutesBetween आदि code hooks के साथ ठीक करें। जब आप अपग्रेड करने के लिए आते हैं, तो आप बस हुक को हटा सकते हैं।