diff --git a/README.md b/README.md index cbd5df8..54e840f 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,4 @@ These tests cover all cases for file/folder structures which contain files with | M | datetime file with random start time | 00:00:00 | 01:00:00 | x | | N | date folder, time file | 12:00:00 | 06:00:00 | | | O | datetime file with random start time and interval exceeding end | 00:00:00 | 01:00:00 | x | +| P | datetime file with file name offset | 00:00:00 | 00:10:00 | | diff --git a/src/Nexus.Sources.StructuredFile/StructuredFileDataSource.cs b/src/Nexus.Sources.StructuredFile/StructuredFileDataSource.cs index d5454d9..50b7e7c 100644 --- a/src/Nexus.Sources.StructuredFile/StructuredFileDataSource.cs +++ b/src/Nexus.Sources.StructuredFile/StructuredFileDataSource.cs @@ -582,7 +582,8 @@ protected abstract Task ReadAsync( ? DateTime.SpecifyKind(begin.Add(fileSource.UtcOffset), DateTimeKind.Local) : throw new ArgumentException("The begin parameter must of kind UTC."); - var localFileBegin = localBegin.RoundDown(fileSource.FilePeriod); + var localFileBegin = localBegin + .RoundDown(fileSource.FilePeriod); var folderNames = fileSource .PathSegments @@ -652,6 +653,10 @@ protected abstract Task ReadAsync( else { + /* correct local file begin */ + if (fileSource.FileNameOffset != TimeSpan.Zero) + localFileBegin = localFileBegin.Add(fileSource.FileNameOffset); + var folderPath = Path.Combine(folderNameArray); var fileName = localFileBegin.ToString(fileSource.FileTemplate); @@ -981,7 +986,7 @@ internal static bool TryGetFileBeginByPath( CustomDateTimeOffset folderBegin = default) { var fileName = Path.GetFileName(filePath); - bool isSuccess; + bool success; if (TryGetFileBeginByName_AnyKind(fileName, fileSource, out fileBegin)) { @@ -991,7 +996,7 @@ internal static bool TryGetFileBeginByPath( // date+time: use file date/time if (fileBegin.DateTime.Date != default) { - isSuccess = true; + success = true; } // time-only: use combined folder and file date/time @@ -1004,7 +1009,7 @@ internal static bool TryGetFileBeginByPath( new DateTime(folderBegin.DateTime.Date.Ticks + fileBegin.DateTime.TimeOfDay.Ticks), fileBegin.Offset); - isSuccess = true; + success = true; } // long way @@ -1016,7 +1021,7 @@ internal static bool TryGetFileBeginByPath( new DateTime(folderBegin.DateTime.Ticks + fileBegin.DateTime.TimeOfDay.Ticks), fileBegin.Offset); - isSuccess = folderBegin != default; + success = folderBegin != default; } } @@ -1027,7 +1032,7 @@ internal static bool TryGetFileBeginByPath( if (folderBegin != default) { fileBegin = folderBegin; - isSuccess = true; + success = true; } // long way @@ -1036,18 +1041,21 @@ internal static bool TryGetFileBeginByPath( folderBegin = GetFolderBegin_AnyKind(filePath, fileSource); fileBegin = folderBegin; - isSuccess = folderBegin != default; + success = folderBegin != default; } } + + if (success && fileSource.FileNameOffset != TimeSpan.Zero) + fileBegin = new CustomDateTimeOffset(fileBegin.DateTime - fileSource.FileNameOffset, fileBegin.Offset); } // no date + no time: failed else { - isSuccess = false; + success = false; } - return isSuccess; + return success; } private static CustomDateTimeOffset GetFolderBegin_AnyKind(string filePath, FileSource fileSource) diff --git a/src/Nexus.Sources.StructuredFile/StructuredFileDataSourceTypes.cs b/src/Nexus.Sources.StructuredFile/StructuredFileDataSourceTypes.cs index fb7f819..c9181bd 100644 --- a/src/Nexus.Sources.StructuredFile/StructuredFileDataSourceTypes.cs +++ b/src/Nexus.Sources.StructuredFile/StructuredFileDataSourceTypes.cs @@ -13,6 +13,7 @@ namespace Nexus.Sources; /// An optional regular expression to select only relevant parts of a file name (e.g. to select the date/time and a unqiue identifier in case there is more than one kind of file in the same folder). In case of a file named 20200101_13_my-id_1234.dat the preselector could be like "(.{11})_my-id". It is also required for file names containing an opaque string that changes for every file. /// An optional date/time selector which is mandatory when the preselector is provided. In case of a file named like "20200101_13_my-id_1234.dat", and a preselector of "(.{11})_my-id", the selector should be like "yyyyMMdd_HH". /// The period per file. +/// The file name offset of the file data. This is useful for files that are named according to the end date of the data they contain. /// The UTC offset of the file data. /// The file time interval is irregular. I.e. the file end is not aligned to multiples of the file period. /// Additional properties to be used by the data source implementation. @@ -23,6 +24,7 @@ public record FileSource( string? FileDateTimePreselector, string? FileDateTimeSelector, TimeSpan FilePeriod, + TimeSpan FileNameOffset, TimeSpan UtcOffset, bool IrregularTimeInterval, JsonElement? AdditionalProperties diff --git a/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/DATA/2020-01-01T00-10-00Z.dat b/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/DATA/2020-01-01T00-10-00Z.dat new file mode 100644 index 0000000..e69de29 diff --git a/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/DATA/2020-01-01T00-20-00Z.dat b/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/DATA/2020-01-01T00-20-00Z.dat new file mode 100644 index 0000000..e69de29 diff --git a/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/config.json b/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/config.json new file mode 100644 index 0000000..7e7c43b --- /dev/null +++ b/tests/Nexus.Sources.StructuredFile.Tests/DATABASES/P/config.json @@ -0,0 +1,15 @@ +{ + "/A/B/C": { + "default": [ + { + "PathSegments": [ + "'DATA'" + ], + "FileTemplate": "yyyy-MM-ddTHH-mm-ssZ'.dat'", + "FilePeriod": "00:10:00", + "UtcOffset": "00:00:00", + "FileNameOffset": "00:10:00" + } + ] + } +} \ No newline at end of file diff --git a/tests/Nexus.Sources.StructuredFile.Tests/StructuredFileDataSourceTests.cs b/tests/Nexus.Sources.StructuredFile.Tests/StructuredFileDataSourceTests.cs index 859664e..42fe77a 100644 --- a/tests/Nexus.Sources.StructuredFile.Tests/StructuredFileDataSourceTests.cs +++ b/tests/Nexus.Sources.StructuredFile.Tests/StructuredFileDataSourceTests.cs @@ -115,6 +115,7 @@ public async Task CanProvideFirstFile() [InlineData("DATABASES/M", "2020-01-01T01-35-23Z", "2020-01-01T05-00-00Z")] [InlineData("DATABASES/N", "2019-12-31T12-00-00Z", "2020-01-03T12-00-00Z")] [InlineData("DATABASES/O", "2020-01-01T00-35-23Z", "2020-01-01T01-00-10Z")] + [InlineData("DATABASES/P", "2020-01-01T00-00-00Z", "2020-01-01T00-20-00Z")] public async Task CanProvideTimeRange(string root, string expectedBeginString, string expectedEndString) { var expectedBegin = DateTime.ParseExact(expectedBeginString, "yyyy-MM-ddTHH-mm-ssZ", null, DateTimeStyles.AdjustToUniversal); @@ -152,6 +153,7 @@ public async Task CanProvideTimeRange(string root, string expectedBeginString, s [InlineData("DATABASES/M", "2020-01-01T00-00-00Z", "2020-01-02T00-00-00Z", 4 / 24.0, 3)] [InlineData("DATABASES/N", "2020-01-01T00-00-00Z", "2020-01-03T00-00-00Z", 7 / 8.0, 2)] [InlineData("DATABASES/O", "2020-01-01T00-00-00Z", "2020-01-02T00-00-00Z", 3 / 288.0, 4)] + [InlineData("DATABASES/P", "2020-01-01T00-00-00Z", "2020-01-02T00-00-00Z", 2 / 144.0, 4)] public async Task CanProvideAvailability(string root, string beginString, string endString, double expected, int precision) { // Arrange @@ -297,6 +299,7 @@ await Assert.ThrowsAsync(() => [InlineData("M", "2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z")] [InlineData("N", "2020-01-01T00:00:00Z", "2020-01-04T00:00:00Z")] [InlineData("O", "2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z")] + [InlineData("P", "2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z")] public async Task CanRead_ReadInfos(string database, string beginString, string endString) { // Arrange diff --git a/tests/Nexus.Sources.StructuredFile.Tests/expected/P.json b/tests/Nexus.Sources.StructuredFile.Tests/expected/P.json new file mode 100644 index 0000000..af8390d --- /dev/null +++ b/tests/Nexus.Sources.StructuredFile.Tests/expected/P.json @@ -0,0 +1,20 @@ +[ + { + "BufferOffset": 0, + "FilePath": "/debug/DATABASES/P/DATA/2020-01-01T00-10-00Z.dat", + "FileSource": null, + "RegularFileBegin": "2020-01-01T00:00:00Z", + "FileOffset": 0, + "FileBlock": 600, + "FileLength": 600 + }, + { + "BufferOffset": 600, + "FilePath": "/debug/DATABASES/P/DATA/2020-01-01T00-20-00Z.dat", + "FileSource": null, + "RegularFileBegin": "2020-01-01T00:10:00Z", + "FileOffset": 0, + "FileBlock": 600, + "FileLength": 600 + } +] \ No newline at end of file