pomera-ai-commander 0.1.0 → 1.2.1
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.
- package/LICENSE +21 -21
- package/README.md +105 -680
- package/bin/pomera-ai-commander.js +62 -62
- package/core/__init__.py +65 -65
- package/core/app_context.py +482 -482
- package/core/async_text_processor.py +421 -421
- package/core/backup_manager.py +655 -655
- package/core/backup_recovery_manager.py +1033 -1033
- package/core/content_hash_cache.py +508 -508
- package/core/context_menu.py +313 -313
- package/core/data_validator.py +1066 -1066
- package/core/database_connection_manager.py +744 -744
- package/core/database_curl_settings_manager.py +608 -608
- package/core/database_promera_ai_settings_manager.py +446 -446
- package/core/database_schema.py +411 -411
- package/core/database_schema_manager.py +395 -395
- package/core/database_settings_manager.py +1507 -1507
- package/core/database_settings_manager_interface.py +456 -456
- package/core/dialog_manager.py +734 -734
- package/core/efficient_line_numbers.py +510 -510
- package/core/error_handler.py +746 -746
- package/core/error_service.py +431 -431
- package/core/event_consolidator.py +511 -511
- package/core/mcp/__init__.py +43 -43
- package/core/mcp/protocol.py +288 -288
- package/core/mcp/schema.py +251 -251
- package/core/mcp/server_stdio.py +299 -299
- package/core/mcp/tool_registry.py +2372 -2345
- package/core/memory_efficient_text_widget.py +711 -711
- package/core/migration_manager.py +914 -914
- package/core/migration_test_suite.py +1085 -1085
- package/core/migration_validator.py +1143 -1143
- package/core/optimized_find_replace.py +714 -714
- package/core/optimized_pattern_engine.py +424 -424
- package/core/optimized_search_highlighter.py +552 -552
- package/core/performance_monitor.py +674 -674
- package/core/persistence_manager.py +712 -712
- package/core/progressive_stats_calculator.py +632 -632
- package/core/regex_pattern_cache.py +529 -529
- package/core/regex_pattern_library.py +350 -350
- package/core/search_operation_manager.py +434 -434
- package/core/settings_defaults_registry.py +1087 -1087
- package/core/settings_integrity_validator.py +1111 -1111
- package/core/settings_serializer.py +557 -557
- package/core/settings_validator.py +1823 -1823
- package/core/smart_stats_calculator.py +709 -709
- package/core/statistics_update_manager.py +619 -619
- package/core/stats_config_manager.py +858 -858
- package/core/streaming_text_handler.py +723 -723
- package/core/task_scheduler.py +596 -596
- package/core/update_pattern_library.py +168 -168
- package/core/visibility_monitor.py +596 -596
- package/core/widget_cache.py +498 -498
- package/mcp.json +51 -61
- package/package.json +61 -57
- package/pomera.py +7482 -7482
- package/pomera_mcp_server.py +183 -144
- package/requirements.txt +32 -0
- package/tools/__init__.py +4 -4
- package/tools/ai_tools.py +2891 -2891
- package/tools/ascii_art_generator.py +352 -352
- package/tools/base64_tools.py +183 -183
- package/tools/base_tool.py +511 -511
- package/tools/case_tool.py +308 -308
- package/tools/column_tools.py +395 -395
- package/tools/cron_tool.py +884 -884
- package/tools/curl_history.py +600 -600
- package/tools/curl_processor.py +1207 -1207
- package/tools/curl_settings.py +502 -502
- package/tools/curl_tool.py +5467 -5467
- package/tools/diff_viewer.py +1071 -1071
- package/tools/email_extraction_tool.py +248 -248
- package/tools/email_header_analyzer.py +425 -425
- package/tools/extraction_tools.py +250 -250
- package/tools/find_replace.py +1750 -1750
- package/tools/folder_file_reporter.py +1463 -1463
- package/tools/folder_file_reporter_adapter.py +480 -480
- package/tools/generator_tools.py +1216 -1216
- package/tools/hash_generator.py +255 -255
- package/tools/html_tool.py +656 -656
- package/tools/jsonxml_tool.py +729 -729
- package/tools/line_tools.py +419 -419
- package/tools/markdown_tools.py +561 -561
- package/tools/mcp_widget.py +1417 -1417
- package/tools/notes_widget.py +973 -973
- package/tools/number_base_converter.py +372 -372
- package/tools/regex_extractor.py +571 -571
- package/tools/slug_generator.py +310 -310
- package/tools/sorter_tools.py +458 -458
- package/tools/string_escape_tool.py +392 -392
- package/tools/text_statistics_tool.py +365 -365
- package/tools/text_wrapper.py +430 -430
- package/tools/timestamp_converter.py +421 -421
- package/tools/tool_loader.py +710 -710
- package/tools/translator_tools.py +522 -522
- package/tools/url_link_extractor.py +261 -261
- package/tools/url_parser.py +204 -204
- package/tools/whitespace_tools.py +355 -355
- package/tools/word_frequency_counter.py +146 -146
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
|
@@ -1,422 +1,422 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Timestamp Converter Module - Date/time conversion utilities
|
|
3
|
-
|
|
4
|
-
This module provides timestamp conversion functionality
|
|
5
|
-
for the Pomera AI Commander application.
|
|
6
|
-
|
|
7
|
-
Features:
|
|
8
|
-
- Unix timestamp to human-readable date
|
|
9
|
-
- Human-readable date to Unix timestamp
|
|
10
|
-
- Multiple date format options
|
|
11
|
-
- Relative time display
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import tkinter as tk
|
|
15
|
-
from tkinter import ttk
|
|
16
|
-
from datetime import datetime, timezone
|
|
17
|
-
import time
|
|
18
|
-
import re
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TimestampConverterProcessor:
|
|
22
|
-
"""Timestamp converter processor."""
|
|
23
|
-
|
|
24
|
-
DATE_FORMATS = {
|
|
25
|
-
"iso": "%Y-%m-%dT%H:%M:%S",
|
|
26
|
-
"iso_date": "%Y-%m-%d",
|
|
27
|
-
"us": "%m/%d/%Y %I:%M:%S %p",
|
|
28
|
-
"us_date": "%m/%d/%Y",
|
|
29
|
-
"eu": "%d/%m/%Y %H:%M:%S",
|
|
30
|
-
"eu_date": "%d/%m/%Y",
|
|
31
|
-
"long": "%B %d, %Y %H:%M:%S",
|
|
32
|
-
"short": "%b %d, %Y %H:%M",
|
|
33
|
-
"rfc2822": "%a, %d %b %Y %H:%M:%S",
|
|
34
|
-
"custom": None
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@staticmethod
|
|
38
|
-
def unix_to_datetime(timestamp, use_utc=False):
|
|
39
|
-
"""Convert Unix timestamp to datetime object."""
|
|
40
|
-
try:
|
|
41
|
-
ts = float(timestamp)
|
|
42
|
-
# Handle milliseconds
|
|
43
|
-
if ts > 1e12:
|
|
44
|
-
ts = ts / 1000
|
|
45
|
-
|
|
46
|
-
if use_utc:
|
|
47
|
-
return datetime.fromtimestamp(ts, tz=timezone.utc)
|
|
48
|
-
else:
|
|
49
|
-
return datetime.fromtimestamp(ts)
|
|
50
|
-
except (ValueError, OSError) as e:
|
|
51
|
-
return None
|
|
52
|
-
|
|
53
|
-
@staticmethod
|
|
54
|
-
def datetime_to_unix(dt):
|
|
55
|
-
"""Convert datetime object to Unix timestamp."""
|
|
56
|
-
return int(dt.timestamp())
|
|
57
|
-
|
|
58
|
-
@staticmethod
|
|
59
|
-
def format_datetime(dt, format_name="iso", custom_format=None):
|
|
60
|
-
"""Format datetime using specified format."""
|
|
61
|
-
if format_name == "custom" and custom_format:
|
|
62
|
-
fmt = custom_format
|
|
63
|
-
else:
|
|
64
|
-
fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name, "%Y-%m-%d %H:%M:%S")
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
return dt.strftime(fmt)
|
|
68
|
-
except Exception:
|
|
69
|
-
return str(dt)
|
|
70
|
-
|
|
71
|
-
@staticmethod
|
|
72
|
-
def parse_datetime(text, format_name="iso", custom_format=None):
|
|
73
|
-
"""Parse datetime from string."""
|
|
74
|
-
text = text.strip()
|
|
75
|
-
|
|
76
|
-
# Try to detect Unix timestamp
|
|
77
|
-
if re.match(r'^\d{10,13}$', text):
|
|
78
|
-
ts = float(text)
|
|
79
|
-
if ts > 1e12:
|
|
80
|
-
ts = ts / 1000
|
|
81
|
-
return datetime.fromtimestamp(ts)
|
|
82
|
-
|
|
83
|
-
# Try specified format
|
|
84
|
-
if format_name == "custom" and custom_format:
|
|
85
|
-
fmt = custom_format
|
|
86
|
-
else:
|
|
87
|
-
fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name)
|
|
88
|
-
|
|
89
|
-
if fmt:
|
|
90
|
-
try:
|
|
91
|
-
return datetime.strptime(text, fmt)
|
|
92
|
-
except ValueError:
|
|
93
|
-
pass
|
|
94
|
-
|
|
95
|
-
# Try common formats
|
|
96
|
-
common_formats = [
|
|
97
|
-
"%Y-%m-%dT%H:%M:%S",
|
|
98
|
-
"%Y-%m-%d %H:%M:%S",
|
|
99
|
-
"%Y-%m-%d",
|
|
100
|
-
"%m/%d/%Y %H:%M:%S",
|
|
101
|
-
"%m/%d/%Y",
|
|
102
|
-
"%d/%m/%Y %H:%M:%S",
|
|
103
|
-
"%d/%m/%Y",
|
|
104
|
-
"%B %d, %Y",
|
|
105
|
-
"%b %d, %Y",
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
for fmt in common_formats:
|
|
109
|
-
try:
|
|
110
|
-
return datetime.strptime(text, fmt)
|
|
111
|
-
except ValueError:
|
|
112
|
-
continue
|
|
113
|
-
|
|
114
|
-
return None
|
|
115
|
-
|
|
116
|
-
@staticmethod
|
|
117
|
-
def relative_time(dt):
|
|
118
|
-
"""Get relative time string (e.g., '2 hours ago')."""
|
|
119
|
-
now = datetime.now()
|
|
120
|
-
diff = now - dt
|
|
121
|
-
|
|
122
|
-
seconds = diff.total_seconds()
|
|
123
|
-
|
|
124
|
-
if seconds < 0:
|
|
125
|
-
# Future
|
|
126
|
-
seconds = abs(seconds)
|
|
127
|
-
suffix = "from now"
|
|
128
|
-
else:
|
|
129
|
-
suffix = "ago"
|
|
130
|
-
|
|
131
|
-
if seconds < 60:
|
|
132
|
-
return f"{int(seconds)} seconds {suffix}"
|
|
133
|
-
elif seconds < 3600:
|
|
134
|
-
minutes = int(seconds / 60)
|
|
135
|
-
return f"{minutes} minute{'s' if minutes != 1 else ''} {suffix}"
|
|
136
|
-
elif seconds < 86400:
|
|
137
|
-
hours = int(seconds / 3600)
|
|
138
|
-
return f"{hours} hour{'s' if hours != 1 else ''} {suffix}"
|
|
139
|
-
elif seconds < 2592000: # 30 days
|
|
140
|
-
days = int(seconds / 86400)
|
|
141
|
-
return f"{days} day{'s' if days != 1 else ''} {suffix}"
|
|
142
|
-
elif seconds < 31536000: # 365 days
|
|
143
|
-
months = int(seconds / 2592000)
|
|
144
|
-
return f"{months} month{'s' if months != 1 else ''} {suffix}"
|
|
145
|
-
else:
|
|
146
|
-
years = int(seconds / 31536000)
|
|
147
|
-
return f"{years} year{'s' if years != 1 else ''} {suffix}"
|
|
148
|
-
|
|
149
|
-
@staticmethod
|
|
150
|
-
def convert_timestamp(text, input_format="unix", output_format="iso",
|
|
151
|
-
use_utc=False, custom_format=None, show_relative=False):
|
|
152
|
-
"""Convert timestamp between formats."""
|
|
153
|
-
text = text.strip()
|
|
154
|
-
|
|
155
|
-
if not text:
|
|
156
|
-
return ""
|
|
157
|
-
|
|
158
|
-
# Parse input
|
|
159
|
-
if input_format == "unix":
|
|
160
|
-
dt = TimestampConverterProcessor.unix_to_datetime(text, use_utc)
|
|
161
|
-
else:
|
|
162
|
-
dt = TimestampConverterProcessor.parse_datetime(text, input_format, custom_format)
|
|
163
|
-
|
|
164
|
-
if dt is None:
|
|
165
|
-
return f"Error: Could not parse '{text}'"
|
|
166
|
-
|
|
167
|
-
# Format output
|
|
168
|
-
if output_format == "unix":
|
|
169
|
-
result = str(TimestampConverterProcessor.datetime_to_unix(dt))
|
|
170
|
-
else:
|
|
171
|
-
result = TimestampConverterProcessor.format_datetime(dt, output_format, custom_format)
|
|
172
|
-
|
|
173
|
-
if show_relative:
|
|
174
|
-
relative = TimestampConverterProcessor.relative_time(dt)
|
|
175
|
-
result += f" ({relative})"
|
|
176
|
-
|
|
177
|
-
return result
|
|
178
|
-
|
|
179
|
-
@staticmethod
|
|
180
|
-
def convert_batch(text, input_format="unix", output_format="iso",
|
|
181
|
-
use_utc=False, custom_format=None, show_relative=False):
|
|
182
|
-
"""Convert multiple timestamps."""
|
|
183
|
-
lines = text.strip().split('\n')
|
|
184
|
-
results = []
|
|
185
|
-
|
|
186
|
-
for line in lines:
|
|
187
|
-
line = line.strip()
|
|
188
|
-
if line:
|
|
189
|
-
result = TimestampConverterProcessor.convert_timestamp(
|
|
190
|
-
line, input_format, output_format, use_utc, custom_format, show_relative
|
|
191
|
-
)
|
|
192
|
-
results.append(result)
|
|
193
|
-
else:
|
|
194
|
-
results.append('')
|
|
195
|
-
|
|
196
|
-
return '\n'.join(results)
|
|
197
|
-
|
|
198
|
-
@staticmethod
|
|
199
|
-
def get_current_timestamp():
|
|
200
|
-
"""Get current Unix timestamp."""
|
|
201
|
-
return str(int(time.time()))
|
|
202
|
-
|
|
203
|
-
@staticmethod
|
|
204
|
-
def get_current_datetime(format_name="iso", custom_format=None):
|
|
205
|
-
"""Get current datetime in specified format."""
|
|
206
|
-
return TimestampConverterProcessor.format_datetime(
|
|
207
|
-
datetime.now(), format_name, custom_format
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
class TimestampConverterWidget(ttk.Frame):
|
|
212
|
-
"""Widget for timestamp converter tool."""
|
|
213
|
-
|
|
214
|
-
def __init__(self, parent, app):
|
|
215
|
-
super().__init__(parent)
|
|
216
|
-
self.app = app
|
|
217
|
-
self.processor = TimestampConverterProcessor()
|
|
218
|
-
|
|
219
|
-
self.input_format = tk.StringVar(value="unix")
|
|
220
|
-
self.output_format = tk.StringVar(value="iso")
|
|
221
|
-
self.use_utc = tk.BooleanVar(value=False)
|
|
222
|
-
self.custom_format = tk.StringVar(value="%Y-%m-%d %H:%M:%S")
|
|
223
|
-
self.show_relative = tk.BooleanVar(value=False)
|
|
224
|
-
|
|
225
|
-
self.create_widgets()
|
|
226
|
-
self.load_settings()
|
|
227
|
-
|
|
228
|
-
def create_widgets(self):
|
|
229
|
-
"""Creates the widget interface."""
|
|
230
|
-
# Input format
|
|
231
|
-
input_frame = ttk.LabelFrame(self, text="Input Format", padding=10)
|
|
232
|
-
input_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
233
|
-
|
|
234
|
-
formats = [("Unix Timestamp", "unix"), ("ISO 8601", "iso"),
|
|
235
|
-
("US Date", "us"), ("EU Date", "eu"), ("Auto-detect", "auto")]
|
|
236
|
-
|
|
237
|
-
for i, (text, value) in enumerate(formats):
|
|
238
|
-
ttk.Radiobutton(input_frame, text=text,
|
|
239
|
-
variable=self.input_format, value=value,
|
|
240
|
-
command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
|
|
241
|
-
|
|
242
|
-
# Output format
|
|
243
|
-
output_frame = ttk.LabelFrame(self, text="Output Format", padding=10)
|
|
244
|
-
output_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
245
|
-
|
|
246
|
-
output_formats = [
|
|
247
|
-
("Unix Timestamp", "unix"), ("ISO 8601", "iso"), ("ISO Date", "iso_date"),
|
|
248
|
-
("US Format", "us"), ("EU Format", "eu"), ("Long", "long"),
|
|
249
|
-
("Short", "short"), ("RFC 2822", "rfc2822"), ("Custom", "custom")
|
|
250
|
-
]
|
|
251
|
-
|
|
252
|
-
for i, (text, value) in enumerate(output_formats):
|
|
253
|
-
ttk.Radiobutton(output_frame, text=text,
|
|
254
|
-
variable=self.output_format, value=value,
|
|
255
|
-
command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
|
|
256
|
-
|
|
257
|
-
# Custom format
|
|
258
|
-
custom_frame = ttk.Frame(self)
|
|
259
|
-
custom_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
260
|
-
|
|
261
|
-
ttk.Label(custom_frame, text="Custom Format:").pack(side=tk.LEFT)
|
|
262
|
-
ttk.Entry(custom_frame, textvariable=self.custom_format, width=25).pack(side=tk.LEFT, padx=5)
|
|
263
|
-
ttk.Label(custom_frame, text="(e.g., %Y-%m-%d)", font=('TkDefaultFont', 8)).pack(side=tk.LEFT)
|
|
264
|
-
|
|
265
|
-
# Options
|
|
266
|
-
options_frame = ttk.Frame(self)
|
|
267
|
-
options_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
268
|
-
|
|
269
|
-
ttk.Checkbutton(options_frame, text="Use UTC",
|
|
270
|
-
variable=self.use_utc,
|
|
271
|
-
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
272
|
-
ttk.Checkbutton(options_frame, text="Show Relative Time",
|
|
273
|
-
variable=self.show_relative,
|
|
274
|
-
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
275
|
-
|
|
276
|
-
# Buttons
|
|
277
|
-
buttons_frame = ttk.Frame(self)
|
|
278
|
-
buttons_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
279
|
-
|
|
280
|
-
ttk.Button(buttons_frame, text="Convert",
|
|
281
|
-
command=self.convert).pack(side=tk.LEFT, padx=5)
|
|
282
|
-
ttk.Button(buttons_frame, text="Insert Current Time",
|
|
283
|
-
command=self.insert_current).pack(side=tk.LEFT, padx=5)
|
|
284
|
-
|
|
285
|
-
def load_settings(self):
|
|
286
|
-
"""Load settings from the application."""
|
|
287
|
-
settings = self.app.settings.get("tool_settings", {}).get("Timestamp Converter", {})
|
|
288
|
-
|
|
289
|
-
self.input_format.set(settings.get("input_format", "unix"))
|
|
290
|
-
self.output_format.set(settings.get("output_format", "iso"))
|
|
291
|
-
self.use_utc.set(settings.get("use_utc", False))
|
|
292
|
-
self.custom_format.set(settings.get("custom_format", "%Y-%m-%d %H:%M:%S"))
|
|
293
|
-
self.show_relative.set(settings.get("show_relative", False))
|
|
294
|
-
|
|
295
|
-
def save_settings(self):
|
|
296
|
-
"""Save current settings to the application."""
|
|
297
|
-
if "Timestamp Converter" not in self.app.settings["tool_settings"]:
|
|
298
|
-
self.app.settings["tool_settings"]["Timestamp Converter"] = {}
|
|
299
|
-
|
|
300
|
-
self.app.settings["tool_settings"]["Timestamp Converter"].update({
|
|
301
|
-
"input_format": self.input_format.get(),
|
|
302
|
-
"output_format": self.output_format.get(),
|
|
303
|
-
"use_utc": self.use_utc.get(),
|
|
304
|
-
"custom_format": self.custom_format.get(),
|
|
305
|
-
"show_relative": self.show_relative.get()
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
self.app.save_settings()
|
|
309
|
-
|
|
310
|
-
def on_setting_change(self, *args):
|
|
311
|
-
"""Handle setting changes."""
|
|
312
|
-
self.save_settings()
|
|
313
|
-
|
|
314
|
-
def convert(self):
|
|
315
|
-
"""Convert timestamps."""
|
|
316
|
-
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
317
|
-
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
318
|
-
|
|
319
|
-
if not input_text.strip():
|
|
320
|
-
return
|
|
321
|
-
|
|
322
|
-
result = TimestampConverterProcessor.convert_batch(
|
|
323
|
-
input_text,
|
|
324
|
-
self.input_format.get(),
|
|
325
|
-
self.output_format.get(),
|
|
326
|
-
self.use_utc.get(),
|
|
327
|
-
self.custom_format.get(),
|
|
328
|
-
self.show_relative.get()
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
332
|
-
active_output_tab.text.config(state="normal")
|
|
333
|
-
active_output_tab.text.delete("1.0", tk.END)
|
|
334
|
-
active_output_tab.text.insert("1.0", result)
|
|
335
|
-
active_output_tab.text.config(state="disabled")
|
|
336
|
-
|
|
337
|
-
self.app.update_all_stats()
|
|
338
|
-
|
|
339
|
-
def insert_current(self):
|
|
340
|
-
"""Insert current timestamp."""
|
|
341
|
-
if self.output_format.get() == "unix":
|
|
342
|
-
result = TimestampConverterProcessor.get_current_timestamp()
|
|
343
|
-
else:
|
|
344
|
-
result = TimestampConverterProcessor.get_current_datetime(
|
|
345
|
-
self.output_format.get(), self.custom_format.get()
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
349
|
-
active_output_tab.text.config(state="normal")
|
|
350
|
-
active_output_tab.text.delete("1.0", tk.END)
|
|
351
|
-
active_output_tab.text.insert("1.0", result)
|
|
352
|
-
active_output_tab.text.config(state="disabled")
|
|
353
|
-
|
|
354
|
-
self.app.update_all_stats()
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
class TimestampConverter:
|
|
358
|
-
"""Main class for Timestamp Converter integration."""
|
|
359
|
-
|
|
360
|
-
def __init__(self):
|
|
361
|
-
self.processor = TimestampConverterProcessor()
|
|
362
|
-
|
|
363
|
-
def create_widget(self, parent, app):
|
|
364
|
-
"""Create and return the Timestamp Converter widget."""
|
|
365
|
-
return TimestampConverterWidget(parent, app)
|
|
366
|
-
|
|
367
|
-
def get_default_settings(self):
|
|
368
|
-
"""Return default settings for Timestamp Converter."""
|
|
369
|
-
return {
|
|
370
|
-
"input_format": "unix",
|
|
371
|
-
"output_format": "iso",
|
|
372
|
-
"use_utc": False,
|
|
373
|
-
"custom_format": "%Y-%m-%d %H:%M:%S",
|
|
374
|
-
"show_relative": False
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
# BaseTool-compatible wrapper
|
|
379
|
-
try:
|
|
380
|
-
from tools.base_tool import ToolWithOptions
|
|
381
|
-
from typing import Dict, Any
|
|
382
|
-
|
|
383
|
-
class TimestampConverterV2(ToolWithOptions):
|
|
384
|
-
"""
|
|
385
|
-
BaseTool-compatible version of TimestampConverter.
|
|
386
|
-
"""
|
|
387
|
-
|
|
388
|
-
TOOL_NAME = "Timestamp Converter"
|
|
389
|
-
TOOL_DESCRIPTION = "Convert between Unix timestamps and human-readable dates"
|
|
390
|
-
TOOL_VERSION = "2.0.0"
|
|
391
|
-
|
|
392
|
-
OPTIONS = [
|
|
393
|
-
("Unix to ISO", "unix_to_iso"),
|
|
394
|
-
("Unix to US Format", "unix_to_us"),
|
|
395
|
-
("Unix to EU Format", "unix_to_eu"),
|
|
396
|
-
("Date to Unix", "date_to_unix"),
|
|
397
|
-
("Current Time", "now"),
|
|
398
|
-
]
|
|
399
|
-
OPTIONS_LABEL = "Conversion"
|
|
400
|
-
USE_DROPDOWN = True
|
|
401
|
-
DEFAULT_OPTION = "unix_to_iso"
|
|
402
|
-
|
|
403
|
-
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
404
|
-
"""Process text using the specified conversion."""
|
|
405
|
-
mode = settings.get("mode", "unix_to_iso")
|
|
406
|
-
|
|
407
|
-
if mode == "now":
|
|
408
|
-
import time
|
|
409
|
-
return str(int(time.time()))
|
|
410
|
-
elif mode == "unix_to_iso":
|
|
411
|
-
return TimestampConverterProcessor.convert_batch(input_text, "unix", "iso")
|
|
412
|
-
elif mode == "unix_to_us":
|
|
413
|
-
return TimestampConverterProcessor.convert_batch(input_text, "unix", "us")
|
|
414
|
-
elif mode == "unix_to_eu":
|
|
415
|
-
return TimestampConverterProcessor.convert_batch(input_text, "unix", "eu")
|
|
416
|
-
elif mode == "date_to_unix":
|
|
417
|
-
return TimestampConverterProcessor.convert_batch(input_text, "auto", "unix")
|
|
418
|
-
else:
|
|
419
|
-
return input_text
|
|
420
|
-
|
|
421
|
-
except ImportError:
|
|
1
|
+
"""
|
|
2
|
+
Timestamp Converter Module - Date/time conversion utilities
|
|
3
|
+
|
|
4
|
+
This module provides timestamp conversion functionality
|
|
5
|
+
for the Pomera AI Commander application.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Unix timestamp to human-readable date
|
|
9
|
+
- Human-readable date to Unix timestamp
|
|
10
|
+
- Multiple date format options
|
|
11
|
+
- Relative time display
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import tkinter as tk
|
|
15
|
+
from tkinter import ttk
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
import time
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TimestampConverterProcessor:
|
|
22
|
+
"""Timestamp converter processor."""
|
|
23
|
+
|
|
24
|
+
DATE_FORMATS = {
|
|
25
|
+
"iso": "%Y-%m-%dT%H:%M:%S",
|
|
26
|
+
"iso_date": "%Y-%m-%d",
|
|
27
|
+
"us": "%m/%d/%Y %I:%M:%S %p",
|
|
28
|
+
"us_date": "%m/%d/%Y",
|
|
29
|
+
"eu": "%d/%m/%Y %H:%M:%S",
|
|
30
|
+
"eu_date": "%d/%m/%Y",
|
|
31
|
+
"long": "%B %d, %Y %H:%M:%S",
|
|
32
|
+
"short": "%b %d, %Y %H:%M",
|
|
33
|
+
"rfc2822": "%a, %d %b %Y %H:%M:%S",
|
|
34
|
+
"custom": None
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def unix_to_datetime(timestamp, use_utc=False):
|
|
39
|
+
"""Convert Unix timestamp to datetime object."""
|
|
40
|
+
try:
|
|
41
|
+
ts = float(timestamp)
|
|
42
|
+
# Handle milliseconds
|
|
43
|
+
if ts > 1e12:
|
|
44
|
+
ts = ts / 1000
|
|
45
|
+
|
|
46
|
+
if use_utc:
|
|
47
|
+
return datetime.fromtimestamp(ts, tz=timezone.utc)
|
|
48
|
+
else:
|
|
49
|
+
return datetime.fromtimestamp(ts)
|
|
50
|
+
except (ValueError, OSError) as e:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def datetime_to_unix(dt):
|
|
55
|
+
"""Convert datetime object to Unix timestamp."""
|
|
56
|
+
return int(dt.timestamp())
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def format_datetime(dt, format_name="iso", custom_format=None):
|
|
60
|
+
"""Format datetime using specified format."""
|
|
61
|
+
if format_name == "custom" and custom_format:
|
|
62
|
+
fmt = custom_format
|
|
63
|
+
else:
|
|
64
|
+
fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name, "%Y-%m-%d %H:%M:%S")
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
return dt.strftime(fmt)
|
|
68
|
+
except Exception:
|
|
69
|
+
return str(dt)
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def parse_datetime(text, format_name="iso", custom_format=None):
|
|
73
|
+
"""Parse datetime from string."""
|
|
74
|
+
text = text.strip()
|
|
75
|
+
|
|
76
|
+
# Try to detect Unix timestamp
|
|
77
|
+
if re.match(r'^\d{10,13}$', text):
|
|
78
|
+
ts = float(text)
|
|
79
|
+
if ts > 1e12:
|
|
80
|
+
ts = ts / 1000
|
|
81
|
+
return datetime.fromtimestamp(ts)
|
|
82
|
+
|
|
83
|
+
# Try specified format
|
|
84
|
+
if format_name == "custom" and custom_format:
|
|
85
|
+
fmt = custom_format
|
|
86
|
+
else:
|
|
87
|
+
fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name)
|
|
88
|
+
|
|
89
|
+
if fmt:
|
|
90
|
+
try:
|
|
91
|
+
return datetime.strptime(text, fmt)
|
|
92
|
+
except ValueError:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Try common formats
|
|
96
|
+
common_formats = [
|
|
97
|
+
"%Y-%m-%dT%H:%M:%S",
|
|
98
|
+
"%Y-%m-%d %H:%M:%S",
|
|
99
|
+
"%Y-%m-%d",
|
|
100
|
+
"%m/%d/%Y %H:%M:%S",
|
|
101
|
+
"%m/%d/%Y",
|
|
102
|
+
"%d/%m/%Y %H:%M:%S",
|
|
103
|
+
"%d/%m/%Y",
|
|
104
|
+
"%B %d, %Y",
|
|
105
|
+
"%b %d, %Y",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
for fmt in common_formats:
|
|
109
|
+
try:
|
|
110
|
+
return datetime.strptime(text, fmt)
|
|
111
|
+
except ValueError:
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def relative_time(dt):
|
|
118
|
+
"""Get relative time string (e.g., '2 hours ago')."""
|
|
119
|
+
now = datetime.now()
|
|
120
|
+
diff = now - dt
|
|
121
|
+
|
|
122
|
+
seconds = diff.total_seconds()
|
|
123
|
+
|
|
124
|
+
if seconds < 0:
|
|
125
|
+
# Future
|
|
126
|
+
seconds = abs(seconds)
|
|
127
|
+
suffix = "from now"
|
|
128
|
+
else:
|
|
129
|
+
suffix = "ago"
|
|
130
|
+
|
|
131
|
+
if seconds < 60:
|
|
132
|
+
return f"{int(seconds)} seconds {suffix}"
|
|
133
|
+
elif seconds < 3600:
|
|
134
|
+
minutes = int(seconds / 60)
|
|
135
|
+
return f"{minutes} minute{'s' if minutes != 1 else ''} {suffix}"
|
|
136
|
+
elif seconds < 86400:
|
|
137
|
+
hours = int(seconds / 3600)
|
|
138
|
+
return f"{hours} hour{'s' if hours != 1 else ''} {suffix}"
|
|
139
|
+
elif seconds < 2592000: # 30 days
|
|
140
|
+
days = int(seconds / 86400)
|
|
141
|
+
return f"{days} day{'s' if days != 1 else ''} {suffix}"
|
|
142
|
+
elif seconds < 31536000: # 365 days
|
|
143
|
+
months = int(seconds / 2592000)
|
|
144
|
+
return f"{months} month{'s' if months != 1 else ''} {suffix}"
|
|
145
|
+
else:
|
|
146
|
+
years = int(seconds / 31536000)
|
|
147
|
+
return f"{years} year{'s' if years != 1 else ''} {suffix}"
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def convert_timestamp(text, input_format="unix", output_format="iso",
|
|
151
|
+
use_utc=False, custom_format=None, show_relative=False):
|
|
152
|
+
"""Convert timestamp between formats."""
|
|
153
|
+
text = text.strip()
|
|
154
|
+
|
|
155
|
+
if not text:
|
|
156
|
+
return ""
|
|
157
|
+
|
|
158
|
+
# Parse input
|
|
159
|
+
if input_format == "unix":
|
|
160
|
+
dt = TimestampConverterProcessor.unix_to_datetime(text, use_utc)
|
|
161
|
+
else:
|
|
162
|
+
dt = TimestampConverterProcessor.parse_datetime(text, input_format, custom_format)
|
|
163
|
+
|
|
164
|
+
if dt is None:
|
|
165
|
+
return f"Error: Could not parse '{text}'"
|
|
166
|
+
|
|
167
|
+
# Format output
|
|
168
|
+
if output_format == "unix":
|
|
169
|
+
result = str(TimestampConverterProcessor.datetime_to_unix(dt))
|
|
170
|
+
else:
|
|
171
|
+
result = TimestampConverterProcessor.format_datetime(dt, output_format, custom_format)
|
|
172
|
+
|
|
173
|
+
if show_relative:
|
|
174
|
+
relative = TimestampConverterProcessor.relative_time(dt)
|
|
175
|
+
result += f" ({relative})"
|
|
176
|
+
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def convert_batch(text, input_format="unix", output_format="iso",
|
|
181
|
+
use_utc=False, custom_format=None, show_relative=False):
|
|
182
|
+
"""Convert multiple timestamps."""
|
|
183
|
+
lines = text.strip().split('\n')
|
|
184
|
+
results = []
|
|
185
|
+
|
|
186
|
+
for line in lines:
|
|
187
|
+
line = line.strip()
|
|
188
|
+
if line:
|
|
189
|
+
result = TimestampConverterProcessor.convert_timestamp(
|
|
190
|
+
line, input_format, output_format, use_utc, custom_format, show_relative
|
|
191
|
+
)
|
|
192
|
+
results.append(result)
|
|
193
|
+
else:
|
|
194
|
+
results.append('')
|
|
195
|
+
|
|
196
|
+
return '\n'.join(results)
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def get_current_timestamp():
|
|
200
|
+
"""Get current Unix timestamp."""
|
|
201
|
+
return str(int(time.time()))
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def get_current_datetime(format_name="iso", custom_format=None):
|
|
205
|
+
"""Get current datetime in specified format."""
|
|
206
|
+
return TimestampConverterProcessor.format_datetime(
|
|
207
|
+
datetime.now(), format_name, custom_format
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class TimestampConverterWidget(ttk.Frame):
|
|
212
|
+
"""Widget for timestamp converter tool."""
|
|
213
|
+
|
|
214
|
+
def __init__(self, parent, app):
|
|
215
|
+
super().__init__(parent)
|
|
216
|
+
self.app = app
|
|
217
|
+
self.processor = TimestampConverterProcessor()
|
|
218
|
+
|
|
219
|
+
self.input_format = tk.StringVar(value="unix")
|
|
220
|
+
self.output_format = tk.StringVar(value="iso")
|
|
221
|
+
self.use_utc = tk.BooleanVar(value=False)
|
|
222
|
+
self.custom_format = tk.StringVar(value="%Y-%m-%d %H:%M:%S")
|
|
223
|
+
self.show_relative = tk.BooleanVar(value=False)
|
|
224
|
+
|
|
225
|
+
self.create_widgets()
|
|
226
|
+
self.load_settings()
|
|
227
|
+
|
|
228
|
+
def create_widgets(self):
|
|
229
|
+
"""Creates the widget interface."""
|
|
230
|
+
# Input format
|
|
231
|
+
input_frame = ttk.LabelFrame(self, text="Input Format", padding=10)
|
|
232
|
+
input_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
233
|
+
|
|
234
|
+
formats = [("Unix Timestamp", "unix"), ("ISO 8601", "iso"),
|
|
235
|
+
("US Date", "us"), ("EU Date", "eu"), ("Auto-detect", "auto")]
|
|
236
|
+
|
|
237
|
+
for i, (text, value) in enumerate(formats):
|
|
238
|
+
ttk.Radiobutton(input_frame, text=text,
|
|
239
|
+
variable=self.input_format, value=value,
|
|
240
|
+
command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
|
|
241
|
+
|
|
242
|
+
# Output format
|
|
243
|
+
output_frame = ttk.LabelFrame(self, text="Output Format", padding=10)
|
|
244
|
+
output_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
245
|
+
|
|
246
|
+
output_formats = [
|
|
247
|
+
("Unix Timestamp", "unix"), ("ISO 8601", "iso"), ("ISO Date", "iso_date"),
|
|
248
|
+
("US Format", "us"), ("EU Format", "eu"), ("Long", "long"),
|
|
249
|
+
("Short", "short"), ("RFC 2822", "rfc2822"), ("Custom", "custom")
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
for i, (text, value) in enumerate(output_formats):
|
|
253
|
+
ttk.Radiobutton(output_frame, text=text,
|
|
254
|
+
variable=self.output_format, value=value,
|
|
255
|
+
command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
|
|
256
|
+
|
|
257
|
+
# Custom format
|
|
258
|
+
custom_frame = ttk.Frame(self)
|
|
259
|
+
custom_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
260
|
+
|
|
261
|
+
ttk.Label(custom_frame, text="Custom Format:").pack(side=tk.LEFT)
|
|
262
|
+
ttk.Entry(custom_frame, textvariable=self.custom_format, width=25).pack(side=tk.LEFT, padx=5)
|
|
263
|
+
ttk.Label(custom_frame, text="(e.g., %Y-%m-%d)", font=('TkDefaultFont', 8)).pack(side=tk.LEFT)
|
|
264
|
+
|
|
265
|
+
# Options
|
|
266
|
+
options_frame = ttk.Frame(self)
|
|
267
|
+
options_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
268
|
+
|
|
269
|
+
ttk.Checkbutton(options_frame, text="Use UTC",
|
|
270
|
+
variable=self.use_utc,
|
|
271
|
+
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
272
|
+
ttk.Checkbutton(options_frame, text="Show Relative Time",
|
|
273
|
+
variable=self.show_relative,
|
|
274
|
+
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
275
|
+
|
|
276
|
+
# Buttons
|
|
277
|
+
buttons_frame = ttk.Frame(self)
|
|
278
|
+
buttons_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
279
|
+
|
|
280
|
+
ttk.Button(buttons_frame, text="Convert",
|
|
281
|
+
command=self.convert).pack(side=tk.LEFT, padx=5)
|
|
282
|
+
ttk.Button(buttons_frame, text="Insert Current Time",
|
|
283
|
+
command=self.insert_current).pack(side=tk.LEFT, padx=5)
|
|
284
|
+
|
|
285
|
+
def load_settings(self):
|
|
286
|
+
"""Load settings from the application."""
|
|
287
|
+
settings = self.app.settings.get("tool_settings", {}).get("Timestamp Converter", {})
|
|
288
|
+
|
|
289
|
+
self.input_format.set(settings.get("input_format", "unix"))
|
|
290
|
+
self.output_format.set(settings.get("output_format", "iso"))
|
|
291
|
+
self.use_utc.set(settings.get("use_utc", False))
|
|
292
|
+
self.custom_format.set(settings.get("custom_format", "%Y-%m-%d %H:%M:%S"))
|
|
293
|
+
self.show_relative.set(settings.get("show_relative", False))
|
|
294
|
+
|
|
295
|
+
def save_settings(self):
|
|
296
|
+
"""Save current settings to the application."""
|
|
297
|
+
if "Timestamp Converter" not in self.app.settings["tool_settings"]:
|
|
298
|
+
self.app.settings["tool_settings"]["Timestamp Converter"] = {}
|
|
299
|
+
|
|
300
|
+
self.app.settings["tool_settings"]["Timestamp Converter"].update({
|
|
301
|
+
"input_format": self.input_format.get(),
|
|
302
|
+
"output_format": self.output_format.get(),
|
|
303
|
+
"use_utc": self.use_utc.get(),
|
|
304
|
+
"custom_format": self.custom_format.get(),
|
|
305
|
+
"show_relative": self.show_relative.get()
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
self.app.save_settings()
|
|
309
|
+
|
|
310
|
+
def on_setting_change(self, *args):
|
|
311
|
+
"""Handle setting changes."""
|
|
312
|
+
self.save_settings()
|
|
313
|
+
|
|
314
|
+
def convert(self):
|
|
315
|
+
"""Convert timestamps."""
|
|
316
|
+
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
317
|
+
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
318
|
+
|
|
319
|
+
if not input_text.strip():
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
result = TimestampConverterProcessor.convert_batch(
|
|
323
|
+
input_text,
|
|
324
|
+
self.input_format.get(),
|
|
325
|
+
self.output_format.get(),
|
|
326
|
+
self.use_utc.get(),
|
|
327
|
+
self.custom_format.get(),
|
|
328
|
+
self.show_relative.get()
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
332
|
+
active_output_tab.text.config(state="normal")
|
|
333
|
+
active_output_tab.text.delete("1.0", tk.END)
|
|
334
|
+
active_output_tab.text.insert("1.0", result)
|
|
335
|
+
active_output_tab.text.config(state="disabled")
|
|
336
|
+
|
|
337
|
+
self.app.update_all_stats()
|
|
338
|
+
|
|
339
|
+
def insert_current(self):
|
|
340
|
+
"""Insert current timestamp."""
|
|
341
|
+
if self.output_format.get() == "unix":
|
|
342
|
+
result = TimestampConverterProcessor.get_current_timestamp()
|
|
343
|
+
else:
|
|
344
|
+
result = TimestampConverterProcessor.get_current_datetime(
|
|
345
|
+
self.output_format.get(), self.custom_format.get()
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
349
|
+
active_output_tab.text.config(state="normal")
|
|
350
|
+
active_output_tab.text.delete("1.0", tk.END)
|
|
351
|
+
active_output_tab.text.insert("1.0", result)
|
|
352
|
+
active_output_tab.text.config(state="disabled")
|
|
353
|
+
|
|
354
|
+
self.app.update_all_stats()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
class TimestampConverter:
|
|
358
|
+
"""Main class for Timestamp Converter integration."""
|
|
359
|
+
|
|
360
|
+
def __init__(self):
|
|
361
|
+
self.processor = TimestampConverterProcessor()
|
|
362
|
+
|
|
363
|
+
def create_widget(self, parent, app):
|
|
364
|
+
"""Create and return the Timestamp Converter widget."""
|
|
365
|
+
return TimestampConverterWidget(parent, app)
|
|
366
|
+
|
|
367
|
+
def get_default_settings(self):
|
|
368
|
+
"""Return default settings for Timestamp Converter."""
|
|
369
|
+
return {
|
|
370
|
+
"input_format": "unix",
|
|
371
|
+
"output_format": "iso",
|
|
372
|
+
"use_utc": False,
|
|
373
|
+
"custom_format": "%Y-%m-%d %H:%M:%S",
|
|
374
|
+
"show_relative": False
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# BaseTool-compatible wrapper
|
|
379
|
+
try:
|
|
380
|
+
from tools.base_tool import ToolWithOptions
|
|
381
|
+
from typing import Dict, Any
|
|
382
|
+
|
|
383
|
+
class TimestampConverterV2(ToolWithOptions):
|
|
384
|
+
"""
|
|
385
|
+
BaseTool-compatible version of TimestampConverter.
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
TOOL_NAME = "Timestamp Converter"
|
|
389
|
+
TOOL_DESCRIPTION = "Convert between Unix timestamps and human-readable dates"
|
|
390
|
+
TOOL_VERSION = "2.0.0"
|
|
391
|
+
|
|
392
|
+
OPTIONS = [
|
|
393
|
+
("Unix to ISO", "unix_to_iso"),
|
|
394
|
+
("Unix to US Format", "unix_to_us"),
|
|
395
|
+
("Unix to EU Format", "unix_to_eu"),
|
|
396
|
+
("Date to Unix", "date_to_unix"),
|
|
397
|
+
("Current Time", "now"),
|
|
398
|
+
]
|
|
399
|
+
OPTIONS_LABEL = "Conversion"
|
|
400
|
+
USE_DROPDOWN = True
|
|
401
|
+
DEFAULT_OPTION = "unix_to_iso"
|
|
402
|
+
|
|
403
|
+
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
404
|
+
"""Process text using the specified conversion."""
|
|
405
|
+
mode = settings.get("mode", "unix_to_iso")
|
|
406
|
+
|
|
407
|
+
if mode == "now":
|
|
408
|
+
import time
|
|
409
|
+
return str(int(time.time()))
|
|
410
|
+
elif mode == "unix_to_iso":
|
|
411
|
+
return TimestampConverterProcessor.convert_batch(input_text, "unix", "iso")
|
|
412
|
+
elif mode == "unix_to_us":
|
|
413
|
+
return TimestampConverterProcessor.convert_batch(input_text, "unix", "us")
|
|
414
|
+
elif mode == "unix_to_eu":
|
|
415
|
+
return TimestampConverterProcessor.convert_batch(input_text, "unix", "eu")
|
|
416
|
+
elif mode == "date_to_unix":
|
|
417
|
+
return TimestampConverterProcessor.convert_batch(input_text, "auto", "unix")
|
|
418
|
+
else:
|
|
419
|
+
return input_text
|
|
420
|
+
|
|
421
|
+
except ImportError:
|
|
422
422
|
pass
|