support IANA time zone names when importing DSV files using column mapping

This commit is contained in:
MaysWind
2025-12-24 23:09:33 +08:00
parent 59a138d417
commit f54c4998ef
20 changed files with 86 additions and 11 deletions
+6 -3
View File
@@ -17,15 +17,18 @@ export class KnownDateTimezoneFormat implements NameValue {
private static readonly allInstances: KnownDateTimezoneFormat[] = [];
private static readonly allInstancesByValue: Record<string, KnownDateTimezoneFormat> = {};
public static readonly HHColonMM = new KnownDateTimezoneFormat('±HH:mm', 'Z', /^[+-]?([0-1][0-9]|2[0-3]):[0-5][0-9]$/);
public static readonly HHMM = new KnownDateTimezoneFormat('±HHmm', 'ZZ', /^[+-]?([0-1][0-9]|2[0-3])[0-5][0-9]$/);
public static readonly HHColonMM = new KnownDateTimezoneFormat('±HH:mm', false, 'Z', /^[+-]?([0-1][0-9]|2[0-3]):[0-5][0-9]$/);
public static readonly HHMM = new KnownDateTimezoneFormat('±HHmm', false, 'ZZ', /^[+-]?([0-1][0-9]|2[0-3])[0-5][0-9]$/);
public static readonly IANAName = new KnownDateTimezoneFormat('IANA Time Zone Name', true, 'zzz', /^[A-Za-z_]+\/[A-Za-z_]+(\/[A-Za-z_]+)?$/);
public readonly name: string;
public readonly nameSupportedI18n: boolean;
public readonly value: string;
private readonly regex: RegExp;
private constructor(name: string, value: string, regex: RegExp) {
private constructor(name: string, nameSupportedI18n: boolean, value: string, regex: RegExp) {
this.name = name;
this.nameSupportedI18n = nameSupportedI18n;
this.value = value;
this.regex = regex;
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Time Format",
"Transaction Type Mapping": "Transaction Type Mapping",
"Timezone Format": "Timezone Format",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Amount Format",
"Geographic Location Separator": "Geographic Location Separator",
"Transaction Tags Separator": "Transaction Tags Separator",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Time Format",
"Transaction Type Mapping": "Transaction Type Mapping",
"Timezone Format": "Timezone Format",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Amount Format",
"Geographic Location Separator": "Geographic Location Separator",
"Transaction Tags Separator": "Transaction Tags Separator",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Formato del Tiempo",
"Transaction Type Mapping": "Asignación de Tipos de Transacciones",
"Timezone Format": "Formato de la Zona Horaria",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Formato de la Cantidad",
"Geographic Location Separator": "Separador de Ubicación Geográfica",
"Transaction Tags Separator": "Separador de Etiquetas de Transacciones",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Format d'heure",
"Transaction Type Mapping": "Mappage du type de transaction",
"Timezone Format": "Format de fuseau horaire",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Format de montant",
"Geographic Location Separator": "Séparateur de localisation géographique",
"Transaction Tags Separator": "Séparateur d'étiquettes de transaction",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Formato ora",
"Transaction Type Mapping": "Mappatura tipo transazione",
"Timezone Format": "Formato fuso orario",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Formato importo",
"Geographic Location Separator": "Separatore posizione geografica",
"Transaction Tags Separator": "Separatore tag transazione",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "時刻形式",
"Transaction Type Mapping": "取引タイプのマッピング",
"Timezone Format": "タイムゾーン形式",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Amount Format",
"Geographic Location Separator": "地理座標の区切り",
"Transaction Tags Separator": "取引タグの区切り",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "ಸಮಯ ರೂಪ",
"Transaction Type Mapping": "ವಹಿವಾಟು ಪ್ರಕಾರ ಮ್ಯಾಪಿಂಗ್",
"Timezone Format": "ಸಮಯ ವಲಯದ ರೂಪ",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "ಮೊತ್ತದ ರೂಪ",
"Geographic Location Separator": "ಭೌಗೋಳಿಕ ಸ್ಥಳ ವಿಭಜಕ",
"Transaction Tags Separator": "ವಹಿವಾಟು ಟ್ಯಾಗ್‌ಗಳ ವಿಭಜಕ",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "시간 형식",
"Transaction Type Mapping": "거래 유형 매핑",
"Timezone Format": "시간대 형식",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "금액 형식",
"Geographic Location Separator": "지리적 위치 구분 기호",
"Transaction Tags Separator": "거래 태그 구분 기호",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Tijdsformaat",
"Transaction Type Mapping": "Transactietypetoewijzing",
"Timezone Format": "Tijdzoneformaat",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Bedragformaat",
"Geographic Location Separator": "Scheidingsteken geografische locatie",
"Transaction Tags Separator": "Scheidingsteken transactietags",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Formato de Tempo",
"Transaction Type Mapping": "Mapeamento de Tipo de Transação",
"Timezone Format": "Formato de Fuso Horário",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Formato de Quantia",
"Geographic Location Separator": "Separador de Localização Geográfica",
"Transaction Tags Separator": "Separador de Tags de Transação",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Time Format",
"Transaction Type Mapping": "Transaction Type Mapping",
"Timezone Format": "Timezone Format",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Amount Format",
"Geographic Location Separator": "Geographic Location Separator",
"Transaction Tags Separator": "Transaction Tags Separator",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "รูปแบบเวลา",
"Transaction Type Mapping": "การแมปประเภทรายการ",
"Timezone Format": "รูปแบบเขตเวลา",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "รูปแบบจำนวนเงิน",
"Geographic Location Separator": "ตัวคั่นตำแหน่งทางภูมิศาสตร์",
"Transaction Tags Separator": "ตัวคั่นแท็กรายการ",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Zaman Formatı",
"Transaction Type Mapping": "İşlem Türü Eşlemesi",
"Timezone Format": "Saat Dilimi Formatı",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Tutar Formatı",
"Geographic Location Separator": "Coğrafi Konum Ayırıcı",
"Transaction Tags Separator": "İşlem Etiketleri Ayırıcı",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Формат часу",
"Transaction Type Mapping": "Відповідність типів транзакцій",
"Timezone Format": "Формат часового поясу",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Формат суми",
"Geographic Location Separator": "Роздільник геолокацій",
"Transaction Tags Separator": "Роздільник тегів транзакцій",
+1
View File
@@ -1937,6 +1937,7 @@
"Time Format": "Time Format",
"Transaction Type Mapping": "Transaction Type Mapping",
"Timezone Format": "Timezone Format",
"IANA Time Zone Name": "IANA Time Zone Name",
"Amount Format": "Amount Format",
"Geographic Location Separator": "Geographic Location Separator",
"Transaction Tags Separator": "Transaction Tags Separator",
+1
View File
@@ -1938,6 +1938,7 @@
"Amount Format": "金额格式",
"Transaction Type Mapping": "交易类型映射",
"Timezone Format": "时区格式",
"IANA Time Zone Name": "IANA 时区名称",
"Geographic Location Separator": "地理位置分隔符",
"Transaction Tags Separator": "交易标签分隔符",
"Lines Per Page": "每页行数",
+1
View File
@@ -1938,6 +1938,7 @@
"Amount Format": "金額格式",
"Transaction Type Mapping": "交易類型對應",
"Timezone Format": "時區格式",
"IANA Time Zone Name": "IANA 時區名稱",
"Geographic Location Separator": "地理位置分隔符號",
"Transaction Tags Separator": "交易標籤分隔符號",
"Lines Per Page": "每頁行數",
@@ -85,8 +85,7 @@
@click="parsedFileDataColumnMapping.timeFormat = ''">
<v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span>
<span class="ms-1" v-if="parsedFileAutoDetectedTimeFormat">({{ parsedFileAutoDetectedTimeFormat }})</span>
<span class="ms-1" v-if="!parsedFileAutoDetectedTimeFormat">({{ tt('Unknown') }})</span>
<span class="ms-1" v-if="parsedFileAutoDetectedTimeFormat">({{ parsedFileAutoDetectedTimeFormat || tt('Unknown') }})</span>
</v-list-item-title>
</v-list-item>
<v-list-item :key="dateTimeFormat.format"
@@ -103,7 +102,7 @@
<v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">
<span>{{ tt('Timezone Format') }}</span>
<span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">({{ KnownDateTimezoneFormat.valueOf(parsedFileDataColumnMapping.timezoneFormat || parsedFileAutoDetectedTimezoneFormat || '')?.name || tt('Unknown') }})</span>
<span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">({{ displayFileCurrentTimezoneFormat }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500">
<v-list>
<v-list-item key="auto"
@@ -111,8 +110,7 @@
@click="parsedFileDataColumnMapping.timezoneFormat = ''">
<v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span>
<span class="ms-1" v-if="parsedFileAutoDetectedTimezoneFormat && KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')?.name }})</span>
<span class="ms-1" v-if="!parsedFileAutoDetectedTimezoneFormat || !KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ tt('Unknown') }})</span>
<span class="ms-1">({{ displayFileAutoDetectedTimezoneFormat }})</span>
</v-list-item-title>
</v-list-item>
<v-list-item :key="timezoneFormat.value"
@@ -120,7 +118,7 @@
v-for="timezoneFormat in KnownDateTimezoneFormat.values()"
@click="parsedFileDataColumnMapping.timezoneFormat = timezoneFormat.value">
<v-list-item-title class="cursor-pointer">
{{ timezoneFormat.name }}
{{ ti(timezoneFormat.name, timezoneFormat.nameSupportedI18n) }}
</v-list-item-title>
</v-list-item>
</v-list>
@@ -137,8 +135,7 @@
@click="parsedFileDataColumnMapping.amountFormat = ''">
<v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span>
<span class="ms-1" v-if="parsedFileAutoDetectedAmountFormat && KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')?.format }})</span>
<span class="ms-1" v-if="!parsedFileAutoDetectedAmountFormat || !KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ tt('Unknown') }})</span>
<span class="ms-1">({{ displayFileAutoDetectedAmountFormat }})</span>
</v-list-item-title>
</v-list-item>
<v-list-item :key="amountFormat.type"
@@ -287,6 +284,7 @@ const props = defineProps<{
const {
tt,
ti,
getCurrentNumeralSystemType,
getAllImportTransactionColumnTypes
} = useI18n();
@@ -408,6 +406,48 @@ const parsedFileAutoDetectedTimeFormat = computed<string | undefined>(() => pars
const parsedFileAutoDetectedTimezoneFormat = computed<string | undefined>(() => parsedFileDataColumnMapping.value.parseFileAutoDetectedTimezoneFormat(props.parsedFileData));
const parsedFileAutoDetectedAmountFormat = computed<string | undefined>(() => parsedFileDataColumnMapping.value.parseFileAutoDetectedAmountFormat(props.parsedFileData));
const displayFileCurrentTimezoneFormat = computed<string>(() => {
const format = parsedFileDataColumnMapping.value.timezoneFormat || parsedFileAutoDetectedTimezoneFormat.value || '';
if (format) {
const knownFormat = KnownDateTimezoneFormat.valueOf(format);
if (knownFormat) {
return ti(knownFormat.name, knownFormat.nameSupportedI18n);
}
}
return tt('Unknown');
});
const displayFileAutoDetectedTimezoneFormat = computed<string>(() => {
const format = parsedFileAutoDetectedTimezoneFormat.value;
if (format) {
const knownFormat = KnownDateTimezoneFormat.valueOf(format);
if (knownFormat) {
return ti(knownFormat.name, knownFormat.nameSupportedI18n);
}
}
return tt('Unknown');
});
const displayFileAutoDetectedAmountFormat = computed<string>(() => {
const format = parsedFileAutoDetectedAmountFormat.value;
if (format) {
const knownFormat = KnownAmountFormat.valueOf(format);
if (knownFormat) {
return knownFormat.format;
}
}
return tt('Unknown');
});
function getDisplayCount(count: number): string {
return numeralSystem.value.formatNumber(count);
}