Tcl Library Source Code

Changes On Branch new-module-clock
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch new-module-clock Excluding Merge-Ins

This is equivalent to a diff from 8252e19883 to 730be11a05

2013-02-06
05:16
Merged the clock fixes into the release. Closed-Leaf check-in: 472c75a7bc user: aku tags: tcllib-1-15-rc
05:16
Implemented the YYYY-Www and YYYYWww formats, with test cases. Closed-Leaf check-in: 730be11a05 user: aku tags: new-module-clock
2013-02-03
20:55
Fixing problems in the ISO parsing, and testsuite ... (1) The [clock scan] heuristics for handling a missing month and/or day are in conflict with the ISO specification. When encountering such a pattern we now rewrite pattern and input to force the ISO defaults. (2) Fixed the regular expression for the time information in a full timestamp as it did not handle that : in a TZ offset is optional, nor that minutes and seconds are optional (The code after the MatchTime was prepared for such missing fields however). (2a) Again missing HMS fields got the wrong defaults. We are now rewriting the pattern and input to force the proper ISO defaults (start of day, hour, minute). (3) Fixed the tests which thought that 199707 is YYYYMM. ISO (Wikipedia) explicitly says that this is YYMMDD instead. (3a) Added missing patterns for YYYY-MM and YYYY. (4) Fixed the test numbering, had some duplicates. (5) Fixed one test with wrong results, expected 11:20 for input specifying 11:00. check-in: 7e70b9d4a7 user: aku tags: new-module-clock
2013-01-28
23:14
New module for zipfile de- and encoding. Plus support new package in fileutil. check-in: ca44329bef user: andreask tags: trunk
2013-01-24
18:30
New module, clock. rfc2822 and iso8601 parsing. taken from wiki. no docs yet check-in: 9a82504277 user: andreask tags: new-module-clock
2013-01-22
20:17
Applied contribution by Nicola Hall. Additional commands. Bumped version to 0.4. check-in: a35937592d user: andreask tags: contrib-nicola-hall-imap4
2013-01-21
22:47
Merge latest certs from trunk into the RC work. check-in: 107f16ba9c user: andreask tags: tcllib-1-15-rc
21:56
Regenerated the certificates here again, 10 years at 1024 bit. The issue with [7b80198969] seems to have been that I extended the Root cert validity to 100 years, and that screwed something up. With the root certs now back to 10 years (+10 days) validity, things are fine. I suspect that I may have run into the Y2038 problem in either Tcl and/or openssl. check-in: 8252e19883 user: andreask tags: trunk
19:37
[Bug 3433470] Regenerated the certificates, again. Expiry is Jan 2023 (10 years). While SimpleCA doesn't seem to allow me to specify a longer period in the GUI it was possible to get, update and run the Tcl code, unwrapped. Further changed to 4096-bit certs. --- Broken --- Investigation points to me setting the root cert validity period to 100 years as the cause, possibly invoking y2038 troubles in Tcl and/or openssl. check-in: 7b80198969 user: andreask

Changes to ChangeLog.






1
2
3
4
5
6
7





2013-01-08  Andreas Kupries  <[email protected]>

	* configure.in: [Bug 3593146]: Extended with CYGPATH usage to
	  allow building under cygwin.
	* configure: Regenerated (autoconf 2.67).

2012-08-07  Andreas Kupries  <[email protected]>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
2013-01-24  Andreas Kupries  <[email protected]>

	* New module and packages: clock (rfc2822, iso8601).
	  Tcl 8.5 only.

2013-01-08  Andreas Kupries  <[email protected]>

	* configure.in: [Bug 3593146]: Extended with CYGPATH usage to
	  allow building under cygwin.
	* configure: Regenerated (autoconf 2.67).

2012-08-07  Andreas Kupries  <[email protected]>

Added modules/clock/ChangeLog.













>
>
>
>
>
>
1
2
3
4
5
6
2013-01-24  Andreas Kupries  <[email protected]>

	* New module, 2 packages.
	* rfc2822.tcl: Parsing rfc2822 dates (mail, news)
	* iso8601.tcl: Parsing iso8601 dates and times.
	* pkgIndex.tcl:

Added modules/clock/iso8601.man.



















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin clock_iso8601 n 0.1]
[moddesc   {Date/Time Utilities}]
[titledesc {Parsing ISO 8601 dates/times}]
[category  {Text processing}]
[require Tcl 8.5]
[require clock::iso8601 [opt 0.1]]
[description]

This package provides functionality to parse dates and times in
ISO 8601 format.

[para]

[list_begin definitions]

[call [cmd {::clock::iso8601 parse_date}] \
     [arg date] [arg options...]]

