[m-rev.] for review: implement io__file_modification_time_2 in C#

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Oct 29 12:03:58 AEDT 2003


On 27-Oct-2003, Peter Ross <pro at missioncriticalit.com> wrote:
> library/io.m:
> 	Implement io__file_modification_time_2 in C#.
> 	
> library/time.m:
> 	Implement most of this module in C#.
> 	Add construct_time_t to the interface for use by
> 	io__file_modification_time_2.

> Index: library/time.m
...
> +:- pragma foreign_type(il, time_t_rep, "valuetype [mscorlib]System.DateTime")
> +       where comparison is compare_time_t_reps.

You should document that this is represented in UTC.

(Or alternatively, document that it is represented in local time... no,
that won't work.  Representing time_t in local time would cause problems
because times such as 2:30am on the day of the switch over from daylight
savings time back to standard time are ambiguous.)

> +:- pragma foreign_proc("C#", time__c_clock(Ret::out, _IO0::di, _IO::uo),
> +	[will_not_call_mercury, promise_pure, tabled_for_io],
> +"{
> +	// XXX Ticks is long in .NET!
> +	Ret = (int) System.DateTime.Now.Ticks;
> +}").

That's wrong.  time__c_clock should return "processor time", i.e. CPU usage,
but System.DateTime.Now returns wall clock time.

You should use System.Process.GetCurrentProcess.UserProcessorTime.Ticks
or something like that.

> @@ -373,6 +399,14 @@
>  "{
>  	Diff = (MR_Float) difftime(T1, T0);
>  }").
> +:- pragma foreign_proc("C#",
> +	time__c_difftime(T1::in, T0::in, Diff::out),
> +	[will_not_call_mercury, promise_pure],
> +"{
> +	System.TimeSpan span;
> +	span = T1 - T0;
> +	Diff = span.Ticks / System.TimeSpan.TicksPerSecond;
> +}").

Here span.Ticks and System.TimeSpan.TicksPerSecond both have integral
types, so this will do integer division, discarding any fractional part,
and then convert the result to a double.

You should instead use "Diff = span.TotalSeconds;".
That will return a value that includes the appropriate fraction of a second
in cases where the difference is not an integral number of seconds.
(Also, it's simpler!)

> +:- pragma foreign_proc("C#",
> +       time__c_time(Ret::out, _IO0::di, _IO::uo),
> +       [will_not_call_mercury, promise_pure, tabled_for_io],
> +"{
> +       Ret = System.DateTime.Now;
> +}").

That should be "Ret = System.DateTime.UtcNow;".
                                      ^^^

(Unless time_t_rep stores local time.)

>  %-----------------------------------------------------------------------------%
>  
> @@ -411,6 +445,25 @@
>  	YD = (MR_Integer) p->tm_yday;
>  	N = (MR_Integer) p->tm_isdst;
>  }").
> +:- pragma foreign_proc("C#",
> +	time__c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
> +		Min::out, Sec::out, YD::out, WD::out, N::out),
> +	[will_not_call_mercury, promise_pure],
> +"{
> +	System.DateTime t = Time.ToLocalTime();

That's OK, if time_t_rep stores UTC.

(If time_t_rep stored local time, that would need to be just "t = Time;".)

> +	// we don't handle leap seconds
> +	Sec = t.Second;
> +	Min = t.Minute;
> +	Hrs = t.Hour;
> +	Mnt = t.Month - 1;
> +	Yr = t.Year - 1900;
> +	WD = (int) t.DayOfWeek;
> +	MD = t.Day;
> +	YD = t.DayOfYear - 1;
> +	// XXX Don't know how to determine if in DST.
> +	N = -1;
> +}").

I think "N = (int) System.TimeZone.CurrentTimeZone.IsDayLightSavingTime(t);"
should do the trick.

>  %:- func time__gmtime(time_t) = tm.
> @@ -448,6 +501,25 @@
>  	YD = (MR_Integer) p->tm_yday;
>  	N = (MR_Integer) p->tm_isdst;
>  }").
> +:- pragma foreign_proc("C#",
> +	time__c_gmtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
> +		Min::out, Sec::out, YD::out, WD::out, N::out),
> +	[will_not_call_mercury, promise_pure],
> +"{
> +	System.DateTime t = Time.ToUniversalTime();

That should be just "System.Datetime t = Time;", since Time should
already be in UTC.

(Unless time_t_rep stores local time.)

> +	// we don't handle leap seconds
> +	Sec = t.Second;
> +	Min = t.Minute;
> +	Hrs = t.Hour;
> +	Mnt = t.Month - 1;
> +	Yr = t.Year - 1900;
> +	WD = (int) t.DayOfWeek;
> +	MD = t.Day;
> +	YD = t.DayOfYear - 1;
> +	// XXX Don't know how to determine if in DST.
> +	N = -1;

That should be "N = 0;", since time__c_gmtime returns a time in UTC,
and UTC times are never daylight savings times.

> +}").
>  
>  :- func int_to_maybe_dst(int) = maybe(dst).
>  
> @@ -491,6 +563,14 @@
>  
>  	Time = mktime(&t);
>  }").
> +:- pragma foreign_proc("C#",
> +	time__c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
> +		_YD::in, _WD::in, _N::in, Time::out),
> +	[will_not_call_mercury, promise_pure],
> + "{
> +	// We don't use YD, WD and N.
> + 	Time = new System.DateTime(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec);
> +}").

Ignoring YD and WD is fine; mktime is supposed to do that.
But ignoring N (the daylight savings time indicator) is not.
On the day when you switch back to standard time from daylight savings time,
the time "2:30am" occurs twice, once during daylight savings time (N = 1),
and then again an hour later, during standard time (N = 0).

Not sure what to do about this right now, but at very least it warrants an XXX.

Also, if time_t_rep stores UTC, you need to convert from local time to UTC.

>  :- func maybe_dst_to_int(maybe(dst)) = int.
>  
> @@ -537,6 +617,18 @@
>  
>  	MR_make_aligned_string_copy(Str, s);
>  }").
> +:- pragma foreign_proc("C#",
> +	time__c_asctime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
> +		_YD::in, _WD::in, _N::in, Str::out),
> +	[will_not_call_mercury, promise_pure],
> +"{
> +	// We don't use YD, WD and N.
> + 	System.DateTime Time = new System.DateTime(Yr + 1900, Mnt + 1, MD,
> +								Hrs, Min, Sec);
> +
> +	// XXX this may need to be formatted differently.
> +	Str = Time.ToString();
> +}").

This shouldn't ignore N either.

Also the XXX comment is correct, it should be formatted differently.

The C standard defines asctime() to be exactly equivalent to the following:

       char *asctime(const struct tm *timeptr)
       {
               static const char wday_name[7][3] = {
                       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
               };
               static const char mon_name[12][3] = {
                       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
               };
               static char result[26];

               sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
                       wday_name[timeptr->tm_wday],
                       mon_name[timeptr->tm_mon],
                       timeptr->tm_mday, timeptr->tm_hour,
                       timeptr->tm_min, timeptr->tm_sec,
                       1900 + timeptr->tm_year);
               return result;
       }

The simplest way to implement it would be to just convert this C code
to Mercury code.

Then ctime(T) can be implemented using asctime(localtime(T)).

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list