Files
mercury/compiler/timestamp.m
Peter Wang b1af59cb29 Deprecate string.substring, string.foldl_substring, etc. in favour of
Branches: main

Deprecate string.substring, string.foldl_substring, etc. in favour of
new procedures named string.between, string.foldl_between, etc.
The "between" procedures take a pair of [Start, End) endpoints instead
of Start, Count arguments.  The reasons for this change are:

- the "between" procedures are more convenient

- "between" should be unambiguous.  You can guess that it takes an End
  argument instead of a Count argument without looking up the manual.

- Count arguments necessarily counted code units, but when working with
  non-ASCII strings, almost the only way that the values would be arrived at
  is by substracting one end point from another.

- it paves the way for a potential change to replace string offsets with an
  abstract type.  We cannot do that if users regularly have to perform a
  subtraction between two offsets.

library/string.m:
	Add string.foldl_between, string.foldl2_between,
	string.foldr_between, string.between.

	Deprecate the old substring names.

	Replace string.substring_by_codepoint by string.between_codepoints.

compiler/elds_to_erlang.m:
compiler/error_util.m:
compiler/rbmm.live_variable_analysis.m:
compiler/timestamp.m:
extras/posix/samples/mdprof_cgid.m:
library/bitmap.m:
library/integer.m:
library/lexer.m:
library/parsing_utils.m:
mdbcomp/trace_counts.m:
profiler/demangle.m:
	Conform to changes.

tests/general/Mercury.options:
tests/general/string_foldl_substring.exp:
tests/general/string_foldl_substring.m:
tests/general/string_foldr_substring.exp:
tests/general/string_foldr_substring.m:
tests/hard_coded/Mercury.options:
tests/hard_coded/string_substring.m:
	Test both between and substring procedures.

tests/hard_coded/string_codepoint.exp:
tests/hard_coded/string_codepoint.exp2:
tests/hard_coded/string_codepoint.m:
	Update names in test outputs.

NEWS:
	Announce the change.
2011-06-15 01:05:34 +00:00