This command parses an ISO8601 date string in an unknown variant and
returns the given date/time in seconds since epoch.

[para] The acceptable options are
[option -base],
[option -gmt],
[option -locale], and
[option -timezone]
of the builtin command [cmd {clock scan}].

[call [cmd {::clock::iso8601 parse_time}] \
     [arg time] [arg options...]]

This command parses a full ISO8601 timestamp string (date and time) in
an unknown variant and returns the given time in seconds since epoch.

[para] The acceptable options are
[option -base],
[option -gmt],
[option -locale], and
[option -timezone]
of the builtin command [cmd {clock scan}].

[list_end]

[section {BUGS, IDEAS, FEEDBACK}]

This document, and the package it describes, will undoubtedly contain
bugs and other problems.

Please report such in the category [emph clock::iso8601] of the
[uri {http://sourceforge.net/tracker/?group_id=12883} {Tcllib SF Trackers}].

Please also report any ideas for enhancements you may have for either
package and/or documentation.


[manpage_end]

Added modules/clock/iso8601.pcx.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# -*- tcl -*- iso8601.pcx
# Syntax of the commands provided by package iso8601.

# For use by TclDevKit's static syntax checker.
# See http://www.activestate.com/solutions/tcl/
# See http://aspn.activestate.com/ASPN/docs/Tcl_Dev_Kit/4.0/Checker.html#pcx_api
# for the documentation describing the format of the code contained in this file
#

package require pcx
pcx::register clock::iso8601
pcx::tcldep   0.1 needs tcl 8.5

namespace eval ::clock::iso8601 {}

#pcx::message FOO {... text ...} type
#pcx::scan <VERSION> <NAME> <RULE>

# Switches are per clock scan (Tcl 8.5), restricted subset.
pcx::check 0.1 std ::clock::iso8601::parse_date \
    {checkSimpleArgs 1 -1 {
	checkWord
	{checkSwitches 0 {
	    {-locale   checkWord}
	    {-timezone checkWord}
	    {-base     checkInt}
	    {-gmt      checkBoolean}
	} {}}
    }}
pcx::check 0.1 std ::clock::iso8601::parse_time \
    {checkSimpleArgs 1 -1 {
	checkWord
	{checkSwitches 0 {
	    {-locale   checkWord}
	    {-timezone checkWord}
	    {-base     checkInt}
	    {-gmt      checkBoolean}
	} {}}
    }}

# Initialization via pcx::init.
# Use a ::iso8601::init procedure for non-standard initialization.
pcx::complete

Added modules/clock/iso8601.tcl.

















































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
## -*- tcl -*-
# # ## ### ##### ######## ############# #####################
## Copyright (c) 2004 Kevin Kenny
## Origin http://wiki.tcl.tk/13094
## Modified for Tcl 8.5 only (eval -> {*}).

# # ## ### ##### ######## ############# #####################
## Requisites

package require Tcl 8.5
package provide clock::iso8601 0.1
namespace eval ::clock::iso8601 {}

# # ## ### ##### ######## ############# #####################
## API

# iso8601::parse_date --
#
#       Parse an ISO8601 date/time string in an unknown variant.
#
# Parameters:
#       string -- String to parse
#       args -- Arguments as for [clock scan]; may include any of
#               the '-base', '-gmt', '-locale' or '-timezone options.
#
# Results:
#       Returns the given date in seconds from the Posix epoch.

proc ::clock::iso8601::parse_date { string args } {
    variable DatePatterns
    variable Repattern
    foreach { regex interpretation } $DatePatterns {
	if { [regexp "^$regex\$" $string] } {
	    #puts A|$string|\t|$regex|\t|$interpretation|

	    # For incomplete dates (month and/or day missing), we have
	    # to set our own default values to overcome clock scan's
	    # settings. We do this by switching to a different pattern
	    # and extending the input properly for that pattern.

	    if {[dict exists $Repattern $interpretation]} {
		lassign [dict get $Repattern $interpretation] interpretation adjust modifier
		{*}$modifier
		# adjust irrelevant here, see parse_time for use.
	    }

	    #puts B|$string|\t|$regex|\t|$interpretation|
	    return [clock scan $string -format $interpretation {*}$args]
	}
    }
    return -code error "not an iso8601 date string"
}

# iso8601::parse_time --
#
#       Parse a point-in-time in ISO8601 format
#
# Parameters:
#       string -- String to parse
#       args -- Arguments as for [clock scan]; may include any of
#               the '-base', '-gmt', '-locale' or '-timezone options.
#
# Results:
#       Returns the given time in seconds from the Posix epoch.

proc ::clock::iso8601::parse_time { string args } {
    variable DatePatterns
    variable Repattern
    if {![MatchTime $string field]} {
	return -code error "not an iso8601 time string"
    }

    #parray field
    #puts A|$string|

    set pattern {}
    foreach {regex interpretation} $DatePatterns {
	if {[Has $interpretation tstart]} {
	    append pattern $interpretation
	}
    }

    if {[dict exists $Repattern $pattern]} {
	lassign [dict get $Repattern $pattern] interpretation adjust modifier
	{*}$modifier
	incr tstart $adjust
    }

    append pattern [Get T len]
    incr tstart $len

    if {[Has %H tstart]} {
	append pattern %H [Get Hcolon len]
	incr tstart $len

	if {[Has %M tstart]} {
	    append pattern %M [Get Mcolon len]
	    incr tstart $len

	    if {[Has %S tstart]} {
		append pattern %S
	    } else {
		# No seconds, default to start of minute.
		append pattern %S
		Insert string $tstart 00
	    }
	} else {
	    # No minutes, nor seconds, default to start of hour.
	    append pattern %M%S
	    Insert string $tstart 0000
	}
    } else {
	# No time information, default to midnight.
	append pattern %H%M%S
	Insert string $tstart 000000
    }
    if {[Has %Z _]} {
	append pattern %Z
    }

    #puts B|$string|\t|$pattern|
    return [clock scan $string -format $pattern {*}$args]
}

# # ## ### ##### ######## ############# #####################

proc ::clock::iso8601::Get {x lv} {
    upvar 1 field field string string $lv len
    lassign $field($x) s e
    if {($s >= 0) && ($e >= 0)} {
	set len [expr {$e - $s + 1}]
	return [string range $string $s $e]
    }
    set len 0
    return ""

}

proc ::clock::iso8601::Has {x nv} {
    upvar 1 field field string string $nv next
    lassign $field($x) s e
    if {($s >= 0) && ($e >= 0)} {
	set  next $e
	incr next
	return 1
    }
    return 0
}

proc ::clock::iso8601::Insert {sv index str} {
    upvar 1 $sv string
    append r [string range $string 0 ${index}-1]
    append r $str
    append r [string range $string $index end]
    set string $r
    return
}

# # ## ### ##### ######## ############# #####################
## State

namespace eval ::clock::iso8601 {

    namespace export parse_date parse_time
    namespace ensemble create

    # Enumerate the patterns that we recognize for an ISO8601 date as both
    # the regexp patterns that match them and the [clock] patterns that scan
    # them.

    variable DatePatterns {
	{\d\d\d\d-\d\d-\d\d}            {%Y-%m-%d}
	{\d\d\d\d\d\d\d\d}              {%Y%m%d}
	{\d\d\d\d-\d\d\d}               {%Y-%j}
	{\d\d\d\d\d\d\d}                {%Y%j}
	{\d\d-\d\d-\d\d}                {%y-%m-%d}
	{\d\d\d\d-\d\d}                 {%Y-%m}
	{\d\d\d\d\d\d}                  {%y%m%d}
	{\d\d-\d\d\d}                   {%y-%j}
	{\d\d\d\d\d}                    {%y%j}
	{--\d\d-\d\d}                   {--%m-%d}
	{--\d\d\d\d}                    {--%m%d}
	{--\d\d\d}                      {--%j}
	{---\d\d}                       {---%d}
	{\d\d\d\d-W\d\d-\d}             {%G-W%V-%u}
	{\d\d\d\dW\d\d\d}               {%GW%V%u}
	{\d\d-W\d\d-\d}                 {%g-W%V-%u}
	{\d\dW\d\d\d}                   {%gW%V%u}
	{\d\d\d\d-W\d\d}                {%G-W%V}
	{\d\d\d\dW\d\d}                 {%GW%V}
	{-W\d\d-\d}                     {-W%V-%u}
	{-W\d\d\d}                      {-W%V%u}
	{-W-\d}                         {%u}
	{\d\d\d\d}                      {%Y}
    }

    # Dictionary of the patterns requiring modifications to the input
    # for proper month and/or day defaults.
    variable Repattern {
	%Y-%m  {%Y-%m-%d  3 {Insert string 7 -01}}
	%Y     {%Y-%m-%d  5 {Insert string 4 -01-01}}
	%G-W%V {%G-W%V-%u 1 {Insert string 8 -1}}
	%GW%V  {%GW%V%u   1 {Insert string 6 1}}
    }
}

# # ## ### ##### ######## ############# #####################
## Initialization

apply {{} {
    # MatchTime -- (constructed procedure)
    #
    #   Match an ISO8601 date/time string and indicate how it matched.
    #
    # Parameters:
    #   string -- String to match.
    #   fieldArray -- Name of an array in caller's scope that will receive
    #                 parsed fields of the time.
    #
    # Results:
    #   Returns 1 if the time was scanned successfully, 0 otherwise.
    #
    # Side effects:
    #   Initializes the field array.  The keys that are significant:
    #           - Any date pattern in 'DatePatterns' indicates that the
    #             corresponding value, if non-empty, contains a date string
    #             in the given format.
    #           - The patterns T, Hcolon, and Mcolon indicate a literal
    #             T preceding the time, a colon following the hour, or
    #             a colon following the minute.
    #           - %H, %M, %S, and %Z indicate the presence of the
    #             corresponding parts of the time.

    variable DatePatterns

    set cmd {regexp -indices -expanded -nocase -- {PATTERN} $timeString ->}
    set re \(?:\(?:
    set sep {}
    foreach {regex interpretation} $DatePatterns {
	append re $sep \( $regex \)
	append cmd " " [list field($interpretation)]
	set sep |
    }
    append re \) {(T|[[:space:]]+)} \)?
    append cmd { field(T)}
    append re {(\d\d)(?:(:?)(\d\d)(?:(:?)(\d\d)?))?}
    append cmd { field(%H) field(Hcolon) } {field(%M) field(Mcolon) field(%S)}
    append re {[[:space:]]*(Z|[-+]\d\d:?\d\d)?}
    append cmd { field(%Z)}
    set cmd [string map [list {{PATTERN}} [list $re]] \
		 $cmd]

    proc MatchTime { timeString fieldArray } "
             upvar 1 \$fieldArray field
             $cmd
         "

    #puts [info body MatchTime]

} ::clock::iso8601}

# # ## ### ##### ######## ############# #####################

return
# Usage examples, disabled.

if { [info exists ::argv0] && ( $::argv0 eq [info script] ) } {
    puts "::clock::iso8601::parse_date"
    puts [::clock::iso8601::parse_date 1970-01-02 -timezone :UTC]
    puts [::clock::iso8601::parse_date 1970-W01-5 -timezone :UTC]
    puts [time {::clock::iso8601::parse_date 1970-01-02 -timezone :UTC} 1000]
    puts [time {::clock::iso8601::parse_date 1970-W01-5 -timezone :UTC} 1000]
    puts "::clock::iso8601::parse_time"
    puts [clock format [::clock::iso8601::parse_time 2004-W33-2T18:52:24Z] \
	      -format {%X %x %z} -locale system]
    puts [clock format [::clock::iso8601::parse_time 18:52:24Z] \
	      -format {%X %x %z} -locale system]
    puts [time {::clock::iso8601::parse_time 2004-W33-2T18:52:24Z} 1000]
    puts [time {::clock::iso8601::parse_time 18:52:24Z} 1000]
}

Added modules/clock/iso8601.test.



















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# -------------------------------------------------------------------------
# iso8601.test -*- tcl -*-
# (C) 2013 Andreas Kupries. BSD licensed.
# -------------------------------------------------------------------------

source [file join \
            [file dirname [file dirname [file join [pwd] [info script]]]] \
            devtools testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 2.0

testing {
    useLocal iso8601.tcl clock::iso8601
}

# -------------------------------------------------------------------------

test clock-iso8601-1.0.0 {parse_date wrong\#args} -constraints {tcl8.5plus tcl8.5minus} -body {
    clock::iso8601 parse_date
} -returnCodes error -result {wrong # args: should be "clock::iso8601 parse_date string ..."}

test clock-iso8601-1.0.1 {parse_date wrong\#args} -constraints {tcl8.6plus} -body {
    clock::iso8601 parse_date
} -returnCodes error -result {wrong # args: should be "clock::iso8601 parse_date string ?arg ...?"}

test clock-iso8601-1.1 {parse_date, bad option} -body {
    clock::iso8601 parse_date 1994-11-05 -foo x
} -returnCodes error -result {bad switch "-foo", must be -base, -format, -gmt, -locale or -timezone}

# NOTE: While listed as legal, -format is NOT. This is because the
# command simply hands off to clock scan, and we are seeing its error
# message.  Either we do our own argument check first, or we capture
# and rewrite the error.

# -------------------------------------------------------------------------

test clock-iso8601-2.0 {parse_date, bad input} -body {
    clock::iso8601 parse_date A
} -returnCodes error -result {not an iso8601 date string}

test clock-iso8601-2.1 {parse_date} -body {
    clock::iso8601 parse_date 1994-11-05
    # It is unclear if this is influenced by TZ.
} -result 784022400

# -------------------------------------------------------------------------

test clock-iso8601-2.0.0 {parse_date, format: 19700102, reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {19700102}] -format {%D}
} -result {01/02/1970}

test clock-iso8601-2.0.1 {parse_date, format: 1970-W01-5, reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {1970-W01-5}] -format {%D}
} -result {01/02/1970}

test clock-iso8601-2.1.0 {parse_date, format: 19700102, using -timezone :UTC} -body {
    clock::iso8601 parse_date {19700102} -timezone :UTC
} -result {86400}

test clock-iso8601-2.1.1 {parse_date, format: 1970-W01-5, using -timezone :UTC} -body {
    clock::iso8601 parse_date {1970-W01-5} -timezone :UTC
} -result {86400}

test clock-iso8601-2.2.0 {parse_date, format: 970701 (yymmdd), reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {970701}] -format {%D}
} -result {07/01/1997}

test clock-iso8601-2.2.1 {parse_date, format: 1997-07, reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {1997-07}] -format {%D}
} -result {07/01/1997}

test clock-iso8601-2.3.0 {parse_date, format: 970701 (yymmdd), using -timezone :UTC} -body {
    clock::iso8601 parse_date {970701} -timezone :UTC
} -result {867715200}

test clock-iso8601-2.3.1 {parse_date, format: 1997-07, using -timezone :UTC} -body {
    clock::iso8601 parse_date {1997-07} -timezone :UTC
} -result {867715200}

test clock-iso8601-2.4.0 {parse_date, format: 1997, reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {1997}] -format {%D}
} -result {01/01/1997}

