Files
swift-aws-lambda-runtime/Sources
Sébastien Stormacq 447c1e4db1 Remove dependency on DispatchWallTime (fix #384) (#540)
Fix
[#384](https://github.com/swift-server/swift-aws-lambda-runtime/issues/384)

Note: this PR introduces an API change that will break Lambda functions
using `LambdaContext`, we should integrate this change during the beta
otherwise it will require a major version bump.

### Motivation:

`DispatchWallTime` has no public API to extract the time in
milliseconds, making it a dead end.
Previous implementation used the internal representation of time inside
`DispatchWallTime` to extract the value, creating a risk if its
implementation will change in the future.
Moreover, the use of `DispatchWallTime` obliges users to import the
`Dispatch` library or `Foundation`.

Old Code:
```
extension DispatchWallTime {
    @usableFromInline
    init(millisSinceEpoch: Int64) {
        let nanoSinceEpoch = UInt64(millisSinceEpoch) * 1_000_000
        let seconds = UInt64(nanoSinceEpoch / 1_000_000_000)
        let nanoseconds = nanoSinceEpoch - (seconds * 1_000_000_000)
        self.init(timespec: timespec(tv_sec: Int(seconds), tv_nsec: Int(nanoseconds)))
    }

    var millisSinceEpoch: Int64 {
        Int64(bitPattern: self.rawValue) / -1_000_000
    }
}
```

Issue
[#384](https://github.com/swift-server/swift-aws-lambda-runtime/issues/384)
has a long discussion about possible replacements, including creating a
brand new `UTCClock`, which I think is an overkill for this project.

Instead, I propose this simple implementation, based on two assumptions:

- AWS always sends the time in milliseconds since Unix Epoch (1st Jan
1970) ([Lambda Runtime API
documentation](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html#runtimes-api-next))
- AWS always uses UTC time (not only for Lambda, this is a general rule
for all AWS APIs) ([TZ=UTC on
Lambda](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html))

Therefore, this library just needs to store and make math on
milliseconds since epoch, without having to care about timezone.

I had two possibilities to implement the storage and the math on
milliseconds since Unix Epoch: either I could use an `UInt64` (as does
[the Rust
implementation](https://github.com/awslabs/aws-lambda-rust-runtime/blob/aff8d883c62997ef2615714dce9f7ddfd557147d/lambda-runtime/src/types.rs#L70))
or I could use a standard Swift type, such as `Duration`.

`Duration` is a good candidate for this because 1/ the time we receive
from the Lambda Service API is indeed a duration between 1/1/1970 and
the execution deadline for the Lambda function, expressed in
milliseconds, 2/ it gives a strong type that can be verified by the
compiler, and 3/ it is possible to do basic arithmetic operations and
compare two values.

As an additional benefit, it allows library users to not import
`Dispatch` or `Foundation`

### Modifications:

I made two changes:

1. I extend the `Duration` type to provide us with simple unix epoch
time manipulation functions and values.

```swift
extension Duration {
    /// Returns the time in milliseconds since the Unix epoch.
    @usableFromInline
    static var millisSinceEpoch: Duration {
        var ts = timespec()
        clock_gettime(CLOCK_REALTIME, &ts)
        return .milliseconds(Int64(ts.tv_sec) * 1000 + Int64(ts.tv_nsec) / 1_000_000)
    }

    /// Returns a Duration between Unix epoch and the distant future
    @usableFromInline
    static var distantFuture: Duration {
        // Use a very large value to represent the distant future
        millisSinceEpoch + Duration.seconds(.greatestFiniteMagnitude)
    }

    /// Returns the Duration in milliseconds
    @usableFromInline
    func milliseconds() -> Int64 {
        Int64(self / .milliseconds(1))
    }

    /// Create a Duration from milliseconds since Unix Epoch
    @usableFromInline
    init(millisSinceEpoch: Int64) {
        self = .milliseconds(millisSinceEpoch)
    }
}
```

3. I replaced all references to `DispatchWallTime` by `Duration` 

### Result:

No more `DispatchWallTime`
No dependencies on Foundation, as I use `clock_gettime()` to get the
epoch from the system clock.
2025-08-05 09:12:11 +02:00
..