diff --git a/cmd/asbackup/readme.md b/cmd/asbackup/readme.md index 6cb46425..7b96cee4 100644 --- a/cmd/asbackup/readme.md +++ b/cmd/asbackup/readme.md @@ -82,14 +82,14 @@ Backup Flags: Format: base64 encoded string Example: EjRWeJq83vEjRRI0VniavN7xI0U= - -a, --modified-before string + -a, --modified-after string Perform an incremental backup; only include records that changed after the given date and time. The system's local timezone applies. If only HH:MM:SS is specified, then today's date is assumed as the date. If only YYYY-MM-DD is specified, then 00:00:00 (midnight) is assumed as the time. - -b, --modified-after string + -b, --modified-before string Only include records that last changed before the given date and time. May combined with --modified-after to specify a range. -M, --max-records int The number of records approximately to back up. 0 - all records diff --git a/cmd/internal/app/configs.go b/cmd/internal/app/configs.go index 6bdc9fe5..d5fd890c 100644 --- a/cmd/internal/app/configs.go +++ b/cmd/internal/app/configs.go @@ -32,6 +32,10 @@ var ( expPartitionRange = regexp.MustCompile(`^([0-9]|[1-9][0-9]{1,3}|40[0-8][0-9]|409[0-5])\-([1-9]|[1-9][0-9]{1,3}|40[0-8][0-9]|409[0-6])$`) expPartitionID = regexp.MustCompile(`^(409[0-6]|40[0-8]\d|[123]?\d{1,3}|0)$`) expPartitionDigest = regexp.MustCompile(`^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$`) + // Time parsing expressions. + expTimeOnly = regexp.MustCompile(`^\d{2}:\d{2}:\d{2}$`) + expDateOnly = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) + expDateTime = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}$`) ) func mapBackupConfig( @@ -105,7 +109,7 @@ func mapBackupConfig( c.SecretAgentConfig = mapSecretAgentConfig(secretAgent) if backupParams.ModifiedBefore != "" { - modBeforeTime, err := time.Parse("2006-01-02_15:04:05", backupParams.ModifiedBefore) + modBeforeTime, err := parseLocalTimeToUTC(backupParams.ModifiedBefore) if err != nil { return nil, fmt.Errorf("failed to parse modified before date: %w", err) } @@ -114,7 +118,7 @@ func mapBackupConfig( } if backupParams.ModifiedAfter != "" { - modAfterTime, err := time.Parse("2006-01-02_15:04:05", backupParams.ModifiedAfter) + modAfterTime, err := parseLocalTimeToUTC(backupParams.ModifiedAfter) if err != nil { return nil, fmt.Errorf("failed to parse modified after date: %w", err) } @@ -403,3 +407,33 @@ func parsePartitionFilterByID(filter string) (*aerospike.PartitionFilter, error) func parsePartitionFilterByDigest(namespace, filter string) (*aerospike.PartitionFilter, error) { return backup.NewPartitionFilterByDigest(namespace, filter) } + +func parseLocalTimeToUTC(timeString string) (time.Time, error) { + location, err := time.LoadLocation("Local") + if err != nil { + return time.Time{}, fmt.Errorf("failed to load timezone location: %w", err) + } + + var validTime string + + switch { + case expDateTime.MatchString(timeString): + validTime = timeString + case expTimeOnly.MatchString(timeString): + currentTime := time.Now().In(location) + validTime = currentTime.Format("2006-01-02") + "_" + timeString + case expDateOnly.MatchString(timeString): + validTime = timeString + "_00:00:00" + default: + return time.Time{}, fmt.Errorf("unknown time format: %s", timeString) + } + + localTime, err := time.ParseInLocation("2006-01-02_15:04:05", validTime, location) + if err != nil { + return time.Time{}, fmt.Errorf("failed to parse time %s: %w", timeString, err) + } + + utcTime := localTime.UTC() + + return utcTime, nil +} diff --git a/cmd/internal/flags/backup.go b/cmd/internal/flags/backup.go index 9c26e985..2e78df7b 100644 --- a/cmd/internal/flags/backup.go +++ b/cmd/internal/flags/backup.go @@ -51,7 +51,7 @@ func (f *Backup) NewFlagSet() *pflag.FlagSet { "This argument is mutually exclusive to partition-list.\n"+ "Format: base64 encoded string\n"+ "Example: EjRWeJq83vEjRRI0VniavN7xI0U=\n") - flagSet.StringVarP(&f.ModifiedBefore, "modified-before", "a", + flagSet.StringVarP(&f.ModifiedAfter, "modified-after", "a", "", "\n"+ "Perform an incremental backup; only include records \n"+ @@ -59,7 +59,7 @@ func (f *Backup) NewFlagSet() *pflag.FlagSet { "local timezone applies. If only HH:MM:SS is specified, then\n"+ "today's date is assumed as the date. If only YYYY-MM-DD is \n"+ "specified, then 00:00:00 (midnight) is assumed as the time.\n") - flagSet.StringVarP(&f.ModifiedAfter, "modified-after", "b", + flagSet.StringVarP(&f.ModifiedBefore, "modified-before", "b", "", "\n"+ "Only include records that last changed before the given\n"+