test clock-iso8601-2.4.1 {parse_date, format: 1997, reformatted with clock format -format {%D}} -body {
    clock format [clock::iso8601 parse_date {1997}] -format {%D}
} -result {01/01/1997}

test clock-iso8601-2.5.0 {parse_date, format: 1997, using -timezone :UTC} -body {
    clock::iso8601 parse_date {1997} -timezone :UTC
} -result {852076800}

test clock-iso8601-2.5.1 {parse_date, format: 1997-, using -timezone :UTC} -body {
    clock::iso8601 parse_date {1997} -timezone :UTC
} -result {852076800}

# -------------------------------------------------------------------------

foreach {n iso week} {
    00 01/01/2005 2004-W53-6 	
    01 01/02/2005 2004-W53-7
    02 12/31/2005 2005-W52-6
    03 01/01/2007 2007-W01-1
    04 12/30/2007 2007-W52-7 	
    05 12/31/2007 2008-W01-1
    06 01/01/2008 2008-W01-2
    07 12/28/2008 2008-W52-7
    08 12/29/2008 2009-W01-1
    09 12/30/2008 2009-W01-2
    10 12/31/2008 2009-W01-3
    11 01/01/2009 2009-W01-4
    12 12/31/2009 2009-W53-4
    13 01/01/2010 2009-W53-5
    14 01/02/2010 2009-W53-6
    15 01/03/2010 2009-W53-7
} {
    test clock-iso8601-2.6.$n {parse_date, format: YYYY-Www-D into %D} -body {
        clock format [clock::iso8601 parse_date $week] -format {%D}
    } -result $iso

    test clock-iso8601-2.7.$n {parse_date, format: YYYYWwwD into %D} -body {
        clock format [clock::iso8601 parse_date [string map {- {}} $week]] -format {%D}
    } -result $iso
}