205 lines
6.4 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2001-2002, 2004-2006, 2011 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: timestamp.m.
% Main author: stayl.
%
% Timestamp representation for smart recompilation.
%
%-----------------------------------------------------------------------------%
:- module libs.timestamp.
:- interface.
:- import_module time.
% A `timestamp' is similar to a `time_t' except that timestamps are system
% independent. A timestamp string (obtained using timestamp_to_string)
% written on one system can be read on any other system. Comparison of
% values of type `timestamp' (via compare/3) is equivalent to comparison
% of the times represented.
:- type timestamp.
% Converts the calendar time value `Time' into a timestamp.
% Equivalent to `gm_time_to_timestamp(gmtime(Time))'.
%
:- func time_t_to_timestamp(time_t) = timestamp.
% Converts a timestamp into a string with format "yyyy-mm-dd hh:mm:ss",
% expressed as UTC.
%
:- func timestamp_to_string(timestamp) = string.
% Converts a string formatted as "yyyy-mm-dd hh:mm:ss", into a timestamp.
% Fails if the string does not have the correct format.
%
:- func string_to_timestamp(string) = timestamp is semidet.
% Return a timestamp which is older than any other timestamp.
%
:- func oldest_timestamp = timestamp.
% Return a timestamp which is newer than any other timestamp.
%
:- func newest_timestamp = timestamp.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module char.
:- import_module int.
:- import_module maybe.
:- import_module string.
%-----------------------------------------------------------------------------%
% A timestamp is a string formatted as "yyyy-mm-dd hh:mm:ss"
% representing a time expressed as UTC (Universal Coordinated Time).
%
% We use a no-tag type rather than an abstract equivalence type
% to avoid type errors with abstract equivalence types in the hlc
% back-end.
:- type timestamp
---> timestamp(string).
oldest_timestamp = timestamp("0000-00-00 00:00:00").
newest_timestamp = timestamp("9999-99-99 99:99:99").
time_t_to_timestamp(Time) = gmtime_to_timestamp(time.gmtime(Time)).
:- func gmtime_to_timestamp(tm) = timestamp.
gmtime_to_timestamp(tm(Year, Month, MD, Hrs, Min, Sec, YD, WD, DST)) =
timestamp(gmtime_to_timestamp_2(Year, Month, MD, Hrs, Min, Sec,
YD, WD, maybe_dst_to_int(DST))).
:- func gmtime_to_timestamp_2(int, int, int, int, int, int, int, int, int)
= string.
:- pragma foreign_decl("C", "
#include ""mercury_string.h""
#include <time.h>
").
:- pragma foreign_proc("C",
gmtime_to_timestamp_2(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in,
Sec::in, YD::in, WD::in, N::in) = (Result::out),
[will_not_call_mercury, promise_pure],
"{
int size;
struct tm t;
t.tm_sec = Sec;
t.tm_min = Min;
t.tm_hour = Hrs;
t.tm_mon = Mnt;
t.tm_year = Yr;
t.tm_wday = WD;
t.tm_mday = MD;
t.tm_yday = YD;
t.tm_isdst = N;
size = sizeof ""yyyy-mm-dd hh:mm:ss"";
MR_allocate_aligned_string_msg(Result, size - 1, MR_ALLOC_ID);
strftime(Result, size, ""%Y-%m-%d %H:%M:%S"", &t);
}").
:- pragma foreign_proc("C#",
gmtime_to_timestamp_2(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in,
Sec::in, _YD::in, _WD::in, _N::in) = (Result::out),
[will_not_call_mercury, promise_pure],
"{
System.DateTime t;
t = new System.DateTime(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec);
string format_str = ""yyyy-MM-dd hh:mm:ss"";
Result = t.ToString(format_str);
}").
:- pragma foreign_proc("Java",
gmtime_to_timestamp_2(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in,
Sec::in, _YD::in, _WD::in, _N::in) = (Result::out),
[will_not_call_mercury, promise_pure],
"
java.util.GregorianCalendar cal =
new java.util.GregorianCalendar(Yr + 1900, Mnt - 1, MD, Hrs, Min, Sec);
java.text.SimpleDateFormat sdf =
new java.text.SimpleDateFormat(""yyyy-MM-dd HH:mm:ss"");
java.util.Date date = new java.util.Date(cal.getTimeInMillis());
Result = sdf.format(date);
").
gmtime_to_timestamp_2(_, _, _, _, _, _, _, _, _) = _ :-
sorry($file, $pred).
:- func maybe_dst_to_int(maybe(dst)) = int.
maybe_dst_to_int(M) = N :-
( M = yes(DST), DST = daylight_time,
N = 1
; M = yes(DST), DST = standard_time,
N = 0
; M = no,
N = -1
).
timestamp_to_string(timestamp(Timestamp)) = Timestamp.
string_to_timestamp(Timestamp) = timestamp(Timestamp) :-
% The if-then-else here is to force order of evaluation --
% we need to ensure that the sanity checks occur before the
% calls to unsafe_index. The offsets are only valid if the string
% contains only ASCII characters, as expected.
(
string.all_match(plausible_timestamp_char, Timestamp),
string.length(Timestamp) : int = string.length("yyyy-mm-dd hh:mm:ss")
->
string.to_int(string.unsafe_between(Timestamp, 0, 4), _),
string.unsafe_index(Timestamp, 4, '-'),
string.to_int(string.unsafe_between(Timestamp, 5, 7), Month),
Month >= 1,
Month =< 12,
string.unsafe_index(Timestamp, 7, '-'),
string.to_int(string.unsafe_between(Timestamp, 8, 10), Day),
Day >= 1,
Day =< 31,
string.unsafe_index(Timestamp, 10, ' '),
string.to_int(string.unsafe_between(Timestamp, 11, 13), Hour),
Hour >= 0,
Hour =< 23,
string.unsafe_index(Timestamp, 13, ':'),
string.to_int(string.unsafe_between(Timestamp, 14, 16), Minute),
Minute >= 0,
Minute =< 59,
string.unsafe_index(Timestamp, 16, ':'),
string.to_int(string.unsafe_between(Timestamp, 17, 19), Second),
Second >= 0,
Second =< 61 % Seconds 60 and 61 are for leap seconds.
;
fail
).
:- pred plausible_timestamp_char(char::in) is semidet.
plausible_timestamp_char(Char) :-
char.to_int(Char, CharInt),
char.to_int(':', HighestInt),
CharInt =< HighestInt.