zone 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,126 @@
1
+ # Zsh Completion for Zone
2
+
3
+ Intelligent command-line completion for the `zone` timezone conversion tool.
4
+
5
+ ## Features
6
+
7
+ - Completes all command-line flags and options
8
+ - Suggests common timezone names (UTC, local, America/New_York, etc.)
9
+ - Provides example timestamp formats
10
+ - Handles mutually exclusive options (e.g., --iso8601 vs --unix)
11
+ - Context-aware completions for option values
12
+
13
+ ## Installation
14
+
15
+ ### Method 1: User-specific installation
16
+
17
+ 1. Create a completions directory if it doesn't exist:
18
+ ```bash
19
+ mkdir -p ~/.zsh/completions
20
+ ```
21
+
22
+ 2. Copy the completion script:
23
+ ```bash
24
+ cp completions/_zone ~/.zsh/completions/
25
+ ```
26
+
27
+ 3. Add to your `~/.zshrc` (before `compinit`):
28
+ ```bash
29
+ fpath=(~/.zsh/completions $fpath)
30
+ autoload -U compinit && compinit
31
+ ```
32
+
33
+ 4. Reload your shell:
34
+ ```bash
35
+ exec zsh
36
+ ```
37
+
38
+ ### Method 2: System-wide installation
39
+
40
+ 1. Copy to the system completions directory:
41
+ ```bash
42
+ sudo cp completions/_zone /usr/local/share/zsh/site-functions/
43
+ ```
44
+
45
+ 2. Rebuild completion cache:
46
+ ```bash
47
+ rm -f ~/.zcompdump && compinit
48
+ ```
49
+
50
+ ### Method 3: Oh My Zsh
51
+
52
+ 1. Copy to Oh My Zsh completions:
53
+ ```bash
54
+ cp completions/_zone ~/.oh-my-zsh/completions/
55
+ ```
56
+
57
+ 2. Reload:
58
+ ```bash
59
+ exec zsh
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ Once installed, press `<Tab>` after typing `zone` to see available completions:
65
+
66
+ ```bash
67
+ zone <Tab> # Shows all options
68
+ zone --zone <Tab> # Shows timezone suggestions
69
+ zone --strftime <Tab> # Prompts for strftime format
70
+ zone 2025 <Tab> # Shows timestamp format examples
71
+ ```
72
+
73
+ ## Examples
74
+
75
+ ```bash
76
+ # Complete timezone names
77
+ zone "$(date)" --zone <Tab>
78
+ # Suggests: UTC, local, America/New_York, Europe/London, Asia/Tokyo, etc.
79
+
80
+ # Complete output formats
81
+ zone "now" --<Tab>
82
+ # Shows: --iso8601, --pretty, --unix, --strftime, etc.
83
+
84
+ # Mutually exclusive options
85
+ zone "now" --unix --<Tab>
86
+ # Won't suggest --iso8601, --pretty, or --strftime (incompatible)
87
+ ```
88
+
89
+ ## Testing
90
+
91
+ Verify the completion is loaded:
92
+ ```bash
93
+ which _zone
94
+ # Should output the path to the completion function
95
+ ```
96
+
97
+ Test syntax:
98
+ ```bash
99
+ zsh -n completions/_zone
100
+ # Should output nothing (syntax valid)
101
+ ```
102
+
103
+ ## Troubleshooting
104
+
105
+ **Completions not working:**
106
+ 1. Ensure `fpath` includes your completions directory:
107
+ ```bash
108
+ echo $fpath
109
+ ```
110
+
111
+ 2. Verify the completion function is loaded:
112
+ ```bash
113
+ which _zone
114
+ ```
115
+
116
+ 3. Rebuild completion cache:
117
+ ```bash
118
+ rm -f ~/.zcompdump && exec zsh
119
+ ```
120
+
121
+ **Completions are outdated:**
122
+ ```bash
123
+ # Force rebuild
124
+ rm -f ~/.zcompdump*
125
+ compinit
126
+ ```
data/completions/_zone ADDED
@@ -0,0 +1,89 @@
1
+ #compdef zone
2
+
3
+ # Zsh completion script for zone
4
+ # https://github.com/anthropics/zone
5
+
6
+ _zone() {
7
+ local context state state_descr line
8
+ typeset -A opt_args
9
+
10
+ local -a output_formats timezones_opts data_opts other_opts
11
+
12
+ output_formats=(
13
+ '(--pretty --unix --strftime)--iso8601[Output in ISO 8601 format (default)]'
14
+ '(--iso8601 --unix --strftime)-p[Output in pretty format]'
15
+ '(--iso8601 --unix --strftime)--pretty[Output in pretty format (e.g., "Jan 02 - 03:04 PM")]'
16
+ '(--iso8601 --pretty --strftime)--unix[Output as Unix timestamp]'
17
+ '(--iso8601 --pretty --unix)-f[Output format using strftime]:format string'
18
+ '(--iso8601 --pretty --unix)--strftime[Output format using strftime]:format string'
19
+ )
20
+
21
+ timezones_opts=(
22
+ '--require[Require external library]:library name'
23
+ '(-z --utc --local)-z[Convert to specified timezone]:timezone:_zone_timezones'
24
+ '(-z --utc --local)--zone[Convert to specified timezone]:timezone:_zone_timezones'
25
+ '(-z --zone --local)--utc[Convert to UTC timezone]'
26
+ '(-z --zone --utc)--local[Convert to local timezone]'
27
+ )
28
+
29
+ data_opts=(
30
+ '(-F)-F[Field index or field name]:field:(1 2 3 4 5)'
31
+ '(-F)--field[Field index or field name]:field:(1 2 3 4 5)'
32
+ '(-d)-d[Field delimiter]:delimiter'
33
+ '(-d)--delimiter[Field delimiter]:delimiter'
34
+ '--headers[Treat first line as headers]'
35
+ )
36
+
37
+ other_opts=(
38
+ '(-v)'{-v,--verbose}'[Enable verbose/debug output]'
39
+ '(-h)'{-h,--help}'[Show help message]'
40
+ )
41
+
42
+ _arguments -C -s \
43
+ $output_formats \
44
+ $timezones_opts \
45
+ $data_opts \
46
+ $other_opts \
47
+ '*:timestamp string:_zone_timestamp'
48
+ }
49
+
50
+ _zone_timezones() {
51
+ local -a timezones
52
+
53
+ # Common timezones for quick completion
54
+ timezones=(
55
+ 'UTC:Coordinated Universal Time'
56
+ 'local:Local system timezone'
57
+ 'America/New_York:Eastern Time'
58
+ 'America/Chicago:Central Time'
59
+ 'America/Denver:Mountain Time'
60
+ 'America/Los_Angeles:Pacific Time'
61
+ 'Europe/London:British Time'
62
+ 'Europe/Paris:Central European Time'
63
+ 'Asia/Tokyo:Japan Standard Time'
64
+ 'Asia/Shanghai:China Standard Time'
65
+ 'Australia/Sydney:Australian Eastern Time'
66
+ )
67
+
68
+ _describe -t timezones 'timezone' timezones
69
+
70
+ # Also allow arbitrary timezone input
71
+ _message -r 'timezone name or fuzzy match'
72
+ }
73
+
74
+ _zone_timestamp() {
75
+ # Suggest common timestamp formats
76
+ local -a formats
77
+ formats=(
78
+ 'now:Current time'
79
+ '$(date):Current date/time from date command'
80
+ '2025-01-15T10:30:00Z:ISO 8601 format'
81
+ '1736937000:Unix timestamp (seconds)'
82
+ '"5 minutes ago":Relative time'
83
+ '"1 hour from now":Relative future time'
84
+ )
85
+
86
+ _describe -t formats 'timestamp format' formats
87
+ }
88
+
89
+ _zone "$@"
@@ -0,0 +1,150 @@
1
+ # User Experience Review of Zone
2
+
3
+ ## My Thoughts on Zone as a User
4
+
5
+ ### What Zone Does Really Well
6
+
7
+ **1. Solves a Real Pain Point**
8
+ Timezone conversion is genuinely annoying. Looking at `2025-01-15T10:30:00Z` in logs and mentally calculating "what time was that for me?" is cognitive overhead. Zone eliminates that instantly. The name is perfect - simple, memorable, and describes exactly what it does.
9
+
10
+ **2. The Dual-Mode Architecture is Brilliant**
11
+ The pattern/field mode split is exactly right:
12
+ - **Pattern mode** feels like `bat` for timestamps - magical and effortless. Just pipe text through and timestamps become readable. No configuration needed.
13
+ - **Field mode** handles the structured data case without polluting the simple case with complexity.
14
+
15
+ Requiring explicit `--field` AND `--delimiter` was the right call. Auto-detection adds complexity and surprising behavior. Explicit is better.
16
+
17
+ **3. The New Defaults are Perfect**
18
+ Before: `zone "2025-01-15T10:30:00Z"` → `2025-01-15T10:30:00Z` (no visible change)
19
+ After: `zone "2025-01-15T10:30:00Z"` → `Jan 15, 2025 - 5:30 AM EST` (immediate value)
20
+
21
+ This is *excellent* UX design. New users see the tool working immediately. The "principle of least surprise" - of course I want my local time, that's why I'm using a timezone tool!
22
+
23
+ **4. Idempotency is Underrated**
24
+ Being able to pipe zone output back through zone is surprisingly powerful. It means zone becomes composable - you can chain transformations without worrying about format compatibility. This is Unix philosophy done right.
25
+
26
+ **5. The Three Pretty Formats are Well-Chosen**
27
+ - `-p 1`: North American default, maximum readability
28
+ - `-p 2`: International/technical users, 24-hour
29
+ - `-p 3`: Sortable, machine-friendly but still readable
30
+
31
+ The progression makes sense and covers real use cases without format proliferation.
32
+
33
+ ### What Could Be Even Better
34
+
35
+ **1. Discovery of Pretty Format Variants**
36
+ Running `zone --help` shows:
37
+ ```
38
+ -p, --pretty [STYLE] Pretty format (1=12hr, 2=24hr, 3=ISO-compact, default: 1)
39
+ ```
40
+
41
+ This is compact but not discoverable. A user has to know to try `-p 2` to see what it looks like. Could consider:
42
+ - Showing example output in `--help` for each format
43
+ - Or: `zone --formats` command that prints all format examples
44
+
45
+ **2. Relative Time Parsing Feels Incomplete**
46
+ Zone parses "5 hours ago" but the pattern doesn't match common formats like:
47
+ - "2 hours ago" (GitHub, Twitter, etc.)
48
+ - "in 3 days" (Google Calendar)
49
+ - "yesterday" / "tomorrow"
50
+
51
+ Either commit fully to natural language parsing (integrate with `chronic` gem?) or remove it entirely. Half-done features are confusing.
52
+
53
+ **3. Color Choice is Good but Could Be Configurable**
54
+ Cyan for timestamps is reasonable, but some users might want:
55
+ - Different colors for different timezones (red=past, green=future)
56
+ - Bold instead of color
57
+ - User-configurable via env var
58
+
59
+ Not critical, but would be nice for power users.
60
+
61
+ **4. Error Messages Could Be More Helpful**
62
+ ```bash
63
+ $ echo "invalid" | zone --field 2 --delimiter ','
64
+ Error: Could not parse time 'invalid'
65
+ ```
66
+
67
+ Could suggest: "Does not look like a timestamp. Expected formats: ISO8601, Unix timestamp, etc."
68
+
69
+ **5. The Name "Pretty" Format**
70
+ Calling it "pretty" format is subjective. Some users might think ISO8601 is "pretty" (it's certainly more standardized). Consider:
71
+ - "human" format (human-readable)
72
+ - "readable" format
73
+ - Just "format 1/2/3" without the "pretty" label
74
+
75
+ Minor bikeshedding, but naming matters.
76
+
77
+ ### Outstanding Design Decisions
78
+
79
+ **1. No Magic, Clear Contracts**
80
+ - Field mode requires explicit delimiter ✓
81
+ - Pattern mode is clearly the default ✓
82
+ - Flags do one thing ✓
83
+
84
+ No surprises, no hidden behavior.
85
+
86
+ **2. Fuzzy Timezone Matching**
87
+ ```bash
88
+ zone --zone tokyo # Just works
89
+ zone --zone pacific # Just works
90
+ zone --zone "new york" # Just works
91
+ ```
92
+
93
+ This is *delightful*. Makes the tool feel intelligent without being magical.
94
+
95
+ **3. No External Dependencies (Base)**
96
+ Pure Ruby stdlib (until you `--require active_support`). Fast startup, easy installation, no surprises.
97
+
98
+ **4. The Pattern Numbering System (P01_, P02_)**
99
+ This is clever engineering:
100
+ - Easy to add new patterns (just add P08_)
101
+ - Priority is explicit and visible
102
+ - Self-documenting code
103
+
104
+ Really nice.
105
+
106
+ ### Use Cases Where Zone Shines
107
+
108
+ 1. **Log Analysis**: `tail -f app.log | zone` - instant readability
109
+ 2. **API Development**: Converting timestamps in JSON responses during debugging
110
+ 3. **Data Migration**: Converting CSV/TSV timestamp columns
111
+ 4. **Slack/IRC Bots**: Processing messages with embedded timestamps
112
+ 5. **International Teams**: Converting meeting times across timezones
113
+
114
+ ### Competitive Analysis
115
+
116
+ **vs `date` command**: Zone is *way* easier for interactive use. Compare:
117
+ ```bash
118
+ # date
119
+ date -d "2025-01-15T10:30:00Z" "+%b %d, %Y - %I:%M %p %Z"
120
+
121
+ # zone
122
+ zone "2025-01-15T10:30:00Z"
123
+ ```
124
+
125
+ Zone wins on ergonomics by a mile.
126
+
127
+ **vs `dateutils`**: More powerful but also more complex. Zone targets the 80% use case perfectly.
128
+
129
+ **vs Online Converters**: Zone works offline, in pipelines, is automatable. Different category.
130
+
131
+ ### Final Verdict
132
+
133
+ Zone is a **10/10 tool for its intended use case**. It's:
134
+ - Simple enough for beginners (just type `zone` with a timestamp)
135
+ - Powerful enough for experts (field mode, regex delimiters, chaining)
136
+ - Well-documented
137
+ - Follows Unix philosophy
138
+ - Actually useful in daily work
139
+
140
+ The recent changes (colors, pretty formats, better defaults) transformed it from "neat utility" to "essential tool I'd add to every machine."
141
+
142
+ ### One More Thing
143
+
144
+ The pattern mode really feels like magic the first time you use it:
145
+ ```bash
146
+ echo "Server started at 1736937000 and crashed at 1736940600" | zone
147
+ # Server started at Jan 15, 2025 - 5:30 AM EST and crashed at Jan 15, 2025 - 6:30 AM EST
148
+ ```
149
+
150
+ That's the kind of "just works" experience that makes a tool memorable. Well done.
data/exe/zone CHANGED
@@ -2,257 +2,6 @@
2
2
 
3
3
  # frozen_string_literal: true
4
4
 
5
- require 'rubygems'
6
- require 'logger'
7
- require 'optparse'
8
- require 'tzinfo'
9
- require 'time'
10
- require 'date'
5
+ require_relative '../lib/zone'
11
6
 
12
- options = { delimiter: nil, strftime: nil, iso8601: false, pretty: false, headers: false, unix: false, index: 1, zone: nil, utc: false, local: false }
13
- parser = OptionParser.new do |parser|
14
- parser.on '--index N', '-i N', Integer, 'Index of the field to convert (default: 1)'
15
- parser.on '--delimiter PATTERN', '-d', 'Field delimiter (default: space)'
16
- parser.on '--iso8601', 'Output in ISO 8601 (default: true)'
17
- parser.on '--strftime FORMAT', '-f', 'Output format using strftime (default: none)'
18
- parser.on '--pretty', 'Output in pretty format (e.g., "Jan 02 - 03:04 PM")'
19
- parser.on '--unix', 'Output as Unix timestamp (default: false)'
20
- parser.on '--zone TZ', 'Convert to time zone (default: local time zone)'
21
- parser.on '--local', 'Convert to local time zone (alias for --zone local)'
22
- parser.on '--utc', 'Convert to UTC time zone (alias for --zone UTC)'
23
- parser.on '--headers', 'Skip the first line as headers'
24
- parser.on '--verbose', '-v', 'Enable verbose/debug output'
25
- parser.on '--help', '-h', 'Show this help message' do
26
- puts parser
27
- exit
28
- end
29
- end
30
-
31
- parser.parse!(into: options)
32
-
33
- COLORS = {
34
- reset: "\e[0m",
35
- bold: "\e[1m",
36
- cyan: "\e[36m",
37
- yellow: "\e[33m",
38
- red: "\e[31m",
39
- gray: "\e[90m"
40
- }
41
-
42
- $logger = Logger.new($stderr).tap do |l|
43
- l.formatter = ->(severity, _datetime, _progname, message) {
44
- color = case severity
45
- when "INFO" then COLORS[:cyan]
46
- when "WARN" then COLORS[:yellow]
47
- when "ERROR" then COLORS[:red]
48
- else COLORS[:gray]
49
- end
50
-
51
- prefix = case severity
52
- when "INFO" then "→"
53
- when "WARN" then "⚠"
54
- when "ERROR" then "✗"
55
- else "·"
56
- end
57
-
58
- "#{color}#{prefix} #{message}#{COLORS[:reset]}\n"
59
- }
60
- l.level = options.delete(:verbose) ? Logger::INFO : Logger::WARN
61
- end
62
-
63
- format = (
64
- case options
65
- in { strftime: nil, iso8601: true, unix: false, pretty: false } then :iso8601
66
- in { strftime: nil, iso8601: false, unix: true, pretty: false } then :unix
67
- in { strftime: String, iso8601: false, unix: false, pretty: false } then :strftime
68
- in { strftime: nil, iso8601: false, unix: false, pretty: true } then :pretty
69
- in { strftime: nil, iso8601: false, unix: false, pretty: false } then :iso8601
70
- else
71
- $logger.error 'Error: Only one of --strftime, --iso8601, or --unix can be specified.'
72
- exit 1
73
- end
74
- )
75
-
76
- zone = (
77
- case options
78
- in { utc: true, local: false, zone: nil } then 'utc'
79
- in { utc: false, local: true, zone: nil } then 'local'
80
- in { zone: nil, utc: false, local: false } then 'utc'
81
- in { zone: String => z, utc: false, local: false } then z
82
- else
83
- $logger.error 'Error: Only one of --zone, --local, or --utc can be specified.'
84
- exit 1
85
- end
86
- )
87
-
88
- class TimezoneSearch
89
- attr_reader :keyword, :debug
90
-
91
- def self.all_zones
92
- TZInfo::Timezone.all_identifiers
93
- end
94
-
95
- def initialize(
96
- keyword,
97
- debug: false,
98
- logger: $logger || Logger.new($stderr)
99
- )
100
- @keyword = keyword
101
- @debug = debug
102
- @logger = logger
103
- end
104
-
105
- def execute
106
- begin
107
- TZInfo::Timezone.get(keyword)
108
- rescue TZInfo::InvalidTimezoneIdentifier
109
- search_wildcard
110
- end
111
- end
112
-
113
- def us_wildcard
114
- keyword
115
- .gsub(/^(?:US)?\/?/, 'US/')
116
- .gsub(/$/,'.*')
117
- .then { Regexp.new(it, Regexp::IGNORECASE) }
118
- end
119
-
120
- def all_wildcard
121
- Regexp.new(".*#{keyword}.*", Regexp::IGNORECASE)
122
- end
123
-
124
- def search_wildcard
125
- case self.class.all_zones
126
- in [*, ^(us_wildcard) => found_zone, *]
127
- in [*, ^(all_wildcard) => found_zone, *]
128
- else nil
129
- end
130
-
131
- return unless found_zone
132
-
133
- TZInfo::Timezone.get(found_zone).tap do
134
- @logger.info "Using time zone '#{found_zone}' matching pattern '#{@keyword}'."
135
- end
136
- end
137
-
138
- private
139
-
140
- def log(message)
141
- warn message if @debug
142
- end
143
- end
144
-
145
- zone_callables = Hash.new do |hash, key|
146
- hash[key] = ->(time) {
147
- search = TimezoneSearch.new(key)
148
- if zone = search.execute
149
- zone.to_local(time)
150
- else
151
- $logger.warn "Error: Invalid time zone identifier '#{key}'."
152
- time
153
- end
154
- }
155
- end
156
-
157
- zone_callables.merge!(
158
- 'utc' => ->(t) { t.utc },
159
- 'local' => ->(t) { t.localtime },
160
- 'UTC' => ->(t) { t.utc },
161
- )
162
-
163
- actual_index = options[:index] - 1
164
- zone_callable = zone_callables[zone]
165
-
166
- # Detect if args are timestamps (not filenames)
167
- timestamps = []
168
- if ARGV.any? && ARGV.all? { |arg| arg.match?(/^\d/) || arg.match?(/[A-Z][a-z]{2}/) || arg.match?(/:/) }
169
- $logger.info "Treating arguments as timestamp strings."
170
- timestamps = ARGV.dup
171
- ARGV.clear # Clear so ARGF will read from STDIN if piped
172
- end
173
-
174
- # Build input enumerable
175
- input = (
176
- if timestamps.any?
177
- timestamps.each
178
- elsif ARGV.any? || !STDIN.tty?
179
- ARGF.each_line(chomp: true)
180
- else
181
- [Time.now.to_s].each
182
- end
183
- )
184
-
185
- input.each do |line|
186
- case [options, $.]
187
- in { headers: true }, 1 then next
188
- in { delimiter: String => delimiter }
189
- in { delimiter: nil }
190
- $logger.info "Auto-detecting delimiter for line: #{line.inspect}"
191
- options[:delimiter] = (
192
- case line
193
- in /,\s*/
194
- $logger.info "Using comma with whitespace as delimiter."
195
- /,\s*/
196
- in /\t/
197
- $logger.info "Using tab as delimiter."
198
- "\t"
199
- in /,/
200
- $logger.info "Using comma as delimiter."
201
- ','
202
- else
203
- $logger.info "Could not detect delimiter. Using whitespace."
204
- /\s+/
205
- end
206
- )
207
- else
208
- ""
209
- end
210
-
211
- delimiter = options[:delimiter] || ""
212
-
213
- fields = (
214
- case [line, delimiter]
215
- in String, "" then [line]
216
- in String, /^.+$/ then line.split(delimiter)
217
- else [line]
218
- end
219
- )
220
-
221
- target = fields[actual_index]
222
-
223
- time = (
224
- begin
225
- case target
226
- in Time then target
227
- in DateTime then target.to_time
228
- in Date then target.to_time
229
- in /^[0-9\.]+$/
230
- Time.at(target.to_f)
231
- else
232
- DateTime.parse(target).to_time
233
- end
234
- rescue StandardError => e
235
- $logger.warn "Warning: Could not parse time '#{target}'. Skipping line."
236
- $logger.warn " #{e.class}: #{e.message}"
237
- next
238
- end
239
- )
240
-
241
- converted = zone_callable.call(time)
242
- formatted = (
243
- case format
244
- in :pretty
245
- if (Time.now - converted).abs > 30 * 24 * 60 * 60
246
- converted.strftime("%b %d, %Y - %I:%M %p %Z")
247
- else
248
- converted.strftime("%b %d - %I:%M %p %Z")
249
- end
250
- in :strftime then converted.strftime(options[:strftime])
251
- in :iso8601 then converted.iso8601
252
- in :unix then converted.to_i
253
- end
254
- )
255
-
256
- fields[actual_index] = formatted
257
- puts fields.join(delimiter)
258
- end
7
+ Zone::CLI.run(ARGV)
data/lib/zone/cli.rb ADDED
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options'
4
+ require_relative 'input'
5
+ require_relative 'output'
6
+ require_relative 'transform'
7
+ require_relative 'colors'
8
+ require_relative 'logging'
9
+ require_relative 'pattern'
10
+ require_relative 'field'
11
+
12
+ module Zone
13
+ class CLI
14
+ def self.run(argv)
15
+ new(argv).run
16
+ end
17
+
18
+ def initialize(argv)
19
+ @argv = argv
20
+ end
21
+
22
+ def run
23
+ options = Options.new
24
+ options.parse!(@argv)
25
+ options.validate!
26
+
27
+ setup_logger!(options.verbose)
28
+ setup_active_support!
29
+
30
+ input = Input.new(@argv)
31
+ output = Output.new(color_mode: options.color)
32
+ transformation = Transform.build(zone: options.zone, format: options.format)
33
+
34
+ if options.field
35
+ Field.process(input, output, transformation, options, @logger)
36
+ else
37
+ Pattern.process(input, output, transformation, @logger)
38
+ end
39
+ rescue OptionParser::MissingArgument, OptionParser::InvalidOption, OptionParser::InvalidArgument => e
40
+ $stderr.puts Colors.colors($stderr).red("Error:") + " #{e.message}"
41
+ $stderr.puts "Run 'zone --help' for usage information."
42
+ exit 1
43
+ rescue ArgumentError, StandardError => e
44
+ message = e.message.gsub(/'([^']+)'/) do
45
+ "'#{Colors.colors($stderr).bold($1)}'"
46
+ end
47
+ $stderr.puts Colors.colors($stderr).red("Error:") + " #{message}"
48
+ exit 1
49
+ end
50
+
51
+ private
52
+
53
+ def setup_logger!(verbose)
54
+ @logger = Logging.build(verbose: verbose)
55
+ end
56
+
57
+ def setup_active_support!
58
+ if defined?(ActiveSupport)
59
+ ActiveSupport.to_time_preserves_timezone = true
60
+ end
61
+ end
62
+
63
+ end
64
+ end