foreach {n iso week} {
    00 01/01/2007 2007-W01
    01 12/31/2007 2008-W01
    02 12/29/2008 2009-W01
} {
    test clock-iso8601-2.8.$n {parse_date, format: YYYY-Www into %D} -body {
        clock format [clock::iso8601 parse_date $week] -format {%D}
    } -result $iso

    test clock-iso8601-2.9.$n {parse_date, format: YYYYWww into %D} -body {
        clock format [clock::iso8601 parse_date [string map {- {}} $week]] -format {%D}
    } -result $iso
}

# -------------------------------------------------------------------------

test clock-iso8601-5.0.0 {parse_time wrong\#args} -constraints {tcl8.5plus tcl8.5minus} -body {
    clock::iso8601 parse_time
} -returnCodes error -result {wrong # args: should be "clock::iso8601 parse_time string ..."}

test clock-iso8601-5.0.1 {parse_time wrong\#args} -constraints {tcl8.6plus} -body {
    clock::iso8601 parse_time
} -returnCodes error -result {wrong # args: should be "clock::iso8601 parse_time string ?arg ...?"}

test clock-iso8601-5.1 {parse_time, bad option} -body {
    clock::iso8601 parse_time A -foo x
} -returnCodes error -result {not an iso8601 time string}

# -------------------------------------------------------------------------

test clock-iso8601-6.0.0 {parse_time, full date time} -body {
    clock::iso8601 parse_time 2004-W33-2T18:52:24Z
} -result {1092163944}

test clock-iso8601-6.0.1 {parse_time, full time} -body {
    clock format [clock::iso8601 parse_time 18:52:24Z] -format {%X %z} -timezone :UTC
} -result {18:52:24 +0000}

test clock-iso8601-6.1.0 {parse_time, full date time to minute and offset as +/-hh:mm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20+01:00
} -result {869077200}

test clock-iso8601-6.1.1 {parse_time, full date time to minute and offset as +/-hhmm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20+0100
} -result {869077200}

test clock-iso8601-6.2.0 {parse_time, full date time to hour and offset as +/-hh:mm} -body {
    clock::iso8601 parse_time 1997-07-16T19+01:00
} -result {869076000}

test clock-iso8601-6.2.1 {parse_time, full date time to hour and offset as +/-hhmm} -body {
    clock::iso8601 parse_time 1997-07-16T19+0100
} -result {869076000}

test clock-iso8601-6.3.0 {parse_time, full date time to second and offset as +/-hh:mm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20:30+01:00
} -result {869077230}

test clock-iso8601-6.3.1 {parse_time, full date time to second and offset as +/-hhmm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20:30+0100
} -result {869077230}

test clock-iso8601-6.4.0 {parse_time, full date time to minute and offset as +/-hh:mm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20:30.45+01:00
} -returnCodes error -result {input string does not match supplied format}

test clock-iso8601-6.4.1 {parse_time, full date time to minute and offset as +/-hhmm} -body {
    clock::iso8601 parse_time 1997-07-16T19:20:30.45+0100
} -returnCodes error -result {input string does not match supplied format}

# -------------------------------------------------------------------------

test clock-iso8601-7.0 {parse_time, bad input} -body {
    clock::iso8601 parse_time A
} -returnCodes error -result {not an iso8601 time string}

test clock-iso8601-7.1 {parse_time} -body {
    # The difference to midnight is constant.
    # The day part is not, and there is TZ.
    expr {[clock::iso8601 parse_time 08:15:30] -
          [clock::iso8601 parse_time 00:00:00]}
} -result 29730

# -------------------------------------------------------------------------

testsuiteCleanup
return

# Local Variables:
#  mode: tcl
#  indent-tabs-mode: nil
# End:

Added modules/clock/pkgIndex.tcl.







>
>
>
1
2
3
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded clock::rfc2822 0.1 [list source [file join $dir rfc2822.tcl]]
package ifneeded clock::iso8601 0.1 [list source [file join $dir iso8601.tcl]]

Added modules/clock/rfc2822.man.









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin clock_rfc2822 n 0.1]
[moddesc   {Date/Time Utilities}]
[titledesc {Parsing ISO 8601 dates/times}]
[category  {Text processing}]
[require Tcl 8.5]
[require clock::rfc2822 [opt 0.1]]
[description]

This package provides functionality to parse dates in
RFC 2822 format.

[para]

[list_begin definitions]

[call [cmd {::clock::rfc2822 parse_date}] [arg date]]

This command parses an RFC2822 date string and returns
the given date in seconds since epoch. An error is thrown
if the command is unable to parse the date.

[list_end]

[section {BUGS, IDEAS, FEEDBACK}]

This document, and the package it describes, will undoubtedly contain
bugs and other problems.

Please report such in the category [emph clock::rfc2822] of the
[uri {http://sourceforge.net/tracker/?group_id=12883} {Tcllib SF Trackers}].

Please also report any ideas for enhancements you may have for either
package and/or documentation.

[manpage_end]

Added modules/clock/rfc2822.pcx.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# -*- tcl -*- rfc2822.pcx
# Syntax of the commands provided by package rfc2822.

# For use by TclDevKit's static syntax checker.
# See http://www.activestate.com/solutions/tcl/
# See http://aspn.activestate.com/ASPN/docs/Tcl_Dev_Kit/4.0/Checker.html#pcx_api
# for the documentation describing the format of the code contained in this file
#

package require pcx
pcx::register clock::rfc2822
pcx::tcldep   0.1 needs tcl 8.5

namespace eval ::clock::rfc2822 {}

#pcx::message FOO {... text ...} type
#pcx::scan <VERSION> <NAME> <RULE>

# Switches are per clock scan (Tcl 8.5), restricted subset.
pcx::check 0.1 std ::clock::rfc2822::parse_date \
    {checkSimpleArgs 1 1 {
	checkWord
    }}

# Initialization via pcx::init.
# Use a ::rfc2822::init procedure for non-standard initialization.
pcx::complete

Added modules/clock/rfc2822.tcl.













































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
## -*- tcl -*-
# # ## ### ##### ######## ############# #####################
## Copyright (c) 2004 Kevin Kenny
## Origin http://wiki.tcl.tk/24074

# # ## ### ##### ######## ############# #####################
## Requisites

package require Tcl 8.5
package provide clock::rfc2822 0.1
namespace eval ::clock::rfc2822 {}

# # ## ### ##### ######## ############# #####################
## API

# ::clock::rfc2822::parse_date --
#
#       Parses a date expressed in RFC2822 format
#
# Parameters:
#       date - The date to parse
#
# Results:
#       Returns the date expressed in seconds from the Epoch, or throws
#       an error if the date could not be parsed.

proc ::clock::rfc2822::parse_date { date } {
    variable datepats

    # Strip comments and excess whitespace from the date field

    regsub -all -expanded {
        \(              # open parenthesis
        (:?
	 [^()[.\.]]     # character other than ()\
	     |\\.       # or backslash escape
	 )*             # any number of times
        \)              # close paren
    } $date {} date
    set date [string trim $date]

    # Match the patterns in order of preference, returning the first success

    foreach {regexp pat} $datepats {
        if { [regexp -nocase $regexp $date] } {
            return [clock scan $date -format $pat]
        }
    }

    return -code error -errorcode {CLOCK RFC2822 BADDATE} \
        "expected an RFC2822 date, got \"$date\""
}


# # ## ### ##### ######## ############# #####################
## Internals, transient, removed after initialization.

# AddDatePat --
#
#       Internal procedure that adds a date pattern to the pattern list
#
# Parameters:
#       wpat - Regexp pattern that matches the weekday
#       wgrp - Format group that matches the weekday
#       ypat - Regexp pattern that matches the year
#       ygrp - Format group that matches the year
#       mdpat - Regexp pattern that matches month and day
#       mdgrp - Format group that matches month and day
#       spat - Regexp pattern that matches the seconds of the minute
#       sgrp - Format group that matches the seconds of the minute
#       zpat - Regexp pattern that matches the time zone
#       zgrp - Format group that matches the time zone
#
# Results:
#       None
#
# Side effects:
#       Adds a complete regexp and a complete [clock scan] pattern to
#       'datepats'

proc ::clock::rfc2822::AddDatePat { wpat wgrp ypat ygrp mdpat mdgrp
				    spat sgrp zpat zgrp } {
    variable datepats

    set regexp {^[[:space:]]*}
    set pat {}
    append regexp $wpat $mdpat {[[:space:]]+} $ypat
    append pat $wgrp $mdgrp $ygrp
    append regexp {[[:space:]]+\d\d?:\d\d} $spat
    append pat { %H:%M} $sgrp
    append regexp $zpat
    append pat $zgrp
    append regexp {[[:space:]]*$}
    lappend datepats $regexp $pat
    return
}

# InitDatePats --
#
#       Internal procedure that initializes the set of date patterns
# 	allowed in an RFC2822 date
#
# Parameters:
#       permissible - 1 if erroneous (but common) time zones are to be
#                     allowed, 0 if they are to be rejected
#
# Results:
#       None.
#
# Side effects:

proc ::clock::rfc2822::InitDatePats { permissible } {
    # Produce formats for the observed variants of ISO2822 dates.
    # Permissible variants come first in the list; impermissible ones
    # come later.

    # The month and day may be "%b %d" or "%d %b"

    foreach mdpat {{[[:alpha:]]+[[:space:]]+\d\d?}
        {\d\d?[[:space:]]+[[:alpha:]]+}} \
        mdgrp {{%b %d} {%d %b}} \
        mdperm {0 1} {
            # The year may be two digits, or four. Four digit year is
            # done first.

            foreach ypat {{\d\d\d\d} {\d\d}} ygrp {%Y %y} {
                # The seconds of the minute may be provided, or
                # omitted.

                foreach spat {{:\d\d} {}} sgrp {:%S {}} {
                    # The weekday may be provided or omitted. It is
                    # common but impermissible to omit the comma after
                    # the weekday name.

                    foreach wpat {
                        {(?:Mon|T(?:ue|hu)|Wed|Fri|S(?:at|un)),[[:space:]]+}
                        {(?:Mon|T(?:ue|hu)|Wed|Fri|S(?:at|un))[[:space:]]+}
                        {}
                    } wgrp {
                        {%a, }
                        {%a }
                        {}
                    } wperm {
                        1
                        0
                        1
                    } {
                        # Time zone is defined as +/- hhmm, or as a
                        # named time zone.  Other common but buggy
                        # formats are GMT+-hh:mm, a time zone name in
                        # quotation marks, and complete omission of
                        # the time zone.

                        foreach zpat {
                            {[[:space:]]+(?:[-+]\d\d\d\d|[[:alpha:]]+)}
                            {[[:space:]]+GMT[-+]\d\d:?\d\d}
                            {[[:space:]]+"[[:alpha:]]+"}
                            {}
                        } zgrp {
                            { %Z}
                            { GMT%Z}
                            { "%Z"}
                            {}
                        } zperm {
                            1
                            0
                            0
                            0
                        } {
                            if { ($zperm && $wperm && $mdperm)
                                 == $permissible } {
                                AddDatePat $wpat $wgrp $ypat $ygrp \
                                    $mdpat $mdgrp \
                                    $spat $sgrp $zpat $zgrp
                            }
                        }
                    }
                }
            }
        }
    return
}

# # ## ### ##### ######## ############# #####################
## State

namespace eval ::clock::rfc2822 {
    namespace export parse_date
    namespace ensemble create

    variable datepats {}
}

# # ## ### ##### ######## ############# #####################
# Initialize the date patterns

namespace eval ::clock::rfc2822 {
    InitDatePats 1
    InitDatePats 0
    rename AddDatePat {}
    rename InitDatePats {}
    #puts [join $datepats \n]
}

# # ## ### ##### ######## ############# #####################

return
# Usage example, disabled

if {![info exists ::argv0] || [info script] ne $::argv0} return
puts [clock format \
          [::clock::rfc2822::parse_date {Mon(day), 23 Aug(ust) 2004 01:23:45 UT}]]
puts [clock format \
          [::clock::rfc2822::parse_date "Tue, Jul 21 2009 19:37:47 GMT-0400"]]

Added modules/clock/rfc2822.test.

























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# -------------------------------------------------------------------------
# rfc2822.test -*- tcl -*-
# (C) 2013 Andreas Kupries. BSD licensed.
# -------------------------------------------------------------------------

source [file join \
	[file dirname [file dirname [file join [pwd] [info script]]]] \
	devtools testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 2.0

testing {
    useLocal rfc2822.tcl clock::rfc2822
}

# -------------------------------------------------------------------------

test clock-rfc2822-1.0 {parse_date wrong\#args} -body {
    clock::rfc2822 parse_date
} -returnCodes error -result {wrong # args: should be "clock::rfc2822 parse_date date"}

test clock-rfc2822-1.1 {parse_date wrong\#args} -body {
    clock::rfc2822 parse_date D X
} -returnCodes error -result {wrong # args: should be "clock::rfc2822 parse_date date"}

# -------------------------------------------------------------------------

test clock-rfc2822-2.0 {parse_date, bad input} -body {
    clock::rfc2822 parse_date D
} -returnCodes error -result {expected an RFC2822 date, got "D"}

test clock-rfc2822-2.1 {parse_date} -body {
    clock::rfc2822 parse_date {Fri, 09 Sep 2005 13:51:39 -0700}
} -result 1126299099

# -------------------------------------------------------------------------
testsuiteCleanup
return

# Local Variables:
#  mode: tcl
#  indent-tabs-mode: nil
# End:

Changes to support/installation/modules.tcl.

41
42
43
44
45
46
47

48
49
50
51
52
53
54
Module  base64      _tcl  _man  _null
Module  bee         _tcl  _man  _null
Module  bench       _tcl _null  _null
Module  bibtex      _tcl  _man  _exa
Module  blowfish    _tcl  _man  _null
Module  cache       _tcl  _man  _null
Module  calendar     _tci _man  _null

Module  cmdline     _tcl  _man  _null
Module  comm        _tcl  _man  _null
Module  control      _tci _man  _null
Module  coroutine   _tcl _null  _null
Module  counter     _tcl  _man  _null
Module  crc         _tcl  _man  _null
Module  csv         _tcl  _man _exa







>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Module  base64      _tcl  _man  _null
Module  bee         _tcl  _man  _null
Module  bench       _tcl _null  _null
Module  bibtex      _tcl  _man  _exa
Module  blowfish    _tcl  _man  _null
Module  cache       _tcl  _man  _null
Module  calendar     _tci _man  _null
Module  clock       _tcl  _null _null
Module  cmdline     _tcl  _man  _null
Module  comm        _tcl  _man  _null
Module  control      _tci _man  _null
Module  coroutine   _tcl _null  _null
Module  counter     _tcl  _man  _null
Module  crc         _tcl  _man  _null
Module  csv         _tcl  _man _exa