mczachurski / wallpapper

:computer: Console application for creating dynamic wallpapers for macOS Mojave and newer
MIT License
3.32k stars 137 forks source link

Time-based HEIC not changing at the right times #76

Open ffxsam opened 10 months ago

ffxsam commented 10 months ago

This is my JSON:

[
  {
    "fileName": "Day.jpg",
    "isPrimary": true,
    "isForLight": true,
    "time": "2023-12-30T15:00:00Z"
  },
  {
    "fileName": "Evening.jpg",
    "isForDark": true,
    "time": "2023-12-31T01:00:00Z"
  },
  {
    "fileName": "Night.jpg",
    "time": "2023-12-31T07:00:00Z"
  },
  {
    "fileName": "Dawn.jpg",
    "time": "2023-12-31T11:00:00Z"
  },
  {
    "fileName": "Morning.jpg",
    "time": "2023-12-31T13:00:00Z"
  }
]

See the video below, and note the time I'm setting. That image that it changes to just past midnight is wrong (and I'm UTC-6, BTW). There's an image change at 7pm my time (01:00 UTC) and at 1am (07:00 UTC), nothing in between. Not sure why this is happening.

https://github.com/mczachurski/wallpapper/assets/12532733/6d8a118e-3265-4c1a-ac4d-d93cd283d257

COV-Steve commented 8 months ago

I was just trying to sort this out today and it appears that you need to put your time zone offset in the timestamps. So if you are UTC-6, try replacing the "Z" at the end with "-06:00" (and of course pay attention to whether daylight savings time is in effect for the date that you specify and adjust your offset accordingly):

"time": "2023-12-30T15:00:00-06:00"

Things also seem to work better if your JSON entries match the order of the files in the file system. Pay attention to the order in which it reads in your images—surprisingly, it's not the order in which you have them in the JSON file.

ffxsam commented 8 months ago

The weird thing, if you deconstruct the official Apple ones, they're in UTC ISO format (ending in Z). Not sure why it doesn't work when I try this, though.

Thanks for the tip! I'll give it a shot if I decide to mess around with this again.

jp-hoehmann commented 8 months ago

The actual plist in which the times are stored in the final HEIC doesn't have times at all. 8am for example is simply stored as the floating point value 0.33. Which makes sense, because the dynamic wallpapers always change at the same local time, no matter your time zone.

The value wallpapper will use is calculated here: https://github.com/mczachurski/wallpapper/blob/baa5fa0ab5564fe3cc657aef8eabe61a205468ac/Sources/WallpapperLib/DynamicWallpaperGenerator/ImageMetadataGenerator.swift#L102

The code is intended to throw away everything except the hour and minute, then turn those two components into a Double representing the fraction of the day that has passed, but that is not what it does. Because the code uses an actual library to handle the dates, instead of just pulling the hour and minute values from the json, it undergoes several time zone conversions which are simply wrong. From my brief testing it seems that in aggregate, your local time zone is added twice, then the time zone in the time-stamp is subtracted from that (see example below).

For example if your local time is +01:00:

This explains why your image changes at 7pm and 1am (your timezone being -06:00 causes it to be wrong by 6 hours, since it subtracts 12 hours from your UTC timestamps). That one of the changes ends up being around midnight might be caused by it actually ending up with a negative value somewhere along the line, which then becomes zero (plus rounding errors), not sure though.

Either way, you should get the correct times, if you specify -12:00 as the time zone offset (no, really) and then just give the local times you want the images to change at. If the problem remains, you could have a look at the actual plist that gets generated.

In order to get the plist in a human-readable form:

wallpapper -e output.heic -o output.plist
plistutil -i output.list -f json -o output.json

The json file will contain an object ap which gives the (zero-based) indices of the isForLight and isForDark images, as well as an array ti, which maps times (given as fractions) to indices of images to show.

If your output were working correctly, you'd expect it to look roughly like this:

{
    "ti": [
        {
            "t": 0.375,
            "i": 0
        },
        {
            "t": 0.79166667,
            "i": 1
        },
        {
            "t": 0.04166667,
            "i": 2
        },
        {
            "t": 0.20833333,
            "i": 3
        },
        {
            "t": 0.29166667,
            "i": 4
        }
    ],
    "ap": {
        "d": 1,
        "l": 0
    }
}
Ender-Wang commented 2 months ago

I also noticed that the time is not precise, and after trying different off set, it changes at the exact time. My time zone is CET, should be UTC +1, I tried +01:00 but seems it still 0.5 hr late, so I tried +2 and then it matches my settings. Maybe you can make a PR of it, seems you know swift. @jp-hoehmann

jp-hoehmann commented 2 months ago

I looked into it at the time, but a proper fix would be a pretty big change (including changing the config file format to not include time zones), which would be a bunch of work for a PR that's fairly unlikely to get accepted (considering the maintainer hasn't weighed in on this issue at all so far). Hence I just wrote up how to work around it, which is good enough for me for now. Sorry :/